tovimx/maestro-mobile-testing-skillMIT1 file

Maestro Mobile Testing

Maestro mobile E2E testing patterns for React Native/Expo apps: YAML test flows, testID selectors, adaptive auth state, optimistic update verification, GraalJS scripting, cross-platform stability, CI/CD integration, Maestro Cloud, and MCP server integration

Specification
Skill ID
tovimx/maestro-mobile-testing-skill/maestro-mobile-testing
Publisher
tovimx
Repository
maestro-mobile-testing-skill
Installs
0
Files
1
License
MIT
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

tovimx/maestro-mobile-testing-skill/maestro-mobile-testingInstalls these files
  • SKILL.md

What this skill tells the agent

Maestro Mobile E2E Testing

Overview

Maestro is a declarative YAML-based mobile E2E testing framework. It provides automatic waiting, built-in retry logic, and fast execution without boilerplate. It's more stable than Detox or Appium for React Native apps.

Key Features

  • Declarative YAML — no imperative test code, just steps
  • Automatic waiting — no manual sleep() or flaky waits
  • Built-in retry — reduces test flakiness
  • Fast execution — runs quickly without setup overhead
  • Maestro Studio — interactive test builder (maestro studio)
  • Sub-flows — reusable YAML sequences for DRY tests
  • JavaScript scripting — GraalJS runtime for HTTP calls and data manipulation
  • Maestro Cloud — real device testing in CI without local simulators

Quick Start

Install

curl -Ls "https://get.maestro.mobile.dev" | bash
brew install openjdk@17
export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home

Minimal test

appId: com.myapp
---
- launchApp
- tapOn:
    id: "my-button"
- assertVisible: "Expected Text"

Run

maestro test .maestro/smoke-test.yaml
maestro test --debug .maestro/smoke-test.yaml  # step through
maestro studio                                  # interactive builder

Core Patterns

1. Selector Strategy: testID vs Text

Choose your selector approach based on project context. Both are valid — the right choice depends on whether your app is localized and your team's testing philosophy.

ContextRecommended SelectorRationale
Multi-language / i18nid: (testID)Stable across translations
Single languageText labelsHuman-readable, self-documenting tests
Agent-maintained testsEither — ask the developerReadability matters less for AI-maintained flows
System dialogsText (always)No testID possible on native alerts
# testID selector — stable across translations
- tapOn:
    id: "submit-button"

# Text selector — human-readable, self-documenting
- tapOn: "Submit"

When to prefer testIDs:

  • App supports multiple languages or will be translated
  • UI text is dynamic or frequently changes
  • Multiple elements share the same visible text

When to prefer text selectors:

  • Single-language app with stable copy
  • Readability and self-documentation are a priority
  • Testing user-visible behavior exactly as it appears

In React Native, add testID props when using ID-based selectors:

<TouchableOpacity testID="submit-button" onPress={handleSubmit}>
  <Text>{t('submit')}</Text>
</TouchableOpacity>

testID Naming Convention

When using ID-based selectors:

{component}-{action/type}[-{variant}]

Examples:
- auth-prompt-login-button
- product-card-{id}
- otp-input-0
- tab-home
- dashboard-loading

2. Auth Pre-Flight Pattern

Prevent race conditions where Maestro interacts with the UI before auth state resolves. Add a zero-size auth-loaded marker that only renders when auth loading completes:

// In your tab bar or root layout
{!isLoading && <View testID="auth-loaded" style={{ width: 0, height: 0 }} />}

Then in every test:

- launchApp

# Prevent XCTest crash on cold boot (iOS)
- swipe:
    direction: DOWN
    duration: 100

# Wait for auth state to resolve
- extendedWaitUntil:
    visible:
      id: "auth-loaded"
    timeout: 15000

# Now safe to interact
- tapOn:
    id: "tab-home"

3. Adaptive Tests (Handle Both Auth States)

Tests should work regardless of whether the user is authenticated:

# Auth flow — only runs if login prompt is visible
- runFlow:
    when:
      visible: "Sign In"
    file: flows/auth-flow.yaml

