From reviw-plugin
Automates mobile app testing on iOS and Android using Maestro MCP: launches apps, interacts with UI elements, captures screenshots, runs flows, collects evidence. Use to verify implementations before completion.
How this skill is triggered — by the user, by Claude, or both
Slash command
/reviw-plugin:mobile-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
To test mobile applications, use **Maestro MCP** for UI automation and verification. Maestro provides a simple, reliable way to test mobile apps on both iOS and Android.
To test mobile applications, use Maestro MCP for UI automation and verification. Maestro provides a simple, reliable way to test mobile apps on both iOS and Android.
CRITICAL: Maestro MCP Installation Check
Before using any Maestro functionality, verify the MCP server is available:
# Check if Maestro MCP is configured
claude mcp list | grep maestro
If not installed, add it:
claude mcp add maestro -- npx @nicepkg/maestro-mcp@latest
After adding, restart Claude Code to activate the MCP server.
CRITICAL: Flow File Placement
maestro/flows/ directory at the project root.artifacts/ - that's for evidence only (screenshots, test logs)Task → Is Maestro MCP available?
│
├─ No → Install Maestro MCP
│ └─ claude mcp add maestro -- npx @nicepkg/maestro-mcp@latest
│ └─ Restart Claude Code
│ └─ Retry
│
└─ Yes → Is a device/simulator running?
├─ No → list_devices → start_device
│
└─ Yes → Is the app installed?
├─ No → Build & install the app first
│
└─ Yes → Reconnaissance-then-action:
1. launch_app
2. take_screenshot (understand current state)
3. inspect_view_hierarchy (find element IDs)
4. Interact (tap_on, input_text, etc.)
5. take_screenshot (verify result)
6. Collect evidence
| Tool | Description | Key Parameters |
|---|---|---|
list_devices | List available devices/simulators | - |
start_device | Start a device/simulator | deviceId, platform |
launch_app | Launch an app on the device | appId (bundle ID / package name) |
take_screenshot | Capture the current screen | - |
tap_on | Tap on a UI element | text, id, point |
input_text | Type text into focused field | text |
back | Press the back button | - |
stop_app | Stop a running app | appId |
run_flow | Execute a Maestro YAML flow | yaml (inline YAML content) |
run_flow_files | Execute flow files from disk | paths (file paths) |
check_flow_syntax | Validate flow YAML syntax | yaml or paths |
inspect_view_hierarchy | Get the current UI element tree | - |
query_docs | Search Maestro documentation | query |
cheat_sheet | Get quick reference for Maestro commands | - |
# maestro/flows/login.yaml
appId: com.example.myapp
---
- launchApp
- tapOn: "Email"
- inputText: "test@example.com"
- tapOn: "Password"
- inputText: "password123"
- tapOn: "Log In"
- assertVisible: "Welcome"
# maestro/flows/login-with-evidence.yaml
appId: com.example.myapp
---
- launchApp
- takeScreenshot: .artifacts/feature/images/01-login-screen
- tapOn: "Email"
- inputText: "test@example.com"
- tapOn: "Password"
- inputText: "password123"
- takeScreenshot: .artifacts/feature/images/02-credentials-filled
- tapOn: "Log In"
- assertVisible: "Welcome"
- takeScreenshot: .artifacts/feature/images/03-login-success
# maestro/flows/login-error.yaml
appId: com.example.myapp
---
- launchApp
- tapOn: "Email"
- inputText: "invalid@example.com"
- tapOn: "Password"
- inputText: "wrong"
- tapOn: "Log In"
- assertVisible: "Invalid credentials"
- takeScreenshot: .artifacts/feature/images/04-login-error
# maestro/flows/onboarding.yaml
appId: com.example.myapp
---
- launchApp
# Skip onboarding if already completed
- runFlow:
when:
visible: "Get Started"
commands:
- tapOn: "Get Started"
- tapOn: "Next"
- tapOn: "Next"
- tapOn: "Done"
- assertVisible: "Home"
# maestro/flows/feed-scroll.yaml
appId: com.example.myapp
---
- launchApp
- assertVisible: "Feed"
# Scroll down to find content
- scrollUntilVisible:
element: "Load More"
direction: DOWN
timeout: 10000
- tapOn: "Load More"
- assertVisible: "More Items"
1. list_devices → Identify available simulators/devices
2. start_device → Boot the target device
3. launch_app → Open the app under test
4. take_screenshot → Capture initial state
5. inspect_view_hierarchy → Map all UI elements and their IDs
Based on reconnaissance:
maestro/flows/check_flow_syntax1. run_flow / run_flow_files → Execute test flows
2. If failure:
a. take_screenshot → Capture failure state
b. inspect_view_hierarchy → Check element state
c. Fix the flow or report the bug
d. Re-run
3. If success:
a. take_screenshot → Capture success state
b. Proceed to evidence collection
FEATURE=${FEATURE:-feature}
mkdir -p .artifacts/$FEATURE/{images,videos}
# Run flows with evidence collection
# (Screenshots taken within flows land in .artifacts/)
# Copy any additional evidence
cp maestro/test-results/*.png .artifacts/$FEATURE/images/
Priority order: testID > accessibility label > text content > position
// Best: testID
<TouchableOpacity testID="login-button">
<Text>Log In</Text>
</TouchableOpacity>
// Maestro flow
- tapOn:
id: "login-button"
// Best: Key
ElevatedButton(
key: const Key('login-button'),
onPressed: _login,
child: const Text('Log In'),
)
// Also: Semantics
Semantics(
identifier: 'login-button',
child: ElevatedButton(...),
)
// Maestro flow
- tapOn:
id: "login-button"
// Best: accessibilityIdentifier
Button("Log In") {
login()
}
.accessibilityIdentifier("login-button")
// Maestro flow
- tapOn:
id: "login-button"
// Best: testTag
Button(
onClick = { login() },
modifier = Modifier.testTag("login-button")
) {
Text("Log In")
}
// Maestro flow
- tapOn:
id: "login-button"
| Priority | Selector | Example | Reliability |
|---|---|---|---|
| 1 | id (testID) | id: "login-button" | Highest - stable across UI changes |
| 2 | text (exact) | text: "Log In" | High - breaks on text changes |
| 3 | text (regex) | text: "Log.*" | Medium - more flexible |
| 4 | point (x, y) | point: "50%,80%" | Low - breaks on layout changes |
project/
├── maestro/ # Maestro test assets (permanent)
│ └── flows/
│ ├── login.yaml
│ ├── login-error.yaml
│ ├── checkout.yaml
│ └── onboarding.yaml
├── .artifacts/ # Evidence only (temporary)
│ └── <feature>/
│ ├── images/ # Screenshots
│ ├── videos/ # Recorded videos
│ └── REPORT.md # Review report
└── src/ # Application source
Key distinction:
maestro/flows/ = Permanent test flow files (committed to repo).artifacts/ = Temporary evidence for PR review (gitignored or LFS)Don't use point (x, y coordinates) as primary selectors
Do use id (testID/accessibilityIdentifier) for reliable element selection
Don't place flow files in .artifacts/
Do place flows in maestro/flows/ as permanent project assets
Don't skip the reconnaissance step
Do always inspect_view_hierarchy before writing selectors
Don't hardcode wait times
Do use assertVisible which has built-in retry/timeout
Don't test only the happy path
Do test error states, empty states, edge cases
Don't forget to add testID/accessibilityIdentifier to new UI elements
Do add testability attributes during implementation, not as an afterthought
### Mobile Test Results
| Flow | Platform | Status | Duration | Evidence |
|------|----------|--------|----------|----------|
| Login (happy path) | iOS 17 | Pass | 4.2s |  |
| Login (error) | iOS 17 | Pass | 3.1s |  |
| Checkout | iOS 17 | Pass | 8.5s |  |
| **Total** | - | **3/3 Pass** | **15.8s** | - |
### Screenshots
| Step 1: Login | Step 2: Credentials | Step 3: Dashboard |
|--------------|--------------------|--------------------|
|  |  |  |
<details>
<summary>Flow execution logs</summary>
\`\`\`bash
# Flow: login.yaml
Running on iPhone 15 Pro (iOS 17.2)
- launchApp: com.example.myapp ... OK
- tapOn: "Email" ... OK (0.3s)
- inputText: "test@example.com" ... OK (0.1s)
- tapOn: "Password" ... OK (0.2s)
- inputText: "password123" ... OK (0.1s)
- tapOn: "Log In" ... OK (0.5s)
- assertVisible: "Welcome" ... OK (1.2s)
Flow completed successfully in 4.2s
\`\`\`
</details>
login-happy-path.yaml, not test1.yamlinspect_view_hierarchy - Always discover elements before writing selectorscheck_flow_syntax to catch YAML errorsnpx claudepluginhub joshuarweaver/cascade-code-general-misc-1 --plugin kazuph-reviwAutomates mobile app testing on iOS simulators/Android emulators using Appium, Detox, XCUITest, Espresso, Maestro. Validates UI interactions, gestures, navigation, and platform behaviors.
Verifies UI changes visually for mobile (Maestro), web (Playwright), and macOS (Peekaboo). Includes availability probes, path selection, screenshot capture, and dev-server preflight.
Executes native UI automation on mobile devices via DSL batch scripts: tap/type/swipe elements, launch apps, verify screens, save screenshots using accessibility tree predicates. For testing apps and device interactions.