mobile-testing-maestro

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Maestro Mobile UI Testing Patterns

Maestro移动端UI测试模式

Quick Guide: Write E2E tests as declarative YAML flows. Use
id
selectors for stable element targeting (not text that changes with localization). Use
runFlow
to compose reusable subflows (login, setup). Use
waitForAnimationToEnd
before assertions on animated screens. Use
onFlowStart
/
onFlowComplete
hooks for setup/teardown. Maestro auto-retries assertions for up to 7 seconds before failing. Current stable: CLI 2.4.0.

<critical_requirements>
**快速指南:**将E2E测试编写为声明式YAML流程。使用
id
选择器实现稳定的元素定位(不要使用随本地化变化的文本)。使用
runFlow
组合可复用的子流程(登录、设置)。在对带动画的屏幕进行断言前使用
waitForAnimationToEnd
。使用
onFlowStart
/
onFlowComplete
钩子进行初始化/清理操作。Maestro在断言失败前会自动重试最多7秒。当前稳定版本:CLI 2.4.0。

<critical_requirements>

CRITICAL: Before Using This Skill

重要提示:使用此技能之前

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST use
id
selectors (accessibility identifiers) as primary selectors - text selectors break with localization or copy changes)
(You MUST use
runFlow
for reusable sequences (login, onboarding) - NEVER duplicate steps across flow files)
(You MUST use
waitForAnimationToEnd
before assertions on screens with animations or transitions - assertions on animated elements are flaky)
(You MUST pair every
startRecording
with a
stopRecording
- unpaired commands produce corrupted or missing video files)
(You MUST use environment variables or
env
blocks for credentials and environment-specific values - NEVER hardcode secrets in YAML flows)
</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
- 对动画元素的断言不稳定)
(必须为每个
startRecording
配对
stopRecording
- 未配对的命令会生成损坏或缺失的视频文件)
(必须使用环境变量或
env
块存储凭据和环境特定值 - 绝对不要在YAML流程中硬编码密钥)
</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:
  1. Declarative over imperative - YAML flows describe user intent, not implementation details
  2. Built-in tolerance - Maestro auto-waits up to 7 seconds for elements, auto-retries taps, and handles animation delays without explicit waits
  3. Single flow, multiple platforms - One YAML file can test both iOS and Android with platform conditions for differences
  4. Composition over duplication - Extract reusable sequences (login, setup, teardown) into subflows with
    runFlow
  5. Stable selectors - Use accessibility identifiers (
    id
    ) over visible text to survive localization and copy changes
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)
</philosophy>
<patterns>
Maestro与基于代码的测试框架采用完全不同的方法:测试是声明式YAML,而非命令式代码。这使得团队中的任何人都能读懂流程,而不仅仅是开发人员。框架自动处理移动端测试的难点——等待元素、重试点击、容忍动画延迟——因此流程专注于_测试什么_,而不是_如何等待_。
核心原则:
  1. 声明式优先于命令式 - YAML流程描述用户意图,而非实现细节
  2. 内置容错机制 - Maestro自动等待元素最多7秒,自动重试点击,无需显式等待即可处理动画延迟
  3. 单流程多平台 - 一个YAML文件可测试iOS和Android,通过平台条件处理差异
  4. 组合优先于重复 - 使用
    runFlow
    将可复用流程(登录、设置、清理)提取为子流程
  5. 稳定选择器 - 使用无障碍标识符(
    id
    )而非可见文本,以适应本地化和文案变更
思维模型:
Maestro流程就像食谱。每个步骤都是用户会执行的操作。框架处理时序、重试和平台差异。你描述流程,Maestro负责执行。
何时使用Maestro:
  • 关键用户旅程的冒烟测试(登录、购买、引导页)
  • 之前出现过问题的流程的回归测试
  • 单个流程文件完成跨平台验证
  • 录制测试运行过程供利益相关方查看
何时不使用Maestro:
  • 业务逻辑的孤立单元测试
  • 无UI的API契约测试
  • 性能基准测试或负载测试
  • 需要大量编程逻辑的复杂数据驱动测试(Maestro的JS支持相比完整测试框架有限)
</philosophy>
<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
id
(accessibility identifier) as the primary selector. Fall back to
text
for static labels,
point
for coordinates only as last resort. Combine selectors for precision.
yaml
undefined
使用
id
(无障碍标识符)作为主要选择器。对于静态标签可退而求其次使用
text
,仅在万不得已时使用
point
坐标选择器。可组合选择器以提高精度。
yaml
undefined

