mobile-testing-maestro
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMaestro Mobile UI Testing Patterns
Maestro移动端UI测试模式
Quick Guide: Write E2E tests as declarative YAML flows. Useselectors for stable element targeting (not text that changes with localization). Useidto compose reusable subflows (login, setup). UserunFlowbefore assertions on animated screens. UsewaitForAnimationToEnd/onFlowStarthooks for setup/teardown. Maestro auto-retries assertions for up to 7 seconds before failing. Current stable: CLI 2.4.0.onFlowComplete
<critical_requirements>
**快速指南:**将E2E测试编写为声明式YAML流程。使用选择器实现稳定的元素定位(不要使用随本地化变化的文本)。使用id组合可复用的子流程(登录、设置)。在对带动画的屏幕进行断言前使用runFlow。使用waitForAnimationToEnd/onFlowStart钩子进行初始化/清理操作。Maestro在断言失败前会自动重试最多7秒。当前稳定版本:CLI 2.4.0。onFlowComplete
<critical_requirements>
CRITICAL: Before Using This Skill
重要提示:使用此技能之前
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST use selectors (accessibility identifiers) as primary selectors - text selectors break with localization or copy changes)
id(You MUST use for reusable sequences (login, onboarding) - NEVER duplicate steps across flow files)
runFlow(You MUST use before assertions on screens with animations or transitions - assertions on animated elements are flaky)
waitForAnimationToEnd(You MUST pair every with a - unpaired commands produce corrupted or missing video files)
startRecordingstopRecording(You MUST use environment variables or blocks for credentials and environment-specific values - NEVER hardcode secrets in YAML flows)
env</critical_requirements>
Auto-detection: Maestro, maestro, .maestro, maestro test, maestro cloud, maestro studio, launchApp, tapOn, assertVisible, assertNotVisible, inputText, scrollUntilVisible, runFlow, evalScript, runScript, swipe, hideKeyboard, waitForAnimationToEnd, onFlowStart, onFlowComplete, maestro.yaml, config.yaml tags
When to use:
- Writing E2E UI tests for iOS and Android mobile apps
- Automating user workflows (login, checkout, onboarding) with YAML flows
- Testing cross-platform behavior from a single flow file
- Running mobile tests in CI with Maestro Cloud
- Recording test execution for debugging or documentation
- Testing deep links, location, permissions, and device interactions
When NOT to use:
- Unit testing business logic (use your unit test framework)
- API-only testing without UI (use direct HTTP tests)
- Testing web-only applications without mobile component
- Performance profiling or load testing (Maestro is for functional UI flows)
Key patterns covered:
- Flow structure with appId, YAML commands, and selectors
- Selector strategies: id (preferred), text, point, relational, state
- Flow control: runFlow, repeat, retry, conditions (when), hooks
- Environment variables and parameterized flows
- JavaScript expressions: inline , evalScript, runScript, output object
${} - Device interactions: swipe, scroll, setLocation, openLink, permissions
- Workspace configuration: tags, test discovery, execution order
- Maestro Studio for visual flow creation and element inspection
- Maestro Cloud for CI integration with GitHub Actions
Detailed Resources:
- examples/core.md - Flow structure, selectors, assertions, input, navigation
- examples/flow-control.md - runFlow, repeat, retry, conditions, hooks, JavaScript
- examples/device-interactions.md - Swipe, scroll, location, links, permissions, recording
- reference.md - Command reference, CLI commands, workspace config, decision frameworks
<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入顺序、、命名常量)import type
(必须使用选择器(无障碍标识符)作为主要选择器 - 文本选择器会因本地化或文案变更失效)
id(必须使用处理可复用流程(登录、引导页) - 绝对不要在多个流程文件中重复步骤)
runFlow(必须在对带有动画或过渡效果的屏幕进行断言前使用 - 对动画元素的断言不稳定)
waitForAnimationToEnd(必须为每个配对 - 未配对的命令会生成损坏或缺失的视频文件)
startRecordingstopRecording(必须使用环境变量或块存储凭据和环境特定值 - 绝对不要在YAML流程中硬编码密钥)
env</critical_requirements>
自动检测标识: Maestro, maestro, .maestro, maestro test, maestro cloud, maestro studio, launchApp, tapOn, assertVisible, assertNotVisible, inputText, scrollUntilVisible, runFlow, evalScript, runScript, swipe, hideKeyboard, waitForAnimationToEnd, onFlowStart, onFlowComplete, maestro.yaml, config.yaml tags
适用场景:
- 为iOS和Android移动应用编写E2E UI测试
- 使用YAML流程自动化用户工作流(登录、结账、引导页)
- 通过单个流程文件测试跨平台行为
- 在CI中使用Maestro Cloud运行移动端测试
- 录制测试执行过程用于调试或文档记录
- 测试深度链接、位置权限、设备交互等功能
不适用场景:
- 业务逻辑的单元测试(使用专用单元测试框架)
- 无UI的纯API测试(使用直接HTTP测试)
- 无移动端组件的纯Web应用测试
- 性能分析或负载测试(Maestro适用于功能性UI流程)
涵盖的核心模式:
- 包含appId、YAML命令和选择器的流程结构
- 选择器策略:id(首选)、text、point、关联选择器、状态选择器
- 流程控制:runFlow、repeat、retry、条件(when)、钩子
- 环境变量和参数化流程
- JavaScript表达式:内联、evalScript、runScript、output对象
${} - 设备交互:swipe、scroll、setLocation、openLink、权限设置
- 工作区配置:标签、测试发现、执行顺序
- 用于可视化流程创建和元素检查的Maestro Studio
- 与GitHub Actions集成的Maestro Cloud CI
详细资源:
- examples/core.md - 流程结构、选择器、断言、输入、导航
- examples/flow-control.md - runFlow、repeat、retry、条件、钩子、JavaScript
- examples/device-interactions.md - 滑动、滚动、位置、链接、权限、录制
- reference.md - 命令参考、CLI命令、工作区配置、决策框架
<philosophy>
Philosophy
设计理念
Maestro takes a fundamentally different approach from code-based testing frameworks: tests are declarative YAML, not imperative code. This makes flows readable by anyone on the team, not just developers. The framework handles the hard parts of mobile testing automatically -- waiting for elements, retrying taps, tolerating animation delays -- so flows focus on what to test, not how to wait.
Core principles:
- Declarative over imperative - YAML flows describe user intent, not implementation details
- Built-in tolerance - Maestro auto-waits up to 7 seconds for elements, auto-retries taps, and handles animation delays without explicit waits
- Single flow, multiple platforms - One YAML file can test both iOS and Android with platform conditions for differences
- Composition over duplication - Extract reusable sequences (login, setup, teardown) into subflows with
runFlow - Stable selectors - Use accessibility identifiers () over visible text to survive localization and copy changes
id
Mental model:
Maestro flows are recipes. Each step is an action a user would take. The framework handles timing, retries, and platform differences. You describe the journey, Maestro drives the car.
When to use Maestro:
- Smoke tests for critical user journeys (login, purchase, onboarding)
- Regression tests for flows that broke before
- Cross-platform verification with a single flow file
- Visual recording of test runs for stakeholder review
When NOT to use Maestro:
- Isolated unit tests for business logic
- API contract testing without UI
- Performance benchmarking or load testing
- Complex data-driven testing requiring heavy programmatic logic (Maestro's JS support is limited compared to full test frameworks)
<patterns>
Maestro与基于代码的测试框架采用完全不同的方法:测试是声明式YAML,而非命令式代码。这使得团队中的任何人都能读懂流程,而不仅仅是开发人员。框架自动处理移动端测试的难点——等待元素、重试点击、容忍动画延迟——因此流程专注于_测试什么_,而不是_如何等待_。
核心原则:
- 声明式优先于命令式 - YAML流程描述用户意图,而非实现细节
- 内置容错机制 - Maestro自动等待元素最多7秒,自动重试点击,无需显式等待即可处理动画延迟
- 单流程多平台 - 一个YAML文件可测试iOS和Android,通过平台条件处理差异
- 组合优先于重复 - 使用将可复用流程(登录、设置、清理)提取为子流程
runFlow - 稳定选择器 - 使用无障碍标识符()而非可见文本,以适应本地化和文案变更
id
思维模型:
Maestro流程就像食谱。每个步骤都是用户会执行的操作。框架处理时序、重试和平台差异。你描述流程,Maestro负责执行。
何时使用Maestro:
- 关键用户旅程的冒烟测试(登录、购买、引导页)
- 之前出现过问题的流程的回归测试
- 单个流程文件完成跨平台验证
- 录制测试运行过程供利益相关方查看
何时不使用Maestro:
- 业务逻辑的孤立单元测试
- 无UI的API契约测试
- 性能基准测试或负载测试
- 需要大量编程逻辑的复杂数据驱动测试(Maestro的JS支持相比完整测试框架有限)
<patterns>
Core Patterns
核心模式
Pattern 1: Flow Structure and Basic Commands
模式1:流程结构与基础命令
Every flow starts with a configuration block (appId, optional env/tags) separated from commands by . Commands execute sequentially top to bottom.
---yaml
appId: com.example.app
tags:
- smoke
- auth
---
- launchApp
- tapOn:
id: "email_input"
- inputText: "user@example.com"
- tapOn:
id: "password_input"
- inputText: "secure_password"
- tapOn:
id: "login_button"
- assertVisible:
id: "home_screen"Why good: appId identifies the app under test, tags enable filtering with --include-tags/--exclude-tags, id selectors are stable across localizations, sequential commands read like a user story
See examples/core.md for complete flow structure with env blocks, labels, and clearState.
每个流程都以配置块(appId、可选的env/tags)开头,通过与命令分隔。命令从上到下依次执行。
---yaml
appId: com.example.app
tags:
- smoke
- auth
---
- launchApp
- tapOn:
id: "email_input"
- inputText: "user@example.com"
- tapOn:
id: "password_input"
- inputText: "secure_password"
- tapOn:
id: "login_button"
- assertVisible:
id: "home_screen"优势: appId标识被测应用,标签支持通过--include-tags/--exclude-tags过滤测试,id选择器在本地化环境下保持稳定,顺序命令读起来像用户故事
查看examples/core.md了解包含env块、标签和clearState的完整流程结构。
Pattern 2: Selector Strategies
模式2:选择器策略
Use (accessibility identifier) as the primary selector. Fall back to for static labels, for coordinates only as last resort. Combine selectors for precision.
idtextpointyaml
undefined使用(无障碍标识符)作为主要选择器。对于静态标签可退而求其次使用,仅在万不得已时使用坐标选择器。可组合选择器以提高精度。
idtextpointyaml
undefinedPreferred: id selector (stable, language-independent)
首选:id选择器(稳定、独立于语言)
- tapOn: id: "submit_button"
- tapOn: id: "submit_button"
Fallback: text selector (breaks with i18n changes)
备选:text选择器(会因国际化变更失效)
- tapOn: text: "Submit"
- tapOn: text: "Submit"
Relational: below/above/childOf for disambiguation
关联选择器:通过below/above/childOf消除歧义
- tapOn: text: "Delete" below: "Shopping Cart"
- tapOn: text: "Delete" below: "Shopping Cart"
State selectors: filter by element state
状态选择器:按元素状态过滤
- tapOn: id: "toggle_switch" enabled: true
**Why good:** id selectors survive text changes, relational selectors disambiguate duplicate labels, state selectors prevent tapping disabled elements
See [examples/core.md](examples/core.md) for all selector types including index, point, and combined selectors.
---- tapOn: id: "toggle_switch" enabled: true
**优势:** id选择器不受文本变更影响,关联选择器可区分重复标签,状态选择器避免点击禁用元素
查看[examples/core.md](examples/core.md)了解所有选择器类型,包括index、point和组合选择器。
---Pattern 3: Reusable Subflows with runFlow
模式3:使用runFlow实现可复用子流程
Extract repeated sequences into separate flow files. Pass context via parameters. Use for clear test reports.
envlabelyaml
undefined将重复流程提取到单独的流程文件中。通过参数传递上下文。使用生成清晰的测试报告。
envlabelyaml
undefinedMain flow: checkout-test.yaml
主流程:checkout-test.yaml
appId: com.example.app
- runFlow: file: subflows/login.yaml env: USERNAME: "test_user@example.com" PASSWORD: "test_password" label: "Log in as test user"
- tapOn: id: "cart_icon"
- runFlow: file: subflows/complete-checkout.yaml label: "Complete purchase flow"
- assertVisible: id: "order_confirmation"
```yamlappId: com.example.app
- runFlow: file: subflows/login.yaml env: USERNAME: "test_user@example.com" PASSWORD: "test_password" label: "以测试用户身份登录"
- tapOn: id: "cart_icon"
- runFlow: file: subflows/complete-checkout.yaml label: "完成购买流程"
- assertVisible: id: "order_confirmation"
```yamlSubflow: subflows/login.yaml
子流程:subflows/login.yaml
appId: com.example.app
- tapOn: id: "email_input"
- inputText: ${USERNAME}
- tapOn: id: "password_input"
- inputText: ${PASSWORD}
- tapOn: id: "login_button"
**Why good:** login sequence defined once and reused across all flows, env parameters make subflows configurable, labels improve test report readability
See [examples/flow-control.md](examples/flow-control.md) for inline subflows, conditional flows, and nested composition.
---appId: com.example.app
- tapOn: id: "email_input"
- inputText: ${USERNAME}
- tapOn: id: "password_input"
- inputText: ${PASSWORD}
- tapOn: id: "login_button"
**优势:** 登录流程仅定义一次即可在所有流程中复用,env参数使子流程可配置,标签提升测试报告可读性
查看[examples/flow-control.md](examples/flow-control.md)了解内联子流程、条件流程和嵌套组合。
---Pattern 4: Conditions and Platform-Specific Logic
模式4:条件与平台特定逻辑
Use with , , , or JavaScript expressions to handle differences between iOS and Android or optional UI states.
whenplatformvisiblenotVisibletrueyaml
undefined使用结合、、或JavaScript 表达式处理iOS和Android之间的差异或可选UI状态。
whenplatformvisiblenotVisibletrueyaml
undefinedPlatform-specific permission handling
平台特定权限处理
-
runFlow: when: platform: Android commands: - tapOn: "Allow"
-
runFlow: when: platform: iOS commands: - tapOn: "Allow While Using App"
-
runFlow: when: platform: Android commands: - tapOn: "Allow"
-
runFlow: when: platform: iOS commands: - tapOn: "Allow While Using App"
Dismiss optional popup if visible
如果可见则关闭可选弹窗
- runFlow: when: visible: "Rate this app" commands: - tapOn: "Not now"
**Why good:** single flow handles both platforms, visibility conditions handle non-deterministic UI (popups, tooltips), no test failure on missing optional elements
See [examples/flow-control.md](examples/flow-control.md) for JavaScript conditions and combined conditions.
---- runFlow: when: visible: "Rate this app" commands: - tapOn: "Not now"
**优势:** 单个流程可处理两个平台,可见性条件可处理非确定性UI(弹窗、提示框),不会因缺失可选元素导致测试失败
查看[examples/flow-control.md](examples/flow-control.md)了解JavaScript条件和组合条件。
---Pattern 5: Environment Variables and Parameterized Flows
模式5:环境变量与参数化流程
Pass runtime values via CLI flags (), shell variables ( prefix), or blocks in flow files. Use syntax for interpolation with JavaScript fallback defaults.
-eMAESTRO_env${}yaml
appId: com.example.app
env:
BASE_URL: "https://staging.example.com"
DEFAULT_USER: "qa_user@example.com"
---
- launchApp
- tapOn:
id: "email_input"
- inputText: ${USERNAME || DEFAULT_USER}bash
undefined通过CLI标志()、shell变量(前缀)或流程文件中的块传递运行时值。使用语法进行插值,并支持JavaScript fallback默认值。
-eMAESTRO_env${}yaml
appId: com.example.app
env:
BASE_URL: "https://staging.example.com"
DEFAULT_USER: "qa_user@example.com"
---
- launchApp
- tapOn:
id: "email_input"
- inputText: ${USERNAME || DEFAULT_USER}bash
undefinedOverride from CLI
从CLI覆盖变量
maestro test -e USERNAME=admin@example.com -e PASSWORD=secret flow.yaml
**Why good:** secrets never hardcoded in flow files, env blocks provide defaults, CLI overrides enable multi-environment testing, `||` fallback prevents failures when variables are missing
See [examples/flow-control.md](examples/flow-control.md) for built-in variables, runScript with env, and shell variable patterns.
---maestro test -e USERNAME=admin@example.com -e PASSWORD=secret flow.yaml
**优势:** 密钥永远不会硬编码在流程文件中,env块提供默认值,CLI覆盖支持多环境测试,`||` fallback可防止变量缺失时失败
查看[examples/flow-control.md](examples/flow-control.md)了解内置变量、结合env的runScript和shell变量模式。
---Pattern 6: Hooks for Setup and Teardown
模式6:初始化与清理钩子
Use and in the configuration block for consistent setup/teardown across all flows. runs even if the flow fails.
onFlowStartonFlowCompleteonFlowCompleteyaml
appId: com.example.app
onFlowStart:
- clearState
- runFlow:
file: subflows/login.yaml
env:
USERNAME: "test_user@example.com"
PASSWORD: "test_password"
onFlowComplete:
- runFlow: subflows/cleanup.yaml
---
- tapOn:
id: "settings_icon"
- assertVisible:
id: "settings_screen"Why good: clearState ensures clean app state, login runs before every flow, cleanup always runs (even on failure), prevents test pollution between flows
Hook failure behavior: If fails, the main flow is skipped but still executes. If fails, the flow is marked as failed even if the main test passed.
onFlowStartonFlowCompleteonFlowCompleteSee examples/flow-control.md for hooks with environment variables and script-based teardown.
在配置块中使用和实现所有流程的统一初始化/清理。即使流程失败,也会执行。
onFlowStartonFlowCompleteonFlowCompleteyaml
appId: com.example.app
onFlowStart:
- clearState
- runFlow:
file: subflows/login.yaml
env:
USERNAME: "test_user@example.com"
PASSWORD: "test_password"
onFlowComplete:
- runFlow: subflows/cleanup.yaml
---
- tapOn:
id: "settings_icon"
- assertVisible:
id: "settings_screen"优势: clearState确保干净的应用状态,登录在每个流程前运行,清理始终执行(即使失败),防止流程间的测试污染
钩子失败行为: 如果失败,主流程会被跳过,但仍会执行。如果失败,即使主测试通过,流程也会被标记为失败。
onFlowStartonFlowCompleteonFlowComplete查看examples/flow-control.md了解结合环境变量的钩子和基于脚本的清理。
Pattern 7: JavaScript Expressions
模式7:JavaScript表达式
Use inline for simple interpolation, for variable computation, and for complex logic in external files. All share a global object.
${}evalScriptrunScript.jsoutputyaml
undefined使用内联进行简单插值,使用进行变量计算,使用处理外部文件中的复杂逻辑。所有方式共享全局对象。
${}evalScriptrunScript.jsoutputyaml
undefinedInline expression
内联表达式
- inputText: user_${Date.now()}@test.com
- inputText: user_${Date.now()}@test.com
evalScript for computation
evalScript计算
- evalScript: ${output.timestamp = Date.now()}
- inputText: ${output.timestamp}
- evalScript: ${output.timestamp = Date.now()}
- inputText: ${output.timestamp}
runScript for complex logic (external file)
runScript处理复杂逻辑(外部文件)
- runScript: scripts/generate-test-data.js
- inputText: ${output.generatedEmail}
**Why good:** inline expressions handle simple dynamic values, evalScript sets variables without UI interaction, runScript keeps complex logic in testable JS files, output object passes data between steps
See [examples/flow-control.md](examples/flow-control.md) for HTTP requests in scripts, DataFaker, and output namespacing.
</patterns>
---
<decision_framework>- runScript: scripts/generate-test-data.js
- inputText: ${output.generatedEmail}
**优势:** 内联表达式处理简单动态值,evalScript无需UI交互即可设置变量,runScript将复杂逻辑保存在可测试的JS文件中,output对象在步骤间传递数据
查看[examples/flow-control.md](examples/flow-control.md)了解脚本中的HTTP请求、DataFaker和output命名空间。
</patterns>
---
<decision_framework>Decision Framework
决策框架
Selector Choice
选择器选择
Can you add an accessibility identifier (testID/accessibilityIdentifier)?
|-- YES -> Use id selector (most stable)
+-- NO -> Is the text static and unique on screen?
|-- YES -> Use text selector
+-- NO -> Is there a unique parent or sibling?
|-- YES -> Use relational selector (below, childOf, etc.)
+-- NO -> Use point selector as last resort (fragile)是否可以添加无障碍标识符(testID/accessibilityIdentifier)?
|-- 是 -> 使用id选择器(最稳定)
+-- 否 -> 文本是否在屏幕上静态且唯一?
|-- 是 -> 使用text选择器
+-- 否 -> 是否有唯一的父元素或兄弟元素?
|-- 是 -> 使用关联选择器(below、childOf等)
+-- 否 -> 万不得已时使用point选择器(不稳定)Flow Organization
流程组织
Is this sequence used in 2+ flows?
|-- YES -> Extract to subflows/ directory, call with runFlow
+-- NO -> Keep inline in the flow
Does the flow need setup/teardown?
|-- YES -> For ALL flows: use onFlowStart/onFlowComplete in config.yaml
| For ONE flow: use runFlow at start/end of that flow
+-- NO -> Start with launchApp directly
Is there platform-specific behavior?
|-- YES -> Use when: platform: Android/iOS conditions
+-- NO -> Single flow handles both platforms该流程是否在2个以上的流程中使用?
|-- 是 -> 提取到subflows/目录,使用runFlow调用
+-- 否 -> 保留在当前流程内
流程是否需要初始化/清理?
|-- 是 -> 针对所有流程:在config.yaml中使用onFlowStart/onFlowComplete
| 针对单个流程:在该流程的开头/结尾使用runFlow
+-- 否 -> 直接以launchApp开头
是否存在平台特定行为?
|-- 是 -> 使用when: platform: Android/iOS条件
+-- 否 -> 单个流程处理两个平台When to Use JavaScript
何时使用JavaScript
Need a dynamic value (timestamp, random ID)?
|-- YES -> Inline ${} expression (e.g., ${Date.now()})
+-- NO -> Need to compute and store a value?
|-- YES -> evalScript for simple computation
+-- NO -> Need HTTP calls, file I/O, or complex logic?
|-- YES -> runScript with external .js file
+-- NO -> Plain YAML commands are sufficient</decision_framework>
<red_flags>
需要动态值(时间戳、随机ID)?
|-- 是 -> 使用内联${}表达式(例如${Date.now()})
+-- 否 -> 是否需要计算并存储值?
|-- 是 -> 使用evalScript进行简单计算
+-- 否 -> 是否需要HTTP请求、文件I/O或复杂逻辑?
|-- 是 -> 使用runScript结合外部.js文件
+-- 否 -> 使用纯YAML命令即可</decision_framework>
<red_flags>
RED FLAGS
警示信号
High Priority Issues:
- Using selectors for buttons/labels that will be localized - breaks when language changes. Use
text(accessibility identifiers) instead.id - Duplicating login/setup steps in every flow file - extract to subflow and call with
runFlow - Missing after
stopRecording- produces corrupted or zero-byte video filesstartRecording - Hardcoding credentials or API keys in YAML flow files - use environment variables with or
-eprefixMAESTRO_ - Using arbitrary or
sleepwith long timeouts instead ofextendedWaitUntil- Maestro's built-in tolerance handles most timing issues automaticallywaitForAnimationToEnd
Medium Priority Issues:
- Not using or
clearStatein setup - test results depend on leftover app state from previous runsclearKeychain - Not using for flow categorization - makes it impossible to run targeted subsets (smoke, regression, etc.)
tags - Using selectors (coordinates) as primary strategy - breaks on different screen sizes and resolutions
point - Not using on runFlow calls - test reports show file paths instead of meaningful step descriptions
label - Putting all flows in the root directory without subdirectories - becomes unmanageable beyond 20+ flows
Gotchas and Edge Cases:
- auto-retries for 7 seconds before failing - this is a feature, not a bug. Don't add explicit waits before assertions.
assertVisible - CLI parameters are always strings - use or comparison in JavaScript if you need numeric logic
parseInt() - prefixed shell variables are automatically available in flows but only via CLI, not Maestro Studio
MAESTRO_ - The string is truthy in JavaScript - use explicit
"false"comparison in=== "true"conditionswhen: true: - runs even when the flow fails - design teardown logic that doesn't assume success
onFlowComplete - with
runFlow(inline) andcommandswithrunFlow(external) are mutually exclusive - you cannot use both in the same runFlow callfile - Template literals (backticks) do not work inside because the command is already wrapped in
evalScript- use string concatenation instead${} - in
console.logwrites toevalScript, not the terminal - usemaestro.logfor terminal-visible loggingrunScript - maxRetries is capped at 3 - for more attempts, restructure the flow logic
retry - Maestro Cloud flag returns immediately without waiting for results - poll the API or use webhooks for completion
--async - FlashList / RecyclerView items may not have stable accessibility IDs - use with text fallback for list items
scrollUntilVisible
</red_flags>
<critical_reminders>
高优先级问题:
- 对会被本地化的按钮/标签使用选择器 - 语言变更时会失效。应使用
text(无障碍标识符)替代。id - 在每个流程文件中重复登录/设置步骤 - 提取为子流程并使用调用
runFlow - 后缺少
startRecording- 生成损坏或零字节的视频文件stopRecording - 在YAML流程文件中硬编码凭据或API密钥 - 使用带或
-e前缀的环境变量MAESTRO_ - 使用任意或
sleep长超时而非extendedWaitUntil- Maestro的内置容错机制可自动处理大多数时序问题waitForAnimationToEnd
中优先级问题:
- 在初始化时未使用或
clearState- 测试结果依赖于之前运行留下的应用状态clearKeychain - 未使用对流程分类 - 无法运行目标子集(冒烟测试、回归测试等)
tags - 将选择器(坐标)作为主要策略 - 在不同屏幕尺寸和分辨率下会失效
point - 在runFlow调用时未使用- 测试报告显示文件路径而非有意义的步骤描述
label - 将所有流程放在根目录而不使用子目录 - 超过20个流程后会难以管理
陷阱与边缘情况:
- 会自动重试最多7秒后才失败 - 这是功能而非bug。不要在断言前添加显式等待。
assertVisible - CLI参数始终是字符串 - 如果需要数值逻辑,在JavaScript中使用或比较操作
parseInt() - 带前缀的shell变量会自动在流程中可用,但仅通过CLI生效,Maestro Studio不支持
MAESTRO_ - 字符串在JavaScript中是真值 - 在
"false"条件中使用显式when: true:比较=== "true" - 即使在流程失败时也会运行 - 设计清理逻辑时不要假设流程成功
onFlowComplete - 带(内联)的
commands和带runFlow(外部)的file互斥 - 不能在同一个runFlow调用中同时使用两者runFlow - 中不支持模板字面量(反引号),因为命令已被
evalScript包裹 - 使用字符串拼接替代${} - 中的
evalScript写入console.log而非终端 - 使用maestro.log实现终端可见的日志runScript - 的maxRetries上限为3 - 需要更多尝试时,重构流程逻辑
retry - Maestro Cloud的标志会立即返回而不等待结果 - 轮询API或使用webhooks获取完成状态
--async - FlashList/RecyclerView项可能没有稳定的无障碍ID - 对列表项使用结合text备选方案
scrollUntilVisible
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
重要提醒
All code must follow project conventions in CLAUDE.md
(You MUST use selectors (accessibility identifiers) as primary selectors - text selectors break with localization or copy changes)
id(You MUST use for reusable sequences (login, onboarding) - NEVER duplicate steps across flow files)
runFlow(You MUST use before assertions on screens with animations or transitions - assertions on animated elements are flaky)
waitForAnimationToEnd(You MUST pair every with a - unpaired commands produce corrupted or missing video files)
startRecordingstopRecording(You MUST use environment variables or blocks for credentials and environment-specific values - NEVER hardcode secrets in YAML flows)
envFailure to follow these rules will produce flaky tests, broken recordings, and security-exposed credentials in version control.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定
(必须使用选择器(无障碍标识符)作为主要选择器 - 文本选择器会因本地化或文案变更失效)
id(必须使用处理可复用流程(登录、引导页) - 绝对不要在多个流程文件中重复步骤)
runFlow(必须在对带有动画或过渡效果的屏幕进行断言前使用 - 对动画元素的断言不稳定)
waitForAnimationToEnd(必须为每个配对 - 未配对的命令会生成损坏或缺失的视频文件)
startRecordingstopRecording(必须使用环境变量或块存储凭据和环境特定值 - 绝对不要在YAML流程中硬编码密钥)
env不遵守这些规则会导致不稳定的测试、损坏的录制文件,以及版本控制系统中暴露的凭据安全问题。
</critical_reminders>