seed-skills/maestro-mobile/SKILL.md
Black-box mobile UI testing with Maestro YAML flows - launchApp, tapOn, inputText, assertVisible, reusable runFlow subflows, environment parameters, JUnit reporting, and emulator-based CI pipelines.
npx skillsauth add PramodDutta/qaskills Maestro Mobile TestingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill makes an AI agent write Maestro flows - declarative YAML files that drive iOS and Android apps black-box style with built-in waiting, no test harness compiled into the app, and no WebDriver session management. Trigger it when a repository contains a .maestro/ directory or *.yaml flows with appId: headers, or when the user wants fast, low-maintenance smoke tests for a native, React Native, or Flutter app without the weight of Appium.
tapOn and assertVisible polls until the element appears or a timeout expires. Reach for extendedWaitUntil for slow screens; never add fixed delays as a first resort.checkout-with-saved-card.yaml that does exactly that is debuggable. A 200-line regression.yaml is not. Compose bigger journeys from subflows with runFlow.tapOn: id: "submit-button" survives copy changes and localization; text selectors are acceptable for stable, single-language labels only.launchApp with clearState: true resets the app between flows; tests that inherit the previous flow's logged-in session fail in random orders.env: blocks or -e KEY=value CLI arguments so the same flow runs against dev, staging, and CI builds.# Install the Maestro CLI
curl -fsSL "https://get.maestro.mobile.dev" | bash
export PATH="$PATH:$HOME/.maestro/bin"
maestro --version
# Run one flow against the booted simulator/emulator
maestro test .maestro/login.yaml
# Run the whole suite with JUnit output for CI
maestro test .maestro/ --format junit --output maestro-report.xml
# Interactive selector explorer while authoring
maestro studio
# .maestro/login.yaml
appId: com.example.shop
env:
EMAIL: [email protected]
PASSWORD: Str0ngPass!
---
- launchApp:
clearState: true
- tapOn: 'Log in'
- tapOn:
id: 'email-input'
- inputText: ${EMAIL}
- tapOn:
id: 'password-input'
- inputText: ${PASSWORD}
- hideKeyboard
- tapOn:
id: 'login-submit'
- assertVisible: 'Welcome back'
- assertNotVisible: 'Invalid email or password'
- takeScreenshot: logged-in-home
# .maestro/subflows/login.yaml -- reusable building block
appId: com.example.shop
---
- launchApp:
clearState: true
- tapOn: 'Log in'
- tapOn:
id: 'email-input'
- inputText: ${EMAIL}
- tapOn:
id: 'password-input'
- inputText: ${PASSWORD}
- tapOn:
id: 'login-submit'
- assertVisible:
id: 'home-screen'
# .maestro/checkout.yaml -- the journey composes the subflow
appId: com.example.shop
env:
EMAIL: [email protected]
PASSWORD: Str0ngPass!
---
- runFlow: subflows/login.yaml
# Dismiss the push-permission prompt only if it appears
- runFlow:
when:
visible: 'Allow notifications?'
commands:
- tapOn: 'Allow'
- tapOn: 'Deals'
- scrollUntilVisible:
element:
text: 'Wireless Headphones'
direction: DOWN
timeout: 20000
- tapOn: 'Wireless Headphones'
- tapOn:
id: 'add-to-cart'
- tapOn:
id: 'cart-icon'
- assertVisible: 'Wireless Headphones'
- assertVisible:
text: 'Checkout'
enabled: true
# .maestro/order-status.yaml
appId: com.example.shop
---
- launchApp
- tapOn:
id: 'tab-orders'
# Wait up to 15s for async content instead of a blind sleep
- extendedWaitUntil:
visible:
id: 'orders-list'
timeout: 15000
# Pull-to-refresh until the order flips to Shipped, max 5 tries
- repeat:
while:
notVisible: 'Shipped'
times: 5
commands:
- swipe:
direction: DOWN
duration: 400
- assertVisible:
text: 'Shipped'
- copyTextFrom:
id: 'order-number'
- assertTrue: ${maestro.copiedText != ''}
# .github/workflows/maestro-android.yml
name: maestro-android
on: [pull_request]
jobs:
flows:
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Build debug APK
run: ./gradlew assembleDebug
- name: Install Maestro
run: |
curl -fsSL "https://get.maestro.mobile.dev" | bash
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
- name: Run flows on emulator
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
arch: x86_64
profile: pixel_7
script: |
adb install app/build/outputs/apk/debug/app-debug.apk
maestro test .maestro/ --format junit --output maestro-report.xml \
-e [email protected] -e PASSWORD="${{ secrets.TEST_USER_PASSWORD }}"
- name: Publish report
if: always()
uses: actions/upload-artifact@v4
with:
name: maestro-report
path: |
maestro-report.xml
~/.maestro/tests
For hosted device execution, the same suite uploads unchanged: maestro cloud --api-key "$MAESTRO_CLOUD_API_KEY" app-debug.apk .maestro/ runs every flow on cloud devices and returns a pass/fail exit code CI can gate on.
maestro studio to discover ids and text before writing selectors by hand; it shows exactly what the accessibility tree exposes.testID in React Native, accessibilityIdentifier on iOS, contentDescription/resource-id on Android) as part of feature work.subflows/ and one top-level flow per journey; flows read top to bottom like a manual test script, keep them that readable.tags: [smoke] and run maestro test --include-tags smoke on every PR, the full suite nightly.takeScreenshot at journey milestones; screenshots plus the JUnit report make CI failures diagnosable without a local repro.- waitForAnimationToEnd and long extendedWaitUntil timeouts everywhere to paper over an app that never settles; fix the spinner, not the test.tapOn: point: 50%,30% or index-based taps) for elements that have ids; coordinate taps break on every screen size.env: plus CI secrets..maestro/ directory, YAML files starting with appId:, or maestro commands in CI workflows.development
Generate test data from the schemas you already have. Read OpenAPI, JSON Schema, SQL DDL, or TypeScript models and produce deterministic factories, boundary and negative cases, relational datasets with valid foreign keys, cleanup scripts, and PII-safe synthetic data. Production records never leave the machine.
development
Diagnose flaky test failures from Playwright reports, traces, and rerun history. Classify each failure as product, test, environment, data, or unknown with cited evidence and a proposed fix. Never auto-modifies code without opt-in.
tools
Test LLM, RAG, MCP, and agentic systems end to end. Build golden datasets, run deterministic checks and LLM judges, score retrieval, probe prompt injection, verify tool use, and gate CI on thresholds. Orchestrates DeepEval, Ragas, promptfoo, and Langfuse.
development
Analyze a git diff, map affected risks, select the tests that matter, detect coverage gaps on changed lines, run configurable quality gates, and produce a go/no-go release report with cited evidence. Recommends only; never merges or deploys.