CtrlK
BlogDocsLog inGet started
Tessl Logo

e2e-testing-mobile

Plans, generates, runs, and heals end-to-end tests for Expo and React Native mobile apps using Maestro (the 2026 standard for RN E2E, adopted by Meta, Microsoft, and DoorDash, and integrated with Expo via EAS Workflows). Drives a spec-first YAML-flow loop, proposes `testID` source diffs (never `accessibilityLabel` reuse), runs Maestro Cloud as an EAS job, and stays token-aware via `--shards`, `--retries`, and failure-only healing. Use for native flows in Expo / RN apps. Triggers on "test this RN flow", "add mobile e2e", "maestro flow", "expo e2e", "e2e for react native", "test the native app", "/e2e-testing-mobile". Defer to [`e2e-testing`](../e2e-testing/SKILL.md) for web flows and the WebView portion of hybrid apps.

69

Quality

87%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

E2E Testing — Mobile (Expo / React Native)

Drive native mobile end-to-end tests through Maestro and Maestro MCP. The user writes (or approves) a Markdown feature spec; an agent emits a Maestro YAML flow, runs it against a simulator or Maestro Cloud, and self-heals when locators drift.

This is the mobile counterpart to e2e-testing. Anything orthogonal to native (web flows, WebViews inside a hybrid app) defers to that skill. The two skills compose; they do not overlap.