Preferred: 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
env
parameters. Use
label
for clear test reports.
yaml
undefined
将重复流程提取到单独的流程文件中。通过
env
参数传递上下文。使用
label
生成清晰的测试报告。
yaml
undefined

Main 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"

```yaml

appId: 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"

```yaml

Subflow: 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
when
with
platform
,
visible
,
notVisible
, or JavaScript
true
expressions to handle differences between iOS and Android or optional UI states.
yaml
undefined
使用
when
结合
platform
visible
notVisible
或JavaScript
true
表达式处理iOS和Android之间的差异或可选UI状态。
yaml
undefined

Platform-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 (
-e
), shell variables (
MAESTRO_
prefix), or
env
blocks in flow files. Use
${}
syntax for interpolation with JavaScript fallback defaults.
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标志(
-e
)、shell变量(
MAESTRO_
前缀)或流程文件中的
env
块传递运行时值。使用
${}
语法进行插值,并支持JavaScript fallback默认值。
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

Override 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
onFlowStart
and
onFlowComplete
in the configuration block for consistent setup/teardown across all flows.
onFlowComplete
runs even if the flow fails.
yaml
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
onFlowStart
fails, the main flow is skipped but
onFlowComplete
still executes. If
onFlowComplete
fails, the flow is marked as failed even if the main test passed.
See examples/flow-control.md for hooks with environment variables and script-based teardown.

在配置块中使用
onFlowStart
onFlowComplete
实现所有流程的统一初始化/清理。即使流程失败,
onFlowComplete
也会执行。
yaml
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确保干净的应用状态,登录在每个流程前运行,清理始终执行(即使失败),防止流程间的测试污染
钩子失败行为: 如果
onFlowStart
失败,主流程会被跳过,但
onFlowComplete
仍会执行。如果
onFlowComplete
失败,即使主测试通过,流程也会被标记为失败。
查看examples/flow-control.md了解结合环境变量的钩子和基于脚本的清理。

Pattern 7: JavaScript Expressions

模式7:JavaScript表达式

Use inline
${}
for simple interpolation,
evalScript
for variable computation, and
runScript
for complex logic in external
.js
files. All share a global
output
object.
yaml
undefined
使用内联
${}
进行简单插值,使用
evalScript
进行变量计算,使用
runScript
处理外部
.js
文件中的复杂逻辑。所有方式共享全局
output
对象。
yaml
undefined

Inline 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
    text
    selectors for buttons/labels that will be localized - breaks when language changes. Use
    id
    (accessibility identifiers) instead.
  • Duplicating login/setup steps in every flow file - extract to subflow and call with
    runFlow
  • Missing
    stopRecording
    after
    startRecording
    - produces corrupted or zero-byte video files
  • Hardcoding credentials or API keys in YAML flow files - use environment variables with
    -e
    or
    MAESTRO_
    prefix
  • Using arbitrary
    sleep
    or
    extendedWaitUntil
    with long timeouts instead of
    waitForAnimationToEnd
    - Maestro's built-in tolerance handles most timing issues automatically
Medium Priority Issues:
  • Not using
    clearState
    or
    clearKeychain
    in setup - test results depend on leftover app state from previous runs
  • Not using
    tags
    for flow categorization - makes it impossible to run targeted subsets (smoke, regression, etc.)
  • Using
    point
    selectors (coordinates) as primary strategy - breaks on different screen sizes and resolutions
  • Not using
    label
    on runFlow calls - test reports show file paths instead of meaningful step descriptions
  • Putting all flows in the root directory without subdirectories - becomes unmanageable beyond 20+ flows
Gotchas and Edge Cases:
  • assertVisible
    auto-retries for 7 seconds before failing - this is a feature, not a bug. Don't add explicit waits before assertions.
  • CLI parameters are always strings - use
    parseInt()
    or comparison in JavaScript if you need numeric logic
  • MAESTRO_
    prefixed shell variables are automatically available in flows but only via CLI, not Maestro Studio
  • The string
    "false"
    is truthy in JavaScript - use explicit
    === "true"
    comparison in
    when: true:
    conditions
  • onFlowComplete
    runs even when the flow fails - design teardown logic that doesn't assume success
  • runFlow
    with
    commands
    (inline) and
    runFlow
    with
    file
    (external) are mutually exclusive - you cannot use both in the same runFlow call
  • Template literals (backticks) do not work inside
    evalScript
    because the command is already wrapped in
    ${}
    - use string concatenation instead
  • console.log
    in
    evalScript
    writes to
    maestro.log
    , not the terminal - use
    runScript
    for terminal-visible logging
  • retry
    maxRetries is capped at 3 - for more attempts, restructure the flow logic
  • Maestro Cloud
    --async
    flag returns immediately without waiting for results - poll the API or use webhooks for completion
  • FlashList / RecyclerView items may not have stable accessibility IDs - use
    scrollUntilVisible
    with text fallback for list items
