maestro-flows
Authors Maestro YAML flow files (`.maestro/*.yaml`) for mobile + web UI automation: declarative `tapOn`, `inputText`, `assertVisible`, `swipe`, supported targets (iOS, Android, Flutter, React Native, web), nested flow imports, JavaScript hooks for complex conditions. Use when the team has already chosen Maestro, is coming from an existing `.maestro/` directory, or explicitly wants YAML-declarative tests readable by non-engineers without a compile step. For framework selection or authoring tests in XCUITest / Espresso / Detox / Appium / Flutter, use mobile-driver-selector or mobile-test-author instead.
maestro-flows
Overview
Per maestro-docs:
"Maestro [is] the simplest and most effective framework for painless mobile and web UI automation using intuitive YAML flows."
The flow file is the artifact - no compile step, no language runtime to set up.
The platform ships three pieces (maestro-docs):
When to use
If the team needs deep gray-box hooks (per-component intercepts), detox-testing (RN) or framework-specific unit/widget tests cover that better.
Step 1 - Install Maestro CLI
curl -Ls "https://get.maestro.mobile.dev" | bash
maestro --versionCross-platform: macOS, Linux, Windows (WSL recommended).
Step 2 - Author a flow
# .maestro/cart-flow.yaml
appId: com.example.app
---
- launchApp
- tapOn: "Sign in"
- inputText: "qa-test@example.com"
- tapOn: "Password"
- inputText: "test-password"
- tapOn: "Continue"
- assertVisible: "Welcome"
- tapOn: "Add to cart"
- assertVisible:
text: "Cart"
enabled: true
- tapOn: "Cart"
- assertVisible: "1 item"
- tapOn: "Checkout"The file is YAML with appId declaration + --- separator + a list of commands. Commands map directly to user actions.
Step 3 - Common commands
| Command | Purpose |
|---|---|
launchApp | Start the app fresh |
tapOn: "<text>" | Tap an element with visible text |
tapOn: { id: "..." } | Tap by accessibility ID |
inputText: "..." | Type text into the focused field |
assertVisible: "..." | Assert element is visible |
assertNotVisible: "..." | Assert element is NOT visible |
swipe: { direction: UP } | Swipe |
scrollUntilVisible: { element: { text: "..." } } | Scroll until found |
back | Press back / navigate back |
pressKey: ENTER | Synthesize a hardware key |
takeScreenshot | Capture screenshot to artifact dir |
runFlow: "<other.yaml>" | Compose flows |
evalScript: "<js>" | Run JavaScript for complex conditions |
Step 4 - Flow modularity
# .maestro/login.yaml
appId: com.example.app
---
- launchApp
- tapOn: "Sign in"
- inputText:
text: ${EMAIL} # env-variable interpolation
- tapOn: "Password"
- inputText: ${PASSWORD}
- tapOn: "Continue"
- assertVisible: "Welcome"# .maestro/cart-flow.yaml
appId: com.example.app
---
- runFlow: "login.yaml"
- tapOn: "Add to cart"
- assertVisible: "1 item"The runFlow composition keeps shared paths (login, navigation) DRY across the test suite.
Step 5 - JavaScript hooks for complex conditions
- evalScript: |
output.timestamp = new Date().toISOString();
- inputText: ${output.timestamp}
- tapOn: "Save"
# Conditional flow
- runFlow:
when:
visible: "Onboarding"
file: "skip-onboarding.yaml"evalScript reads + writes to an output object that subsequent commands can reference. when makes commands conditional.
Step 6 - Run
# Single flow
maestro test .maestro/cart-flow.yaml
# Whole directory
maestro test .maestro/
# With env vars
EMAIL=qa-test@example.com PASSWORD=test maestro test .maestro/Step 7 - Studio for visual authoring
maestro studioOpens a desktop app showing the connected device + an inspector for each tapped element. Click an element → Studio generates the corresponding YAML command. For non-engineers, this dramatically shortens the authoring loop.
Step 8 - CI integration
# .github/workflows/maestro.yml
jobs:
maestro-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
script: |
curl -Ls "https://get.maestro.mobile.dev" | bash
export PATH="$HOME/.maestro/bin:$PATH"
maestro test .maestro/
maestro-ios:
runs-on: macos-15
steps:
- uses: actions/checkout@v5
- run: |
xcrun simctl boot 'iPhone 15'
curl -Ls "https://get.maestro.mobile.dev" | bash
export PATH="$HOME/.maestro/bin:$PATH"
maestro test .maestro/For matrix runs across N devices in parallel, use Maestro Cloud (per maestro-docs) - handles farm-side parallelism without local emulator orchestration.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
tapOn text that's translated | Tests fail in non-English locales. | Use tapOn: { id: "..." } for stable lookups. |
| One mega-flow with 100 steps | Failure mid-flow obscures cause; reruns repeat all 100 steps. | Modularize via runFlow (Step 4). |
| Hard-coded credentials in YAML | Secrets in git. | Env vars + ${VAR} interpolation (Step 4). |
evalScript for everything (writing tests in JS via YAML) | Defeats Maestro's YAML simplicity. | Use evalScript only for genuinely complex conditions. |
swipe without direction / verification | Swipes vary per platform; visible state may not change. | assertVisible after swipe to confirm state changed. |
Skipping appId declaration | Maestro can't tell which app to drive; ambiguous failures. | Always declare appId (Step 2). |