maestro
Maestro — declarative E2E mobile UI testing framework by mobile.dev. YAML-based flow files, single tool for Android + iOS (and Compose Multiplatform / Flutter / React Native). Built-in cloud runner, recording mode, JS scripting for complex assertions, screen state diffing, no flakiness from explicit waits. USE WHEN: user mentions "Maestro", "maestro test", "mobile E2E", "cross-platform UI test", "maestro studio", "mobile.dev cloud", ".maestro" folder, "launchApp" YAML DO NOT USE FOR: web E2E - use `testing/playwright` DO NOT USE FOR: unit tests - use `testing/kotest`, `testing/vitest`, etc. DO NOT USE FOR: instrumented Android tests - use Espresso/Compose Test DO NOT USE FOR: snapshot tests - use `testing/compose-snapshot`
获取
https://deepseekmodel.com/api/download.php?id=claude-dev-suite-claude-dev-suite-skills-testing-maestro-skill-md&format=skill
name maestro description Maestro — declarative E2E mobile UI testing framework by mobile.dev. YAML-based flow files, single tool for Android + iOS (and Compose Multiplatform / Flutter / React Native). Built-in cloud runner, recording mode, JS scripting for complex assertions, screen state diffing, no flakiness from explicit waits. USE WHEN: user mentions "Maestro", "maestro test", "mobile E2E", "cross-platform UI test", "maestro studio", "mobile.dev cloud", ".maestro" folder, "launchApp" YAML DO NOT USE FOR: web E2E - use `testing/playwright` DO NOT USE FOR: unit tests - use `testing/kotest`, `testing/vitest`, etc. DO NOT USE FOR: instrumented Android tests - use Espresso/Compose Test DO NOT USE FOR: snapshot tests - use `testing/compose-snapshot` allowed-tools Read, Grep, Glob, Write, Edit Maestro — E2E Mobile Testing Deep Knowledge : Use mcp__documentation__fetch_docs with technology: maestro . Why Maestro Feature Maestro Espresso/XCUITest Detox/Appium Cross-platform (Android + iOS) ✅ Single test ❌ Separate per platform ✅ Test format YAML (declarative) Kotlin / Swift JS Implicit waits / retry ✅ Built-in ❌ Manual waitFor Partial Recording mode ✅ Maestro Studio ❌ ❌ Cloud runner ✅ Free tier on mobile.dev — Sauce Labs / BrowserStack Compose / Flutter / RN ✅ All Espresso for Compose / XCUITest Detox: RN only Setup time < 5 min hours 30+ min Flakiness Low (smart waits) High (timing) Medium Install # macOS / Linux curl -Ls "https://get.maestro.mobile.dev" | bash # adds to ~/.maestro/bin # Or via brew brew tap mobile-dev-inc/tap brew install maestro # Windows # Use WSL2 or Docker; native Windows support limited # Verify maestro --version For iOS testing, also install: brew install facebook/fb/idb-companion Project Layout project-root/ ├── .maestro/ │ ├── flows/ │ │ ├── onboarding.yaml │ │ ├── send_bitcoin.yaml │ │ ├── receive_bitcoin.yaml │ │ └── settings.yaml │ ├── helpers/ │ │ └── common.yaml # reusable subFlow │ └── config.yaml # global config ├── apps/ │ ├── android/ # APK builds │ └── ios/ # IPA builds └── ... First Flow .maestro/flows/onboarding.yaml : appId: com.bhodl.android --- - launchApp: clearState: true # fresh state every run - assertVisible: "Welcome to BHODL" - tapOn: "Get Started" - assertVisible: "Create Wallet" - tapOn: "Create new wallet" - assertVisible: text: "Backup your seed" timeout: 5000 - tapOn: id: "btn_continue" - inputText: "my-secure-passphrase" - tapOn: "Confirm" - assertVisible: "Wallet created" Run: maestro test .maestro/flows/onboarding.yaml # Run all flows in folder maestro test .maestro/flows/ # With env var substitution maestro test -e API_BASE=https://staging.bhodl.app .maestro/flows/ Cross-Platform appId Same flow, different bundle IDs: appId: ${APP_ID} # set via env or config --- - launchApp APP_ID=com.bhodl.android maestro test flow.yaml APP_ID=com.bhodl.ios.BHODL maestro test flow.yaml Or config.yaml : appId: com.bhodl flows: - flows/*.yaml Selectors Maestro finds elements by text, id, accessibility label, or content description. Composable rules. - tapOn: "Send" # exact text match - tapOn: text: "Send" # explicit text matcher - tapOn: id: "send_button" # by accessibility id (Android: contentDescription, iOS: accessibilityIdentifier) - tapOn: text: "Send" index: 0 # if multiple matches - tapOn: text: ".*coin.*" # regex - tapOn: point: "50%, 50%" # screen coordinates - tapOn: below: "Recipient" # spatial relations - tapOn: leftOf: "Cancel" - tapOn: enabled: true # filter by state text: "Continue" For Compose/SwiftUI testability: // Compose Button( onClick = { /* ... */ }, modifier = Modifier.testTag( "send_button" ), // accessible to Maestro as id ) { Text( "Send" ) } // SwiftUI Button( "Send" ) { /* ... */ } .accessibilityIdentifier( "send_button" ) Common Actions - launchApp: clearState: true # fresh app state clearKeychain: true # iOS keychain wipe arguments: debug: true permissions: camera: allow # auto-grant on launch (iOS 14+, Android) location: deny - tapOn: "Button" - doubleTapOn: "Item" - longPressOn: "Item" - swipe: from: "30%, 50%" to: "70%, 50%" - swipe: direction: UP - scroll # default scroll - scrollUntilVisible: element: "End of list" direction: DOWN - inputText: "hello" - copyTextFrom: "Address field" # to clipboard - pasteText # from clipboard (iOS only) - eraseText: 10 # delete N chars - hideKeyboard - pressKey: BACK # Android back button - pressKey: ENTER - openLink: "bitcoin:bc1q..." # deep link - openBrowser: "https://example.com" - back - waitForAnimationToEnd: timeout: 5000 - takeScreenshot: "after_send" - assertVisible: "Sent successfully" - assertNotVisible: "Error" - assertTrue: "${output.success == true}" SubFlows (Reusable) helpers/login.yaml : appId: com.bhodl.android --- - inputText: ${USERNAME} - tapOn: id: "password_field" - inputText: ${PASSWORD} - tapOn: "Login" Use: - runFlow: file: ../helpers/login.yaml env: USERNAME: alice@example.com PASSWORD: secret - assertVisible: "Welcome, Alice" Conditionals & Loops - runFlow: when: visible: "Permission required" commands: - tapOn: "Allow" - runFlow: when: notVisible: "Already onboarded" commands: - runFlow: ../helpers/onboarding.yaml - repeat: times: 3 commands: - tapOn: "Refresh" - waitForAnimationToEnd - repeat: while: visible: "Loading..." commands: - waitForAnimationToEnd: timeout: 1000 JavaScript Scripting For complex assertions or test data generation: - runScript: scripts/generate_address.js env: NETWORK: testnet - inputText: ${output.address} scripts/generate_address.js : output. address = generateAddress (env. NETWORK ); function generateAddress ( network ) { return network === "testnet" ? "tb1q..." : "bc1q..." ; } Or inline: - evalScript: ${output.balance = parseFloat(output.amount) * 100000000 } - assertTrue: ${output.balance > 0 } Tags & Filtering tags: - smoke - critical appId: com.bhodl.android --- - launchApp # Run only smoke tests maestro test .maestro/flows/ --include-tags smoke # Exclude slow tests maestro test .maestro/flows/ --exclude-tags slow Maestro Studio (Recording / Inspection) Interactive UI for crafting tests: maestro studio Opens browser at http://localhost:9999 . Connects to running emulator/device. You can: Inspect element tree Tap elements to generate YAML Record test session Try selectors live Export to YAML flow Use to bootstrap tests, then refine in code. Cloud Runner (mobile.dev) # Run tests on cloud devices maestro cloud --apiKey= $MAESTRO_API_KEY \ apps/android/app-debug.apk \ .maestro/flows/ # iOS maestro cloud --apiKey= $MAESTRO_API_KEY \ apps/ios/build/BHODL.app \ .maestro/flows/ Free tier: limited monthly minutes. Paid tier for parallel runs, more devices, screenshots/videos retention. CI Integration GitHub Actions # .github/workflows/e2e.yml name: Maestro E2E on: [ pull_request ] jobs: android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: { java-version: '17' , distribution: 'temurin' } - name: Build APK run: ./gradlew :apps:android:assembleDebug - name: Setup Maestro run: | curl -Ls "https://get.maestro.mobile.dev" | bash echo "$HOME/.maestro/bin" >> $GITHUB_PATH - uses: reactivecircus/android-emulator-runner@v2 with: api-level: 33 arch: x86_64 script: | adb install apps/android/app/build/outputs/apk/debug/app-debug.apk maestro test .maestro/flows/ ios: runs-on: macos-14 steps: - uses: actions/checkout@v4 - name: Build app for simulator run: | xcodebuild -scheme BHODL -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=iPhone 15' build - name: Setup Maestro run: | curl -Ls "https://get.maestro.mobile.dev" | bash echo "$HOME/.maestro/bin" >> $GITHUB_PATH brew install facebook/fb/idb-companion - run: maestro test .maestro/flows/ Cloud Mode (Simpler CI) - name: Run on Maestro Cloud run: | maestro cloud --apiKey=${{ secrets.MAESTRO_API_KEY }} \ apps/android/app-debug.apk \ .maestro/flows/ No emulator setup needed. Test Patterns for Wallet Apps Send transaction (mocked backend) appId: com.bhodl.android --- - launchApp: clearState: true - runFlow: ../helpers/restore_test_wallet.yaml - tapOn: "Send" - inputText: "tb1q...test_address" - tapOn: id: "amount_field" - inputText: "1000" - tapOn: "Continue" - assertVisible: "Confirm send" - assertVisible: "1000 sats" - assertVisible: text: "tb1q.*" - tapOn: "Confirm" - tapOn: "Authenticate" # biometric prompt — needs special handling # Biometric: in regtest/staging build, replace with bypass button - tapOn: "Use test biometric" - assertVisible: text: "Sent successfully" timeout: 30000 - takeScreenshot: "after_send" Backup & restore flow appId: com.bhodl.android --- - launchApp: clearState: true # Create wallet, get seed - tapOn: "Create new wallet" - copyTextFrom: id: "seed_phrase" - evalScript: ${output.seed = maestro.copiedText} - tapOn: "I've backed up" # Wipe and restore - launchApp: clearState: true - tapOn: "Restore wallet" - inputText: ${output.seed} - tapOn: "Restore" - assertVisible: "Wallet restored" - assertVisible: "Balance: 0 sats" Biometric / Permission Handling Maestro can grant permissions on launch: - launchApp: permissions: camera: allow location: deny notifications: allow For biometric, build a debug-only test mode in your app that accepts a fixed test PIN instead of real biometric — Maestro can't simulate Face/Touch ID prompts. // Android class BiometricAuthHelper { fun authenticate (callback: ( Boolean ) -> Unit ) { if (BuildConfig.DEBUG && System.getenv( "MAESTRO_TEST" ) == "1" ) { callback( true ) // bypass for E2E return } // real biometric prompt } } Performance & Best Practices Idempotent flows — clearState: true ensures predictable starting point Avoid timing-dependent waits — Maestro auto-retries; use assertVisible: { timeout: 10000 } only when needed Use id /accessibility over text where possible — survives copy changes and i18n Tag flows by speed/criticality — run smoke on every PR, full suite nightly Screenshot key states — takeScreenshot for visual diff in cloud Wallet apps : use a regtest/signet backend — no real money, predictable balances Anti-Patterns Anti-pattern Why it's bad Correct approach Hardcoded sleeps ( waitForAnimationToEnd: { timeout: 30000 } ) Slow + brittle Use assertVisible (auto-retries) Selecting by absolute coordinates Breaks across screen sizes Use text/id/spatial selectors Real biometric in CI Can't automate Debug bypass + Maestro test flag Flow that depends on previous flow's state Brittle clearState: true + helper subFlows Hardcoded test data (specific addresses) Breaks on env change Use env vars / setup helpers No screenshots on failure Hard to debug takeScreenshot at key checkpoints Running all flows on every PR Slow CI Tag and run subset on PR, full nightly
该技能未提供触发词。
| 字段 | 说明 |
|---|---|
| format | 格式标识(skill/v1) |
| skill_id | 技能唯一 ID |
| name | 技能名称 |
| version | 版本号 |
| description | 技能描述 |
| category | 所属分类(数组) |
| trigger_words | 触发词列表 |
| tags | 标签列表 |
| source | 来源标识 |
| source_url | 来源链接(本页地址) |
| exported_at | 导出时间(每次下载生成) |
| system_prompt | 系统提示词正文 |
| model_config | 模型参数:provider / model / temperature / max_tokens / top_p |
| examples | 示例 |
| install_guide | 各平台导入说明(Coze / Dify / Claude / 自定义框架) |