agentsclimarketplace

Mobile testing maestro

Skill agents-inc/skills/src/skills/mobile-testing-maestro

The official skills marketplace for Agents Inc, an agent composition framework that builds stacks and compiles specialized subagents for Claude Code

Install
npx -y skills add agents-inc/skills --skill mobile-testing-maestro

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 18 stars18 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Maestro mobile E2E testing - YAML flows, selectors, flow control, environment variables, JavaScript expressions, device interactions, Maestro Studio, Maestro Cloud CI, tags, test suites

SKILL.md

16.5 KB, ~3.8k tokens by cl100k_base, as published. Nobody here has run it

Maestro Mobile UI Testing Patterns

Quick Guide: Write E2E tests as declarative YAML flows. Use id selectors for stable element targeting (not text that changes with localization). Use runFlow to compose reusable subflows (login, setup). Use waitForAnimationToEnd before assertions on animated screens. Use onFlowStart/onFlowComplete hooks for setup/teardown. Maestro auto-retries assertions for up to 7 seconds before failing. Current stable: CLI 2.4.0.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST use id selectors (accessibility identifiers) as primary selectors - text selectors break with localization or copy changes)

(You MUST use runFlow for reusable sequences (login, onboarding) - NEVER duplicate steps across flow files)

(You MUST use waitForAnimationToEnd before assertions on screens with animations or transitions - assertions on animated elements are flaky)

(You MUST pair every startRecording with a stopRecording - unpaired commands produce corrupted or missing video files)

(You MUST use environment variables or env blocks for credentials and environment-specific values - NEVER hardcode secrets in YAML flows)

</critical_requirements>


Auto-detection: Maestro, maestro, .maestro, maestro test, maestro cloud, maestro studio, launchApp, tapOn, assertVisible, assertNotVisible, inputText, scrollUntilVisible, runFlow, evalScript, runScript, swipe, hideKeyboard, waitForAnimationToEnd, onFlowStart, onFlowComplete, maestro.yaml, config.yaml tags

When to use:

  • Writing E2E UI tests for iOS and Android mobile apps
  • Automating user workflows (login, checkout, onboarding) with YAML flows
  • Testing cross-platform behavior from a single flow file
  • Running mobile tests in CI with Maestro Cloud
  • Recording test execution for debugging or documentation
  • Testing deep links, location, permissions, and device interactions

When NOT to use:

  • Unit testing business logic (use your unit test framework)
  • API-only testing without UI (use direct HTTP tests)
  • Testing web-only applications without mobile component
  • Performance profiling or load testing (Maestro is for functional UI flows)

Key patterns covered:

  • Flow structure with appId, YAML commands, and selectors
  • Selector strategies: id (preferred), text, point, relational, state
  • Flow control: runFlow, repeat, retry, conditions (when), hooks
  • Environment variables and parameterized flows
  • JavaScript expressions: inline ${}, evalScript, runScript, output object
  • Device interactions: swipe, scroll, setLocation, openLink, permissions
  • Workspace configuration: tags, test discovery, execution order
  • Maestro Studio for visual flow creation and element inspection
  • Maestro Cloud for CI integration with GitHub Actions

Detailed Resources:


<philosophy>

Philosophy

Maestro takes a fundamentally different approach from code-based testing frameworks: tests are declarative YAML, not imperative code. This makes flows readable by anyone on the team, not just developers. The framework handles the hard parts of mobile testing automatically -- waiting for elements, retrying taps, tolerating animation delays -- so flows focus on what to test, not how to wait.

Core principles:

  1. Declarative over imperative - YAML flows describe user intent, not implementation details
  2. Built-in tolerance - Maestro auto-waits up to 7 seconds for elements, auto-retries taps, and handles animation delays without explicit waits
  3. Single flow, multiple platforms - One YAML file can test both iOS and Android with platform conditions for differences
  4. Composition over duplication - Extract reusable sequences (login, setup, teardown) into subflows with runFlow
  5. Stable selectors - Use accessibility identifiers (id) over visible text to survive localization and copy changes

Mental model:

Maestro flows are recipes. Each step is an action a user would take. The framework handles timing, retries, and platform differences. You describe the journey, Maestro drives the car.

When to use Maestro:

  • Smoke tests for critical user journeys (login, purchase, onboarding)
  • Regression tests for flows that broke before
  • Cross-platform verification with a single flow file
  • Visual recording of test runs for stakeholder review