</red_flags>

<critical_reminders>
高优先级问题:
  • 对会被本地化的按钮/标签使用
    text
    选择器 - 语言变更时会失效。应使用
    id
    (无障碍标识符)替代。
  • 在每个流程文件中重复登录/设置步骤 - 提取为子流程并使用
    runFlow
    调用
  • startRecording
    后缺少
    stopRecording
    - 生成损坏或零字节的视频文件
  • 在YAML流程文件中硬编码凭据或API密钥 - 使用带
    -e
    MAESTRO_
    前缀的环境变量
  • 使用任意
    sleep
    extendedWaitUntil
    长超时而非
    waitForAnimationToEnd
    - Maestro的内置容错机制可自动处理大多数时序问题
中优先级问题:
  • 在初始化时未使用
    clearState
    clearKeychain
    - 测试结果依赖于之前运行留下的应用状态
  • 未使用
    tags
    对流程分类 - 无法运行目标子集(冒烟测试、回归测试等)
  • point
    选择器(坐标)作为主要策略 - 在不同屏幕尺寸和分辨率下会失效
  • 在runFlow调用时未使用
    label
    - 测试报告显示文件路径而非有意义的步骤描述
  • 将所有流程放在根目录而不使用子目录 - 超过20个流程后会难以管理
陷阱与边缘情况:
  • assertVisible
    会自动重试最多7秒后才失败 - 这是功能而非bug。不要在断言前添加显式等待。
  • CLI参数始终是字符串 - 如果需要数值逻辑,在JavaScript中使用
    parseInt()
    或比较操作
  • MAESTRO_
    前缀的shell变量会自动在流程中可用,但仅通过CLI生效,Maestro Studio不支持
  • 字符串
    "false"
    在JavaScript中是真值 - 在
    when: true:
    条件中使用显式
    === "true"
    比较
  • onFlowComplete
    即使在流程失败时也会运行 - 设计清理逻辑时不要假设流程成功
  • commands
    (内联)的
    runFlow
    和带
    file
    (外部)的
    runFlow
    互斥 - 不能在同一个runFlow调用中同时使用两者
  • evalScript
    中不支持模板字面量(反引号),因为命令已被
    ${}
    包裹 - 使用字符串拼接替代
  • evalScript
    中的
    console.log
    写入
    maestro.log
    而非终端 - 使用
    runScript
    实现终端可见的日志
  • retry
    的maxRetries上限为3 - 需要更多尝试时,重构流程逻辑
  • Maestro Cloud的
    --async
    标志会立即返回而不等待结果 - 轮询API或使用webhooks获取完成状态
  • FlashList/RecyclerView项可能没有稳定的无障碍ID - 对列表项使用
    scrollUntilVisible
    结合text备选方案
</red_flags>

<critical_reminders>

CRITICAL REMINDERS

重要提醒

All code must follow project conventions in CLAUDE.md
(You MUST use
id
selectors (accessibility identifiers) as primary selectors - text selectors break with localization or copy changes)
(You MUST use
runFlow
for reusable sequences (login, onboarding) - NEVER duplicate steps across flow files)
(You MUST use
waitForAnimationToEnd
before assertions on screens with animations or transitions - assertions on animated elements are flaky)
(You MUST pair every
startRecording
with a
stopRecording
- unpaired commands produce corrupted or missing video files)
(You MUST use environment variables or
env
blocks for credentials and environment-specific values - NEVER hardcode secrets in YAML flows)
Failure to follow these rules will produce flaky tests, broken recordings, and security-exposed credentials in version control.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定
(必须使用
id
选择器(无障碍标识符)作为主要选择器 - 文本选择器会因本地化或文案变更失效)
(必须使用
runFlow
处理可复用流程(登录、引导页) - 绝对不要在多个流程文件中重复步骤)
(必须在对带有动画或过渡效果的屏幕进行断言前使用
waitForAnimationToEnd
- 对动画元素的断言不稳定)
(必须为每个
startRecording
配对
stopRecording
- 未配对的命令会生成损坏或缺失的视频文件)
(必须使用环境变量或
env
块存储凭据和环境特定值 - 绝对不要在YAML流程中硬编码密钥)
不遵守这些规则会导致不稳定的测试、损坏的录制文件,以及版本控制系统中暴露的凭据安全问题。
</critical_reminders>