# Already authenticated — proceed directly
- runFlow:
    when:
      visible:
        id: "tab-home"
    file: flows/authenticated-action.yaml

4. Testing Optimistic Updates

Use short timeouts to verify UI changes happen before server response:

# Trigger mutation
- tapOn:
    id: "action-button"

# OPTIMISTIC: UI must change within 3s (not waiting for server)
- extendedWaitUntil:
    visible:
      id: "undo-button"
    timeout: 3000

# Verify derived UI state
- extendedWaitUntil:
    visible:
      id: "user-indicator"
    timeout: 5000
ActionExpected ChangeTimeout
Mutation triggerButton state flips< 3s
List updateItem appears/disappears< 5s
Re-do actionProves persistence< 3s

5. Dismissing Native Alerts

React Native Alert.alert() creates native dialogs that block the UI:

- tapOn:
    id: "action-button"

# Wait for expected state change first
- extendedWaitUntil:
    visible:
      id: "new-state-element"
    timeout: 5000

# Dismiss alert (optional in case it already closed)
- tapOn:
    text: "OK"
    optional: true

# Brief delay for alert animation
- swipe:
    direction: DOWN
    duration: 300

6. Sub-Flows for Reusability

Break repeated sequences into sub-flow files:

.maestro/
├── flows/
│   ├── auth-and-return.yaml
│   ├── complete-purchase.yaml
│   └── verify-result.yaml
├── smoke-test.yaml
└── feature-test.yaml
# In main test
- runFlow:
    file: flows/auth-and-return.yaml

7. Deep Links (Expo)

Use the Expo scheme from app.json, not the bundle ID:

# WRONG
- openLink: "com.myapp://profile/settings"

# CORRECT
- openLink: "myapp://profile/settings"

Deep links must be registered in your app's deep link handler. Unregistered routes silently fail.

8. Platform-Specific Logic

- runFlow:
    when:
      platform: ios
    file: flows/ios-specific.yaml

- runFlow:
    when:
      platform: android
    file: flows/android-specific.yaml

9. Environment Variables

appId: com.myapp
env:
  TEST_EMAIL: maestro-test@example.com
  API_BASE_URL: http://localhost:3000
---
- inputText: ${TEST_EMAIL}

10. Selector State Properties

Use enabled, selected, checked, and focused to target elements by their current state. This is useful for validating interactive element states before or after actions.

# Only tap the submit button if it's enabled
- tapOn:
    id: "submit-button"
    enabled: true

# Assert a checkbox is checked
- assertVisible:
    id: "terms-checkbox"
    checked: true

# Wait for an input to be focused
- extendedWaitUntil:
    visible:
      id: "email-input"
      focused: true
    timeout: 3000
PropertyValuesUse Case
enabledtrue / falseButtons that disable during submission or until form is valid
checkedtrue / falseCheckboxes, toggle switches
selectedtrue / falseTab items, segmented controls
focusedtrue / falseInput fields with auto-focus

11. Relative Position Selectors

Distinguish between similar elements by their spatial relationship to other elements. This is more idiomatic and resilient than index-based selection.

# BAD — fragile, breaks if order changes
- tapOn:
    text: "Add to Basket"
    index: 1

# GOOD — contextual, self-documenting
- tapOn:
    text: "Add to Basket"
    below:
      text: "Awesome Shoes"

Available relative selectors:

# Target element below another
- tapOn:
    text: "Buy Now"
    below: "Product Title"

# Target element that is a child of a parent
- tapOn:
    text: "Delete"
    childOf:
      id: "item-card-42"

# Target a parent that contains a specific child
- tapOn:
    containsChild: "Urgent"

# Target by multiple descendants
- tapOn:
    containsDescendants:
      - id: title_id
        text: "Specific Title"
      - "Another descendant text"

# Horizontal positioning
- tapOn:
    text: "Edit"
    rightOf: "Username"
SelectorMeaning
below:Element is positioned below the referenced element

Use this skill in a project

Open any RiverX project, search the Skills panel for tovimx/maestro-mobile-testing-skill/maestro-mobile-testing, and install it in one click.

Use this skill