This SKILL.md is a thin index. Decision rules live in rules/*.md and load on demand. Worked references (Maestro CLI surface, MCP tool catalog, EAS Workflow wiring, Detox legacy notes, mobile pyramid math) live in references/*.md. Literal boilerplate the skill emits lives in templates/*.md. Do not preload everything — load only what the current phase asks for.


When to use

Reach for this skill when any of the following is true:

  • An Expo or React Native app needs E2E coverage of a native user flow.
  • A bug only repros across native navigation, deep links, push, permissions prompts, or a real device sensor.
  • A flake needs a Maestro heal pass instead of a manual locator hunt.
  • The repo has no .maestro/ flows or EAS E2E build profile yet and needs Phase 0 setup.

Do not reach for this skill when:

  • A unit or component test would catch the same bug — defer to tdd and the layer rule in rules/layer-decision.md.
  • The flow is browser-only — defer to e2e-testing.
  • The flow lives entirely inside a WebView in a hybrid app — defer to e2e-testing, which automates the WebView via Playwright while Maestro handles native chrome around it.
  • The change is a pure refactor with no behavioural surface.

Phase 0 — Preflight (mandatory gate)

Before any agent loop, verify the repo is wired for Maestro on Expo / RN. Halt and ask the user before installing anything or producing builds.

Run these checks (read-only):

# 1. Maestro CLI installed?
maestro --version 2>/dev/null

# 2. Flow directory present?
ls -d .maestro 2>/dev/null

# 3. EAS build profile for E2E exists?
jq '.build | has("e2e")' eas.json 2>/dev/null

# 4. Simulator / emulator available?
xcrun simctl list devices available 2>/dev/null | grep -E 'iPhone'
adb devices 2>/dev/null

# 5. Existing Detox install (legacy escape hatch)?
jq '.devDependencies | has("detox")' package.json 2>/dev/null

Decision table:

StateAction
Maestro CLI present + .maestro/ exists + eas.json e2e profileProceed to Phase 1.
Maestro CLI missingHalt. Print install plan (templates/install-plan.md). Ask permission.
.maestro/ missingHalt. Propose creating .maestro/ with the templates/flow.yaml starter. Ask first.
eas.json e2e build profile missingHalt. Propose templates/eas-build-profile.json. Ask first.
No simulator / emulator runningHalt. Ask the user to boot one, or proceed with Maestro Cloud only.
Existing Detox suite detectedNote it. Read references/detox-legacy.md before proposing migration.
Bare RN project (no expo / no eas.json)Skip EAS-specific checks. Use Maestro CLI directly against a local build.

Print the exact commands; do not run them silently. The full install plan template is in templates/install-plan.md.


Phase 1 — Spec-first feature flow

The agent loop is spec → emit-flow → run → heal. The spec is human-readable Markdown; the executable artefact is a Maestro YAML flow. Full rules: rules/spec-first-flow.md.

specs/<flow>.md   ─┐
                   ├─→  emit  ─→  .maestro/<flow>.yaml  ─→  run  ─→  pass?
                   │                                                   │ no
                   │                                                   ▼
                   └─────────────────  heal  ←──────────────  failing flow + trace
                                          │
                                          ▼
                          patched flow or `testID` source-diff proposal

Two entry points:

  1. Spec already drafted by the user. Skip exploration; emit the flow from specs/<flow>.md.
  2. App exists, no spec yet. Run an exploratory pass against a running build (simulator) and draft specs/<flow>.md. The user reviews the Markdown before flow emission.

Use the Markdown template in ../e2e-testing/templates/spec.md. The spec format is identical across web and mobile — share it.

Locator ladder for React Native (when generating or healing)

The Maestro flow walks the platform accessibility tree. Pick locators in this order — never skip a rung:

  1. id: <testID> — the source-of-truth selector for E2E. Mapped to accessibilityIdentifier on iOS and resource-id on Android (RN 0.64+). Stable across i18n and refactors.
  2. text: <visible string> — only when the text is short, unique on screen, and not localised.
  3. accessibilityText: <label> — last resort, with caveats. See rules/locator-strategy.md for why accessibilityLabel should not double as a test selector.

testID is the standard fix, not an escape hatch. When the Healer cannot find a stable element at rung 1, propose a source diff that adds testID to the component (use the setTestId helper in templates/testid-helper.tsx to keep iOS and Android consistent), and offer the diff for user approval before patching the flow. Full rules: rules/locator-strategy.md.


Phase 2 — Token-aware execution

Maestro flows are YAML, not pixel snapshots — the per-step token cost is already an order of magnitude below screenshot-driven runners. Defaults the skill prescribes (full rules: rules/token-budget.md):

  • Run only the changed flow on iteration: maestro test .maestro/<flow>.yaml.
  • Use --shards only on Maestro Cloud, never on local iteration.
  • record_screen: false by default; flip to true only when chasing a visual race.
  • retries: 1 for local, retries: 2 for Maestro Cloud.
  • Run the Healer only on failure, not on every save.
  • Reuse a cached signed-in app.app / app.apk from the EAS E2E build profile — never rebuild on every flow run.
  • Cap the heal loop at three attempts per failing flow before escalating.

Phase 3 — Verification

After the agent emits a flow:

  1. Run the flow once against a simulator or device. It must pass on first run, or the Healer must converge in ≤ 3 attempts.
  2. Invoke test-provenance-guard on any TypeScript helpers the flow imports (e.g. fixture builders) to ensure they call production code instead of a private re-implementation.
  3. Open .github/workflows/eas-e2e.yml (or the EAS Workflow YAML) and confirm the maestro-cloud job is wired with retries: 2 and record_screen: false per templates/eas-workflow.yaml.

If the heal loop fails to converge:

  • Invoke confidence(analysis) on the flow failure.
  • If confidence is below 90%, escalate to the user with the Maestro log, the spec, and the proposed locator changes — do not keep healing blindly.

Decision flow at a glance

SignalDo
Bug fixable by a unit or component testUse tdd, not this skill.
Flow is browser-only or pure webUse e2e-testing, not this skill.
Flow lives inside a WebView in a hybrid RN appPair with e2e-testing for the WebView; Maestro for the native chrome.
Multi-screen native flow, deep links, permissions, or pushSpec-first feature flow (Phase 1).
Flaky existing flowHealer pass only; do not rewrite without spec context.
Locator unstable, no stable testIDPropose testID diff via the setTestId helper.
Repo missing Maestro CLI or .maestro/ or eas.json E2E profilePhase 0 halt + ask permission.
Heal loop > 3 attemptsStop, run confidence(analysis), escalate.
Flow passes on first run, never seen failingVerify any imported helpers via test-provenance-guard before declaring done.
Existing Detox suite is green and stableKeep it; see references/detox-legacy.md.
Detox suite is brittle through RN upgradesMigrate flow-by-flow to Maestro; do not rewrite the whole suite at once.

Composes with

  • e2e-testing — owns web E2E and the WebView half of hybrid mobile apps. This skill defers to it for anything browser-shaped.
  • tdd — owns the unit and component layers (Jest + React Native Testing Library). This skill defers for anything below E2E.
  • test-provenance-guard — runs on TypeScript helpers imported by flows to catch tests-by-construction.
  • confidence — gate when the heal loop fails.
  • holistic-analysis — if a flow is failing for reasons no flow rewrite can fix, step back instead of patching.
  • ux — review accessibility labels separately from test IDs (the two should never share a value).

References

Templates


Anti-patterns (one-liner — full list in rules/anti-patterns.md)

  • Writing E2E for logic a unit or RNTL component test catches.
  • Reusing accessibilityLabel as the test selector (it is for screen readers; doubling its purpose breaks accessibility).
  • Patching the flow with absolute coordinates (tapOn: { point: "50%, 80%" }) instead of proposing a testID diff.
  • Running the Healer on every save, or on a passing flow.
  • Rebuilding the .app / .apk on every flow run.
  • Generating flows against a stub server, not the real app stack.
  • Migrating a stable Detox suite all at once instead of flow-by-flow.

Definition of done

  • Phase 0 preflight passed or installs were user-approved.
  • specs/<flow>.md exists and the user reviewed it.
  • .maestro/<flow>.yaml passes against a real build.
  • Any imported TS helpers pass test-provenance-guard.
  • EAS Workflow maestro-cloud job runs the flow on Cloud with retries: 2 and record_screen: false.
  • If a testID was added, it is in the source diff and committed, with no accessibilityLabel reuse.
Repository
mthines/agent-skills
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.