Skip to content

πŸ§ͺ E2E Testing (Maestro) ​

End-to-end smoke tests for the mobile app, driven by Maestro. Flows run locally against an Android emulator or iOS Simulator, and in CI on workflow dispatch. The smoke suite is the release gate β€” run it green before submitting a staging build.

NOTE

Flows, the full tag matrix, and per-flow status live in apps/mobile/.maestro/README.md. This page is the setup + usage guide; that README is the authoring reference.

How it fits together ​

  • Flows are YAML files in apps/mobile/.maestro/. Top-level nn-name.yaml are individual scenarios; 00-authed-suite.yaml is an orchestrator that logs in once and runs ~13 sub-flows from scenarios/. Shared steps live in helpers/.
  • Tags select what runs. Default is smoke (cross-platform critical path). Override with MAESTRO_INCLUDE_TAGS.
  • Pipelines are shell scripts (scripts/e2e.sh for Android, scripts/e2e-ios.sh for iOS) wrapped by pnpm scripts. They build a staging-variant release of the app with the JS bundle baked in (no Metro server needed), install it, and run the flows.

IMPORTANT

E2E always builds the staging app variant (com.tellia.mobile.staging, APP_VARIANT=preview) pointed at the staging backend β€” never your local dev build. Test accounts only exist in the tell-ia-staging Firebase project.

One-time setup ​

1. Simulator / emulator ​

You need a working iOS Simulator (Xcode + a runtime + CocoaPods) or Android emulator (Android Studio + an AVD + ANDROID_HOME). See Local Dev Setup β†’ simulators β€” same toolchain.

2. Install the Maestro CLI ​

bash
pnpm --filter @tellia-solutions/mobile-app e2e:install-cli   # curl -Ls https://get.maestro.mobile.dev | bash

Add Maestro to your PATH if the installer prompts you to (export PATH="$PATH:$HOME/.maestro/bin" in ~/.zshrc). Verify:

bash
maestro --version

3. Test accounts + env ​

Pipelines source apps/mobile/.maestro/.env. Copy the example and fill it in:

bash
cp apps/mobile/.maestro/.env.example apps/mobile/.maestro/.env

iOS and Android use separate staging accounts (each owns its own company) so parallel runs don't share company-scoped drafts/reports. The pipeline picks the matching suffix via E2E_PLATFORM; flow YAMLs read the unsuffixed canonical names.

VariablePurpose
MAESTRO_TEST_EMAIL_ANDROID / _PASSWORD_ANDROIDAndroid staging smoke account
MAESTRO_TEST_EMAIL_IOS / _PASSWORD_IOSiOS staging smoke account
EXPO_PUBLIC_MAPBOX_ACCESS_TOKENpk.* token β€” baked into the bundle so map tiles load (else blank map)
MAESTRO_TEST_PHONE_* / MAESTRO_TEST_OTPOnly for phone-auth flows; needs Firebase test phone numbers

IMPORTANT

The account passwords and Mapbox token come from the Mobile ENV 1Password item. Create the staging accounts in the Firebase Console first β€” see E2E_TEST_ACCOUNTS.md.

Running the suite ​

Full pipeline ​

full runs doctor β†’ boot β†’ build β†’ install β†’ run end to end. First run builds the native release (several minutes); later runs reuse it.

bash
# Android (emulator must have an AVD configured)
pnpm --filter @tellia-solutions/mobile-app e2e:android

# iOS (Xcode + Simulator runtime + CocoaPods)
pnpm --filter @tellia-solutions/mobile-app e2e:ios

Re-run flows only ​

After the app is already built + installed, skip straight to the flows β€” fast iteration loop:

bash
pnpm --filter @tellia-solutions/mobile-app e2e:android:run
pnpm --filter @tellia-solutions/mobile-app e2e:ios:run

# A single flow or a tag
pnpm --filter @tellia-solutions/mobile-app e2e:ios:run .maestro/05-login-wrong-password.yaml
MAESTRO_INCLUDE_TAGS=group-1-auth pnpm --filter @tellia-solutions/mobile-app e2e:android:run

