| name | maestro-flows |
| description | Expert guidance on writing, running, and parallelizing Maestro YAML flows for cross-platform mobile E2E testing. Use when adding Maestro coverage, wiring Maestro to CI, or moving off Detox/Appium for smoke flows. |
Maestro Flows
Instructions
Maestro is a YAML-based black-box E2E runner that works on Android, iOS, and Flutter / React Native without a tool-specific binding. Its strengths: simple syntax, built-in retry, great CI ergonomics on Firebase Test Lab and Maestro Cloud. Its weakness: black-box only — no access to in-process state — so pair it with unit and integration tests for depth.
1. Project Layout
.maestro/
├── config.yaml # shared env, onFlowStart hooks
├── smoke/
│ ├── login.yaml
│ ├── checkout-happy.yaml
│ └── tabs-smoke.yaml
└── regression/
├── account-settings.yaml
└── search-results.yaml
Commit .maestro/ at the repo root. Tag flows by directory; CI runs one tier at a time.
2. A Minimal Flow
appId: com.example.app
tags:
- smoke
onFlowStart:
- runScript: ../scripts/seed_user.sh
---
- launchApp:
clearState: true
- assertVisible: "Sign in"
- tapOn:
id: "emailField"
- inputText: "${EMAIL}"
- tapOn:
id: "passwordField"
- inputText: "${PASSWORD}"
- tapOn: "Sign in"
- assertVisible:
id: "homeFeed"
timeout: 15000
Use IDs (testID / accessibilityIdentifier / Key), not visible text, for selectors that matter across locales.
3. Parameterizing Runs
Top-level env or CI environment variables feed ${VAR} substitution:
EMAIL=e2e+$RUN_ID@example.com \
PASSWORD=$E2E_PASSWORD \
maestro test .maestro/smoke/
Use runScript: to call a tiny shell or Node script that seeds users via the backend API and echoes credentials as output.EMAIL=....
4. Composition: runFlow
Factor shared preconditions into sub-flows:
- launchApp: { clearState: true }
- tapOn: { id: "emailField" }
- inputText: "${EMAIL}"
- tapOn: { id: "passwordField" }
- inputText: "${PASSWORD}"
- tapOn: "Sign in"
- runFlow: ../_shared/login.yaml
- tapOn: { id: "cartTab" }
- assertVisible: { id: "cartEmpty" }
5. Waits, Retries, Assertions
- Every
assertVisible accepts timeout: in ms — use it; the default (~5 s) is often too short after a navigation.
extendedWaitUntil: for long async operations (network post-login).
retry: on whole flows for transient device-farm noise; pair with a flake budget (see flaky-test-reduction).
Avoid generic sleeps. If you find yourself adding - sleep: 3000, add an extendedWaitUntil on a visible element instead.
6. Platform Differences
Use platform: guards sparingly. Most flows run unmodified on Android and iOS. When platforms diverge:
- runFlow:
when:
platform: android
file: ../_shared/android_system_dialog.yaml
- runFlow:
when:
platform: ios
file: ../_shared/ios_permission_dialog.yaml
Keep divergent steps at system-dialog boundaries; the app UI itself should be shared.
7. CI Integration
GitHub Actions sketch:
- uses: mobile-dev-inc/action-maestro-cloud@latest
with:
api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }}
app-file: app/build/outputs/apk/release/app-release.apk
include-tags: smoke
For self-hosted runs on Firebase Test Lab:
gcloud firebase test android run \
--type instrumentation \
--app app-debug.apk \
--test app-debug-androidTest.apk \
--device model=redfin,version=33,orientation=portrait
For Android-only Maestro, you can also use the Maestro bundle: maestro test --device emulator-5554 .maestro/smoke/.
8. Parallelization
- Shard by flow file. Maestro Cloud shards automatically; on self-hosted, split directories across matrix jobs.
- Isolate user data. Each shard uses a different seeded account (
EMAIL=e2e+$RUN_ID-$SHARD@example.com).
- Cap concurrent writes to shared backend resources; prefer per-user resources.
9. Failure Artifacts
Maestro Cloud and the CLI both produce a video + screenshots per step. Upload them unconditionally on failure:
- name: Upload Maestro artifacts
if: failure()
uses: actions/upload-artifact@v4
with: { name: maestro, path: ~/.maestro/tests/ }
10. What Maestro Is Not Good At
- In-process assertions (Redux state, coroutine job counts) — use component tests.
- Very complex branching logic — YAML becomes unreadable; split into sub-flows or move to code-based (XCUITest, Espresso, Detox).
- Anything needing real biometrics, push certificates, or deep OS integration — use the platform framework.
11. Checklist