When NOT to use Maestro:

  • Isolated unit tests for business logic
  • API contract testing without UI
  • Performance benchmarking or load testing
  • Complex data-driven testing requiring heavy programmatic logic (Maestro's JS support is limited compared to full test frameworks)
</philosophy>
<patterns>

Core Patterns

Pattern 1: Flow Structure and Basic Commands

Every flow starts with a configuration block (appId, optional env/tags) separated from commands by ---. Commands execute sequentially top to bottom.

appId: com.example.app
tags:
  - smoke
  - auth
---
- launchApp
- tapOn:
    id: "email_input"
- inputText: "[email protected]"
- tapOn:
    id: "password_input"
- inputText: "secure_password"
- tapOn:
    id: "login_button"
- assertVisible:
    id: "home_screen"

Why good: appId identifies the app under test, tags enable filtering with --include-tags/--exclude-tags, id selectors are stable across localizations, sequential commands read like a user story

See examples/core.md for complete flow structure with env blocks, labels, and clearState.


Pattern 2: Selector Strategies

Use id (accessibility identifier) as the primary selector. Fall back to text for static labels, point for coordinates only as last resort. Combine selectors for precision.

# Preferred: id selector (stable, language-independent)
- tapOn:
    id: "submit_button"

# Fallback: text selector (breaks with i18n changes)
- tapOn:
    text: "Submit"

# Relational: below/above/childOf for disambiguation
- tapOn:
    text: "Delete"
    below: "Shopping Cart"

# State selectors: filter by element state
- tapOn:
    id: "toggle_switch"
    enabled: true

Why good: id selectors survive text changes, relational selectors disambiguate duplicate labels, state selectors prevent tapping disabled elements

See examples/core.md for all selector types including index, point, and combined selectors.


Pattern 3: Reusable Subflows with runFlow

Extract repeated sequences into separate flow files. Pass context via env parameters. Use label for clear test reports.

# Main flow: checkout-test.yaml
appId: com.example.app
---
- runFlow:
    file: subflows/login.yaml
    env:
      USERNAME: "[email protected]"
      PASSWORD: "test_password"
    label: "Log in as test user"
- tapOn:
    id: "cart_icon"
- runFlow:
    file: subflows/complete-checkout.yaml
    label: "Complete purchase flow"
- assertVisible:
    id: "order_confirmation"
# Subflow: subflows/login.yaml
appId: com.example.app
---
- tapOn:
    id: "email_input"
- inputText: ${USERNAME}
- tapOn:
    id: "password_input"
- inputText: ${PASSWORD}
- tapOn:
    id: "login_button"

Why good: login sequence defined once and reused across all flows, env parameters make subflows configurable, labels improve test report readability

See examples/flow-control.md for inline subflows, conditional flows, and nested composition.


Pattern 4: Conditions and Platform-Specific Logic

Use when with platform, visible, notVisible, or JavaScript true expressions to handle differences between iOS and Android or optional UI states.

# Platform-specific permission handling
- runFlow:
    when:
      platform: Android
    commands:
      - tapOn: "Allow"

- runFlow:
    when:
      platform: iOS
    commands:
      - tapOn: "Allow While Using App"

# Dismiss optional popup if visible
- runFlow:
    when:
      visible: "Rate this app"
    commands:
      - tapOn: "Not now"

Why good: single flow handles both platforms, visibility conditions handle non-deterministic UI (popups, tooltips), no test failure on missing optional elements

See examples/flow-control.md for JavaScript conditions and combined conditions.


Pattern 5: Environment Variables and Parameterized Flows

Pass runtime values via CLI flags (-e), shell variables (MAESTRO_ prefix), or env blocks in flow files. Use ${} syntax for interpolation with JavaScript fallback defaults.

appId: com.example.app
env:
  BASE_URL: "https://staging.example.com"
  DEFAULT_USER: "[email protected]"
---
- launchApp
- tapOn:
    id: "email_input"
- inputText: ${USERNAME || DEFAULT_USER}
# Override from CLI
maestro test -e [email protected] -e PASSWORD=secret flow.yaml

Why good: secrets never hardcoded in flow files, env blocks provide defaults, CLI overrides enable multi-environment testing, || fallback prevents failures when variables are missing

See examples/flow-control.md for built-in variables, runScript with env, and shell variable patterns.


Pattern 6: Hooks for Setup and Teardown

Use onFlowStart and onFlowComplete in the configuration block for consistent setup/teardown across all flows. onFlowComplete runs even if the flow fails.

appId: com.example.app
onFlowStart:
  - clearState
  - runFlow:
      file: subflows/login.yaml
      env:
        USERNAME: "[email protected]"
        PASSWORD: "test_password"
onFlowComplete:
  - runFlow: subflows/cleanup.yaml
---
- tapOn:
    id: "settings_icon"
- assertVisible:
    id: "settings_screen"

Why good: clearState ensures clean app state, login runs before every flow, cleanup always runs (even on failure), prevents test pollution between flows

Hook failure behavior: If onFlowStart fails, the main flow is skipped but onFlowComplete still executes. If onFlowComplete fails, the flow is marked as failed even if the main test passed.

See examples/flow-control.md for hooks with environment variables and script-based teardown.


Pattern 7: JavaScript Expressions

Use inline ${} for simple interpolation, evalScript for variable computation, and runScript for complex logic in external .js files. All share a global output object.

# Inline expression
- inputText: user_${Date.now()}@test.com

# evalScript for computation
- evalScript: ${output.timestamp = Date.now()}
- inputText: ${output.timestamp}

# runScript for complex logic (external file)
- runScript: scripts/generate-test-data.js
- inputText: ${output.generatedEmail}

Why good: inline expressions handle simple dynamic values, evalScript sets variables without UI interaction, runScript keeps complex logic in testable JS files, output object passes data between steps

See examples/flow-control.md for HTTP requests in scripts, DataFaker, and output namespacing.

</patterns>

<decision_framework>

Decision Framework

Selector Choice

Can you add an accessibility identifier (testID/accessibilityIdentifier)?
|-- YES -> Use id selector (most stable)
+-- NO  -> Is the text static and unique on screen?
    |-- YES -> Use text selector
    +-- NO  -> Is there a unique parent or sibling?
        |-- YES -> Use relational selector (below, childOf, etc.)
        +-- NO  -> Use point selector as last resort (fragile)

Flow Organization

Is this sequence used in 2+ flows?
|-- YES -> Extract to subflows/ directory, call with runFlow
+-- NO  -> Keep inline in the flow

Does the flow need setup/teardown?
|-- YES -> For ALL flows: use onFlowStart/onFlowComplete in config.yaml
|          For ONE flow: use runFlow at start/end of that flow
+-- NO  -> Start with launchApp directly

Is there platform-specific behavior?
|-- YES -> Use when: platform: Android/iOS conditions
+-- NO  -> Single flow handles both platforms

When to Use JavaScript

Need a dynamic value (timestamp, random ID)?
|-- YES -> Inline ${} expression (e.g., ${Date.now()})
+-- NO  -> Need to compute and store a value?
    |-- YES -> evalScript for simple computation
    +-- NO  -> Need HTTP calls, file I/O, or complex logic?
        |-- YES -> runScript with external .js file
        +-- NO  -> Plain YAML commands are sufficient

</decision_framework>


<red_flags>

RED FLAGS

High Priority Issues:

  • Using text selectors for buttons/labels that will be localized - breaks when language changes. Use id (accessibility identifiers) instead.
  • Duplicating login/setup steps in every flow file - extract to subflow and call with runFlow
  • Missing stopRecording after startRecording - produces corrupted or zero-byte video files
  • Hardcoding credentials or API keys in YAML flow files - use environment variables with -e or MAESTRO_ prefix
  • Using arbitrary sleep or extendedWaitUntil with long timeouts instead of waitForAnimationToEnd - Maestro's built-in tolerance handles most timing issues automatically

Medium Priority Issues:

  • Not using clearState or clearKeychain in setup - test results depend on leftover app state from previous runs
  • Not using tags for flow categorization - makes it impossible to run targeted subsets (smoke, regression, etc.)
  • Using point selectors (coordinates) as primary strategy - breaks on different screen sizes and resolutions
  • Not using label on runFlow calls - test reports show file paths instead of meaningful step descriptions
  • Putting all flows in the root directory without subdirectories - becomes unmanageable beyond 20+ flows

Gotchas and Edge Cases:

  • assertVisible auto-retries for 7 seconds before failing - this is a feature, not a bug. Don't add explicit waits before assertions.
  • CLI parameters are always strings - use parseInt() or comparison in JavaScript if you need numeric logic
  • MAESTRO_ prefixed shell variables are automatically available in flows but only via CLI, not Maestro Studio
  • The string "false" is truthy in JavaScript - use explicit === "true" comparison in when: true: conditions
  • onFlowComplete runs even when the flow fails - design teardown logic that doesn't assume success
  • runFlow with commands (inline) and runFlow with file (external) are mutually exclusive - you cannot use both in the same runFlow call
  • Template literals (backticks) do not work inside evalScript because the command is already wrapped in ${} - use string concatenation instead
  • console.log in evalScript writes to maestro.log, not the terminal - use runScript for terminal-visible logging
  • retry maxRetries is capped at 3 - for more attempts, restructure the flow logic
  • Maestro Cloud --async flag returns immediately without waiting for results - poll the API or use webhooks for completion
  • FlashList / RecyclerView items may not have stable accessibility IDs - use scrollUntilVisible with text fallback for list items

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST use id selectors (accessibility identifiers) as primary selectors - text selectors break with localization or copy changes)

(You MUST use runFlow for reusable sequences (login, onboarding) - NEVER duplicate steps across flow files)

(You MUST use waitForAnimationToEnd before assertions on screens with animations or transitions - assertions on animated elements are flaky)

(You MUST pair every startRecording with a stopRecording - unpaired commands produce corrupted or missing video files)

(You MUST use environment variables or env blocks for credentials and environment-specific values - NEVER hardcode secrets in YAML flows)

Failure to follow these rules will produce flaky tests, broken recordings, and security-exposed credentials in version control.

</critical_reminders>

What ships with it: 5 files

31.3 KB alongside SKILL.md

Keep looking

Skills are one crate of 327,069. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.