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
By tovimx · 989 installs
npx skills add tovimx/maestro-mobile-testing-skill --skill maestro-mobile-testing
Source repository · Upstream listing
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
Minimal test
Run
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.
Context Recommended Selector Rationale
Multi language / i18n id: (testID) Stable across translations
Single language Text labels Human readable, self documenting tests
Agent maintained tests Either — ask the developer Readability matters less for AI maintained flows
System dialogs Text (always) No testID possible on native alerts
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:
testID Naming Convention
When using ID based selectors:
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:
Then in every test:
3. Adaptive Tests (Handle Both Auth States)
Tests should work regardless of whether the user is authenticated:
4. Testing Optimistic Updates
Use short timeouts to verify UI changes happen before server response:
Action Expected Change Timeout
Mutation trigger Button state flips < 3s
List update Item appears/disappears < 5s
Re do action Proves persistence < 3s
5. Dismissing Native Alerts
React Native Alert.alert() creates native dialogs that block the UI:
6. Sub Flows for Reusability
Break repeated sequences into sub flow files:
7. Deep Links (Expo)
Use the Expo scheme from app.json , not the bundle ID:
Deep links must be registered in your app's deep link handler. Unregistered routes silently fail.
8. Platform Specific Logic
9. Environment Variables
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.
Property Values Use Case
enabled true / false Buttons that disable during submission or until form is valid
checked true / false Checkboxes, toggle switches
selected true / false Tab items, segmented controls
focused true / false Input 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.
Available relative selectors:
Selector Meaning
below: Element is positioned below the referenced element
above: Element is positioned above the referenced element
leftOf: Element is to the left of the referenced element
rightOf: Element is to the right of the referenced element
childOf: Element is a direct child of the referenced parent
containsChild: Element contains a direct child matching the reference
containsDescendants: Element contains all specified descendant elements
Authentication Testing
Architecture
Testing OTP or magic link authentication in E2E requires capturing emails programmatically. The general pattern:
Common email capture services: [Mailpit](https://github.com/axllent/mailpit), [MailHog](https://github.com/mailhog/MailHog), [Ethereal](https://ethereal.email/).
OTP Fetch Script
Fetch OTP codes from your email capture service using Maestro's GraalJS runtime:
OTP Input Strategy
OTP components with auto focus need individual digit entry. Tap each input before typing:
For provider specific implementations (Supabase + Mailpit, Firebase Auth, Auth0), create a project level skill that extends this one.
GraalJS Script Rules
Maestro uses the GraalJS runtime. These constraints are non negotiable:
Feature Status
async/await NOT supported
fetch() NOT supported
http.get() , http.post() Use these instead
json() Use to parse response bodies
output.VAR Set variables for use in YAML flow
var declarations Required (use var , not const / let for safety)
Critical Gotchas
clearState Does NOT Clear iOS Keychain
clearState: true clears the app sandbox (UserDefaults, files, caches) but does NOT clear the iOS Keychain. Auth tokens stored via expo secure store (or any Keychain based storage) persist across clearState resets and even app reinstalls.
Rules:
Never rely on clearState to produce guest state on iOS
For auth tests: skip clearState , use auth loaded pre flight
For guest tests: use adaptive flows that handle both states
Never assert guest only UI after clearState
Note: On Android, clearState: true fully resets app data including credentials. This is an iOS only gotcha.
XCTest kAXErrorInvalidUIElement Crash (iOS)
The XCTest driver may crash if Maestro interacts with the accessibility tree before the first render cycle completes on cold boot.
Fix: Add a no op swipe immediately after launchApp :
API Server Dependency
Mobile apps calling backend APIs on localhost need either the full server or a mock server running. Without it, all API dependent screens show loading spinners or empty states (queries fail silently).
Fix: Start a mock API server before running Maestro tests:
Create a lightweight mock that returns canned JSON for each endpoint your app calls. This is faster and more deterministic than running your full backend.
Auth Aware Tab Bars
Tab bars that show different tabs for guest vs authenticated users will cause selector failures:
State Typical Tabs
Guest home, search, cart, profile
Auth home, feed, create, messages, profile
Only assert tabs that exist in both states, or use adaptive when: conditions.
Test File Template
Folder Structure
Naming Conventions
Type Pattern Example
Main test {feature} {action}.yaml checkout purchase.yaml
Sub flow {action} {context}.yaml auth and return to dashboard.yaml
Script {verb} {noun}.js fetch otp.js
Infrastructure
Mock Server for API Dependent Tests
Create a lightweight mock server that serves canned responses for your API layer. This is faster and more deterministic than running your full backend during E2E tests.
Orchestration Script
Automate the full E2E setup with a shell script that:
1. Starts backend services (database, auth)
2. Seeds test data
3. Starts mock API server
4. Runs Maestro tests
5. Cleans up all services
Seed Data
Tests that depend on specific data require seeded databases. Keep seed scripts alongside your test infrastructure and run them before each test suite.
Android Specific Patterns
Emulator Setup
Android tests require an emulator or a USB connected physical device. Maestro auto detects connected devices.
iOS vs Android Differences
Aspect iOS Android
Device type Simulator only (no physical) Emulator + physical via ADB
clearState Does NOT clear Keychain Fully resets app data
Cold boot crash XCTest kAXError (add swipe delay) No equivalent issue
Performance Runs natively (fast) ARM emulation (slower on x86)
Permission dialogs System alerts System dialogs with different text
ADB Debugging
Android Permission Handling
Android permissions appear as system dialogs. Dismiss with optional taps:
CI/CD Integration
GitHub Actions with Maestro Cloud
Maestro Cloud provides real devices in CI without local simulators. Use the official action:
Tag Based Flow Filtering
Use tags to control which tests run in CI vs locally:
Local CI with Docker (Android Only)
Note: iOS tests cannot run in Docker (requires macOS). Use Maestro Cloud for iOS in CI.
Maestro Cloud
[Maestro Cloud](https://cloud.maestro.dev/) runs tests on real devices without local simulator setup.
Setup
1. Create account at cloud.maestro.dev
2. Generate API key from dashboard
3. Store as MAESTRO API KEY secret in your CI provider
Running from CLI
Key Points
iOS testing : Supported on Maestro Cloud (not on local physical devices)
Android testing : Both local physical devices and Maestro Cloud
Results : Dashboard with video recordings, logs, and screenshots
CI outputs : MAESTRO CLOUD CONSOLE URL , MAESTRO CLOUD FLOW RESULTS
Maestro MCP Server
The [Maestro MCP server](https://docs.maestro.dev/getting started/maestro mcp) exposes Maestro's full command set as Model Context Protocol tools, letting AI agents execute tests and interact with devices directly — not just write YAML.
How It Complements This Skill
This Skill Maestro MCP
Role Teaches correct patterns Provides runtime execution
Layer Authoring (write good YAML) Execution (run, tap, assert, screenshot)
Output Better test files Live device interaction
Use both together: this skill ensures the AI writes correct tests; the MCP lets it run them immediately, see failures, and iterate.
Setup
The MCP ships with the Maestro CLI — no extra install needed:
Claude Code — add to project .mcp.json or global settings:
Claude Desktop — add to ~/Library/Application Support/Claude/claude desktop config.json (macOS):
Also supported on Cursor, Windsurf, VS Code, and JetBrains IDEs. See the [Maestro MCP docs](https://docs.maestro.dev/getting started/maestro mcp) for IDE specific setup.
Key Capabilities
The MCP server exposes 47 tools organized by category:
Category Tools Examples
UI Interaction tap, swipe, scroll, long press tapOn , scrollUntilVisible
Text Input type, erase, paste, copy inputText , eraseText
Assertions visibility, AI powered assertVisible , assertWithAI
App Lifecycle launch, stop, clear state launchApp , clearState
Device Control location, orientation, airplane setLocation , hideKeyboard
Flow Control run flows, repeat, eval scripts runFlow , evalScript
Media screenshots, recording takeScreenshot , startRecording
AI Powered visual assertions, defect detection assertWithAI , assertNoDefectsWithAI
Write Run Fix Loop
With both this skill and the MCP active, the AI can:
1. Write a test YAML using patterns from this skill
2. Run it via MCP's runFlow / launchApp + interaction tools
3. See failures via screenshots and assertions
4. Fix the YAML and re run — all in one conversation
Debugging
Screenshots saved to ~/.maestro/tests/{timestamp}/ .
Checklist for New Tests
Common Errors
Error Cause Fix
"Unable to locate Jav