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 a mobile driver-selection or per-flow mobile test-authoring step instead.
Install with skills.sh (any agent)
npx skills add testland/qa --skill maestro-flowsmaestro-flows
Overview
Maestro automates mobile and web UI from declarative YAML flows (per maestro-docs (opens in new window)). The flow file is the artifact - no compile step, no language runtime to set up.
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/Verify: maestro test exits non-zero if any command fails. If it does, Maestro writes a screenshot of the failing screen to its output dir - use it to fix the failing tapOn / assertVisible target, then re-run before moving on.
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/Verify: maestro test exits non-zero on any flow failure, so a failing flow fails the job. Upload Maestro's screenshot output as a build artifact (e.g. actions/upload-artifact) so batch failures are debuggable without a local re-run.
For matrix runs across N devices in parallel, use Maestro Cloud (per maestro-docs (opens in new window)) - 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). |
Limitations
References
Related skills
appium-testing
Wires Appium for cross-platform mobile UI automation - uses the WebDriver protocol, picks a driver per platform (XCUITest for iOS, UiAutomator2 / Espresso for Android, Mac2 for macOS, Windows for desktop), authors tests in JS / Python / Java / Ruby / .NET, configures `desiredCapabilities`, runs against simulators / emulators / device farms. Use when a single test suite must cover both iOS and Android, or when the team's stack is multi-platform (iOS + Android + Mac + Windows).
detox-testing
Authors React Native E2E tests with Detox (Wix) - gray-box architecture (runs in-process with the app), `element(by.id|by.text|by.label)` matchers, `waitFor()` for explicit sync beyond Detox's automatic async tracking, Jest runner. Use when the app is React Native and speed matters. For Flutter use flutter-testing; for black-box cross-platform use appium-testing; for YAML-declarative flows use maestro-flows; for non-RN native use xcuitest-suite or espresso-suite.
espresso-suite
Authors Espresso UI tests for Android - uses `onView(withId(...)).perform(...).check(matches(...))`, leans on Espresso's automatic synchronization (no `Thread.sleep`), wires `IdlingResource` for app-specific async, runs via `./gradlew connectedAndroidTest` and parses the JUnit XML output. Use when an Android app needs UI tests in Google's first-party framework.
flutter-testing
Authors Flutter tests across the three-layer pyramid - unit (`flutter test`), widget (`testWidgets` + `WidgetTester`), integration (`integration_test` on simulator/emulator/device). Picks the right layer per change, mocks via `mockito` + `build_runner`, LCOV coverage, CI with the Flutter Action. Use when the app is Flutter and the team wants its first-party stack. For React Native use detox-testing; for black-box cross-platform use appium-testing; for YAML-declarative flows use maestro-flows.
mobile-a11y-test-author
Authors native mobile accessibility tests covering iOS (Accessibility Inspector, XCUITest `performAccessibilityAudit()` introduced in iOS 17, VoiceOver label/trait/hint verification) and Android (Espresso `AccessibilityChecks.enable()`, Accessibility Scanner, TalkBack traversal, `contentDescription` labelling) with WCAG-aligned checks for element labels, 44pt/48dp touch targets, contrast ratios, and focus order. Use when an iOS or Android app needs automated and manual accessibility test coverage beyond what `xcuitest-suite` or `espresso-suite` provide.
mobile-device-matrix-toolkit
Dispatches mobile UI test runs across a 3-tier device matrix (smoke per-PR, regression per-merge, full farm at release) to control CI cost: generates per-target Appium capability configs from a central YAML, parallelises via GitHub Actions matrix strategy, and aggregates JUnit XML into a cross-device pass/fail table. Use when deciding which iOS / Android devices and OS versions to run tests on and at which stage (smoke / regression / full farm), not how to configure a specific test framework (for that, use xcuitest-suite, espresso-suite, etc.).
mobile-web-emulation-runner
Builds a workflow to run web E2E tests under mobile viewports + DPRs (device pixel ratios): Playwright's `devices` catalog (iPhone 15, Pixel 7), suite run per-device as matrix shards, per-device screenshots, mobile assertions (`.tap()`, viewport-conditional layout). Use when a responsive web app needs mobile-breakpoint regression without a real-device farm. Mobile WEB only - for native apps use appium-testing, detox-testing, or flutter-testing; for cross-shard aggregation use mobile-device-matrix-toolkit; for gesture sequences use touch-gesture-tester.
mobile-web-perf-budget
Pure-reference skill for mobile-web performance budgets - Core Web Vitals at the 75th percentile mobile (LCP ≤2.5s, INP ≤200ms, CLS ≤0.1; FID retired March 2024 in favor of INP), Lighthouse mobile profile config, per-route resource budgets (JS bundle, image weight, font load). Use as the team's reference for "what should the mobile perf gate enforce" - paired with `lighthouse-perf` (the runner) and `lighthouse-budget-author` (the per-route author).
touch-gesture-tester
Verifies touch-gesture handlers (tap, double-tap, long-press, swipe, pinch, rotate, pan) work as expected under both mobile-emulation (Playwright) and native (XCUITest / Espresso / Detox) - distinguishes "mouse click handler also fires on tap" from "real touch event fired with correct properties." Use when the app has bespoke gesture handlers (custom carousels, sliders, drag-drop, pull-to-refresh) and the team needs targeted gesture verification beyond generic UI assertions.
xcuitest-suite
Authors XCUIest UI tests for iOS / iPadOS / tvOS - uses the three-class XCUIApplication / XCUIElement / XCUIElementQuery pattern, sets accessibility identifiers on production code, runs via `xcodebuild test` with destination, parses the `xcresult` bundle. Use when an iOS app needs UI tests in Apple's first-party framework (no external runtime; native to Xcode).