Pipeline subcommands ​

Each stage is also exposed individually (handy when debugging a specific step):

pnpm script (per platform)What it does
e2e:<platform>:doctorCheck prerequisites (SDK, adb/xcrun, maestro, device)
e2e:<platform>:bootStart an emulator/simulator if none running (idempotent)
e2e:<platform>:buildBuild the staging release with JS bundled
e2e:<platform>:install(Re-)install the build on the device
e2e:<platform>:run [path|tag]Execute flows with reports + debug artifacts
e2e:<platform>:studioLaunch Maestro Studio against the running app
e2e:<platform>:cleanUninstall the app from the device
e2e:<platform>:killShut down the emulator/simulator

TIP

Start with doctor β€” it tells you exactly which prerequisite is missing (SDK path, no AVD, maestro not on PATH) before you wait on a build.

Useful env overrides ​

VariableEffect
MAESTRO_INCLUDE_TAGSTag filter (default smoke). e.g. smoke,permissions-deny
IOS_SIMULATORPin the iOS device, e.g. IOS_SIMULATOR="iPhone 17 Pro" (iOS only)
AVD_NAMEPick a specific AVD (Android only; default = first emulator -list-avds)
ANDROID_HOMEAndroid SDK root (default ~/Library/Android/sdk)

Reports ​

Each run writes to apps/mobile/.maestro/reports/<timestamp>/, with an HTML report, device logs, and per-flow screenshots on failure. .maestro/reports/latest points at the most recent run. Open the report:

bash
open apps/mobile/.maestro/reports/latest/report.html

Maestro Studio (recorder / inspector) ​

Studio walks the running app and records taps/asserts as YAML, and its "Inspect" view confirms testID β†’ accessibilityIdentifier parity (important for Mapbox / ActionSheet wrappers on iOS).

bash
pnpm --filter @tellia-solutions/mobile-app e2e:ios:studio       # auto-installs+launches, pins to the simulator UDID
pnpm --filter @tellia-solutions/mobile-app e2e:android:studio   # auto-picks the running emulator

Studio opens at http://localhost:9999.

Pre-submit gate ​

Before eas submit --profile staging --platform <android|ios>, run the matching e2e:<platform> pipeline and confirm green. The smoke suite is the release gate today β€” there's no automated enforcement, just the convention. If a flow fails, fix it (or document a Maestro bug) before submitting; don't submit on red.

Authoring flows (essentials) ​

Full guidance is in the .maestro/README.md β€” the load-bearing rules:

  • testID needs a button role on Pressables. Bare <Pressable testID="x"> doesn't surface accessibilityIdentifier reliably β€” add accessibilityRole="button" (the shared Button already does).
  • Inside an ActionSheet, never call hideKeyboard between inputText and tapOn. Maestro maps it to Android BACK, which dismisses the sheet.
  • Cross-platform first. Default to one nn-name.yaml; only fork into *.android.yaml / *.ios.yaml when platform commands genuinely diverge.
  • Tag every new flow β€” at minimum smoke (cross-platform) or platform-<os>-only.
  • Strip newlines from labels and typed text β€” Maestro's text: substring match silently fails when the target contains a \n.

Troubleshooting ​

SymptomFix
doctor reports maestro not foundRe-run e2e:install-cli, add ~/.maestro/bin to PATH
"No AVD configured" (Android)Create a Pixel API 33+ device in Android Studio Device Manager
Map renders blank / reload iconEXPO_PUBLIC_MAPBOX_ACCESS_TOKEN missing in .maestro/.env (must be pk.*)
Login fails on every flowWrong/empty MAESTRO_TEST_* creds, or account doesn't exist in staging Firebase
text: assertion never matchesTarget string contains a newline β€” keep labels/transcripts single-line
App installs but is the wrong envE2E must build the preview/staging variant β€” use the pipeline, not a dev build