CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

testland/maestro-flows

Use when a team uses or wants Maestro YAML flows for mobile or web UI tests that non-engineers can read: .maestro/*.yaml with tapOn, inputText, assertVisible and swipe, for iOS, Android, Flutter, React Native and web, with nested flows and JavaScript hooks.

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

SKILL.md

name:
maestro-flows
description:
Use when a team uses or wants Maestro YAML flows for mobile or web UI tests that non-engineers can read: .maestro/*.yaml with tapOn, inputText, assertVisible and swipe, for iOS, Android, Flutter, React Native and web, with nested flows and JavaScript hooks.

maestro-flows

Overview

Maestro automates mobile and web UI from declarative YAML flows (per maestro-docs). The flow file is the artifact - no compile step, no language runtime to set up.

When to use

  • A team needs cross-platform mobile UI tests with the lowest setup ceremony.
  • Non-engineers (PMs, designers) need to write or read tests - YAML is more accessible than Swift / Kotlin / JS.
  • The product spans iOS + Android (and optionally Flutter / RN / web).
  • Existing Detox / Appium suites would be overkill for the test count (5-30 critical flows).

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 --version

Cross-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

CommandPurpose
launchAppStart 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
backPress back / navigate back
pressKey: ENTERSynthesize a hardware key
takeScreenshotCapture 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 studio

Opens 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) - handles farm-side parallelism without local emulator orchestration.

Anti-patterns

Anti-patternWhy it failsFix
tapOn text that's translatedTests fail in non-English locales.Use tapOn: { id: "..." } for stable lookups.
One mega-flow with 100 stepsFailure mid-flow obscures cause; reruns repeat all 100 steps.Modularize via runFlow (Step 4).
Hard-coded credentials in YAMLSecrets 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 / verificationSwipes vary per platform; visible state may not change.assertVisible after swipe to confirm state changed.
Skipping appId declarationMaestro can't tell which app to drive; ambiguous failures.Always declare appId (Step 2).

Limitations

  • No deep state inspection. Maestro is UI-level; for assertions on app internals (Redux state, network calls), gray-box frameworks (detox-testing) fit better.
  • Per-platform UI quirks. Same YAML may behave differently iOS vs Android (toast positioning, keyboard interaction); per-platform branching via evalScript may be needed.
  • Limited debugging tools. Failure debugging relies on screenshots + studio replay; less rich than IDE-debugger integration.
  • Cloud features behind Maestro Cloud. Parallel matrix runs + per-device farms are paid (per maestro-docs).

References

  • mae - Maestro overview: YAML flows, three-component model (CLI / Studio / Cloud), modular learning path.
  • detox-testing, appium-testing - alternative cross-platform frameworks with deeper hooks.
  • xcuitest-suite, espresso-suite - native alternatives for single-platform.

SKILL.md

tile.json