test: add an automated Android end to end check for the example - #8
Merged
Merged
Conversation
Adds e2e/android.mjs plus a pnpm e2e:android script. The check builds and installs the example release variant, which embeds the JavaScript bundle and is signed with the checked-in debug keystore, cold launches it, opens the Sensor lab, drives the emulator hinge angle and asserts the readouts with a screenshot per asserted state. Sets hermesCommand in the example Android build so the release variant finds hermesc in the hoisted pnpm workspace at the repository root. Without it assembleRelease fails, because the React Native Gradle plugin resolves hermes-compiler under example/ rather than the workspace root. Adds .github/workflows/e2e-android.yml, which runs the same script on workflow_dispatch and on pull requests labeled e2e-android. That workflow has not been executed yet.
janicduplessis
marked this pull request as ready for review
September 21, 2026 09:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The example has no automated device coverage. Every hinge check is a manual pass
on an emulator, so nothing catches a regression in the native angle or posture
reaching the JavaScript readouts.
Solution
pnpm e2e:androidbuilds and installs the example, drives the emulator hingesensor, and asserts what the app actually renders.
The run must not depend on a Metro server, so it installs the release variant:
the React Native template signs it with the checked-in
debug.keystoreandembeds the JavaScript bundle. That exposed a pre-existing break.
assembleReleasefailed withCouldn't determine Hermesc location, because theReact Native Gradle plugin resolves
hermes-compilerrelative to itsrootsetting, which defaults to
example/, while the hoisted pnpm workspace keeps itat the repository root.
example/android/app/build.gradlealready redirectsreactNativeDir,codegenDirandcliFilefor the same reason, so this addshermesCommandalongside them.What the check asserts, in order:
NATIVE HINGE.adb -s <serial> emu sensor set hinge-angle0 45, the NATIVE ANGLE metricreads
45.0°within 5 seconds, and the status detail readspartiallyOpen.180.0°and the status readsfullyOpen.NATIVE 180.0°.Each asserted state is screenshotted into
e2e/artifacts/, already covered bythe existing
artifacts/ignore rule. A failed assertion exits non-zero anddumps the accessibility snapshot.
Three behaviours look arbitrary in the diff and are worth naming:
cmd device_state state resetbefore driving the sensor. Aposture override left behind by an earlier session pins the committed device
state, so the hinge angle stops driving posture and the status assertions read
whatever that session left. This bit a verification run here: the angle read
180.0°while the status stayedpartiallyOpen, withdevice_stateshowingBase state: OPENEDandOverride state: HALF_OPENED.seconds while the angle assertions keep the 5 second bound.
Back to Field Notes control. That control occupies the bottom 44dp of an
edge-to-edge window and the system taskbar covers it on a foldable inner
display, so it is not hittable. That looks like a real example layout bug and
is worth a separate look.
Every adb call is bounded by a 60 second timeout. An unbounded
adb shell getprophung a run here against a flapping emulator.How to run
E2E_ANDROID_SERIALis required when more than one device is attached. SetE2E_SKIP_BUILD=1to reuse the installed APK. The target needs a hinge sensorand real device states; the
pixel_foldandpixel_9_pro_foldAVD profiles giveboth.
Local run evidence
Device:
emulator-5590, AVDstim-react-native-hinges-fold3-camera, Pixel 9 ProFold profile, API 34 google_apis arm64-v8a.
An earlier run on the same emulator, before the device-state reset was added,
reached the same seven assertions green. A third attempt could not start because
another local agent-device session held the emulator lease, which is a shared
host constraint rather than a check failure.
CI workflow is unverified
.github/workflows/e2e-android.ymlhas never executed. It cannot run locally, soit is written from the action documentation:
reactivecircus/android-emulator-runnerpinned to the v2.38.0 commit,profile: pixel_fold, API 34 google_apis x86_64, KVM enabled exactly as theaction README describes, and the serial passed as
emulator-$EMULATOR_PORT. Idid verify locally that an AVD created with
--device pixel_foldgets the samehinge configuration as the emulator used here: one hinge, 0 to 180 range, posture
bands 0-30, 30-150 and 150-180. Everything else about the runner is unproven,
including whether the emulator publishes device states under
-no-window, whichthe status assertions depend on. Expect to iterate on the first real run.
The
e2e-androidlabel does not exist in this repository yet, so the labeltrigger cannot fire until someone creates it.