Skip to content

e2e: native Android GUI tests (Maestro + Playwright dashboard), nightly e2e-android workflow - #490

Draft
sgedda wants to merge 4 commits into
cursor/e2e-dashboard-gui-videos-7d37from
cursor/e2e-android-native-a6d7
Draft

sgedda wants to merge 4 commits into
cursor/e2e-dashboard-gui-videos-7d37from
cursor/e2e-android-native-a6d7

Conversation

@sgedda

@sgedda sgedda commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Description

This runs the dashboard GUI tests that map cleanly onto the native app against the WanderWeird Android debug APK. Playwright drives the Mockifyer dashboard, and Maestro drives the app on an emulator. The emulator is recorded with adb shell screenrecord and placed next to the dashboard recording, aligned on their start times. Each test also gets a Markdown file with a "Steps as run" table (each Maestro command and dashboard action, with its result and duration).

A new workflow, .github/workflows/e2e-android.yml, runs nightly at 02:30 UTC and on workflow_dispatch (presentation checkbox, default on). It does not run on pull requests, and e2e.yml is unchanged.

Test mapping

Web GUI test Android
mock body edit native
field override native
503 status override native, as its own test. The app has no error state here: wander-api answers 200 with fallback 18°, so the test asserts 18° and that discover-error is not shown.
delay + status override native. The delay is measured on the dashboard Network view (hop duration), which the test states.
header Scenario picker / lane global fallback native
from scratch native
Settings client lane not ported. The native client id is fixed by Metro env for the run, so a port needs an in-app lane switch or a Metro restart plus a reload.
Network view not ported (hop details have not been observed on a device)
tracing walkthrough not ported (same reason as Network view, and it needs the lane switch)

How it works

The Android test reuses the shared test body. Only the app side differs (DemoApp):

// e2e/tests/android-gui.spec.ts
test.beforeEach(async ({ dash, app }) => {
  await expect(dash.page.getByRole('heading', { name: 'Total Files' })).toBeVisible();
  await app.signIn(TEMPERATURE_TEXT.exactly(BASE_TEMP));   // Maestro: login screen -> Sign in -> Discover
});
test(DEMO_GUI_TESTS.bodyEdit.title, gui(DEMO_GUI_TESTS.bodyEdit.asserts, DEMO_GUI_TESTS.bodyEdit.steps), ({ dash, app }) =>
  DEMO_GUI_TESTS.bodyEdit.run({ dash, app, athens }));

Each app step is one Maestro run against the app's testIDs:

# e2e/native/flows/refetch-until.yaml
- retry:
    maxRetries: 3
    commands:
      - tapOn: { id: tab-trips }
      - tapOn: { id: tab-discover }
      - extendedWaitUntil:
          visible: { id: discover-temperature, text: "${TEMP_TEXT}" }
          timeout: 10000
      - assertNotVisible: { id: discover-error }

The app goes through the same dashboard proxy as the web app. This is set on Metro, since the debug APK loads its JS from there:

# e2e/native/scripts/metro.sh start
EXPO_PUBLIC_E2E=1 EXPO_PUBLIC_MOCKIFYER_BACKEND=redis EXPO_PUBLIC_MOCKIFYER_CLIENT_ID=wanderweird-demo npx expo start --port 8081

Changes

  • WanderWeird app:
    • testIDs on the login, tab bar and Discover screens.
    • EXPO_PUBLIC_E2E=1 hides LogBox banners.
    • react-native-worklets 0.10.1: 0.13 failed Reanimated 4.5.1's Gradle version check, so the APK did not build.
  • e2e:
    • lib/gui-demo.ts holds the shared test bodies (web specs refactored onto it).
    • lib/android.ts holds the fixture, Maestro runner, chunked screenrecord and step log.
    • tests/android-gui*.spec.ts are the Android specs.
    • native/flows and native/scripts hold the flows and scripts; native/README.md documents them.
    • Clip sync and the "Steps as run" table are in the artifacts script.
  • Shared fix: the delay test compared other mocks' settings including mocks recorded during the test (the services record), which made it flaky. It now compares only the mocks that existed before.
  • CI: e2e-android.yml uses ubuntu-24.04 with KVM. android-emulator-runner, setup-gradle, cache and upload actions are pinned by SHA. It has AVD and Gradle caching, a 90 min timeout and the same artifact names as e2e.yml.

Testing

  • Emulator: could not boot on the agent VM. Nested KVM hits a host kernel bug (kernel BUG at arch/x86/kvm/x86.c:702), so the Maestro flows and the recording have not been run on a device.
  • APK build passes (build-apk.sh, x86_64, 168 s). The Android bundle builds through Metro.
  • maestro check-syntax passes on all flows. actionlint passes on both workflows. shellcheck passes. tsc passes.
  • Web GUI project: 8/8 pass. Full default suite: 33 passed, 8 skipped (one earlier run had a one-off Network-trace timing failure that did not reproduce).
  • Artifacts script on synthetic clips (mp4 app, webm dashboard, 1.5 s offset): the side-by-side output is trimmed correctly and the Markdown has the steps table.
  • Root npm test passes. No packages/* changes, so there are no version bumps.
Open in Web Open in Cursor 

cursoragent and others added 4 commits October 28, 2026 23:42
…droid build

Login, tab bar and Discover get testIDs Maestro selects on; EXPO_PUBLIC_E2E=1 hides LogBox banners
over the tab bar. react-native-worklets 0.13 failed Reanimated 4.5.1's version check in Gradle;
expo install picked 0.10.1 (dependency, not devDependency).

Co-authored-by: Sebastian Gedda <sebastian.gedda@gmail.com>
The body edit, field override, header picker and from-scratch tests run against a DemoApp
(browser here, Android next). Other-mock settings are compared only for mocks that existed
before the change, since the services record new mocks during the run.

Co-authored-by: Sebastian Gedda <sebastian.gedda@gmail.com>
…h Maestro

E2E_ANDROID=1 adds the android project: Playwright drives the dashboard, Maestro flows in
e2e/native/flows drive the app, adb screenrecord (chunked at 3 min) records the emulator.
build-run-artifacts aligns both clips on their start times and the docs list every step as run.
Scripts and README in e2e/native.

Co-authored-by: Sebastian Gedda <sebastian.gedda@gmail.com>
Co-authored-by: Sebastian Gedda <sebastian.gedda@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants