Mobile E2E Testing skill for Codex

Few journeys, stable ids, green on every run. Codex picks Maestro, Detox or native UI test frameworks based on the stack, makes the app testable with stable ids, launch arguments and API-seeded data, then writes a small set of critical journeys that wait on elements instead of sleeping. It wires the suite into CI on macOS simulators or KVM Android emulators with JUnit output, screenshots and smoke tags.

  • Testing & QA
  • $mobile-e2e-testing
  • 180 lines
  • 2 files
  • ~1,850 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- mobile-e2e-testing

Installs to ~/.agents/skills/mobile-e2e-testing/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $mobile-e2e-testing, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Writes and stabilises mobile end-to-end tests with Maestro YAML flows and Detox for React Native, choosing between them and native XCUITest, Espresso or Appium - stable testID selectors, deterministic app state and test accounts, waits instead of sleeps, simulator and emulator setup, and CI on macOS runners or Android emulators with JUnit reports. Use when adding E2E tests to an iOS, Android, React Native, Expo or Flutter app, when mobile UI tests are flaky or slow, or when wiring Maestro or Detox into CI.

Use it when

  • Adding E2E coverage to an iOS, Android, React Native or Flutter app
  • Mobile UI tests are flaky, slow or tied to text and coordinates
  • Wiring Maestro or Detox into CI with reports and screenshots

Use something else when

How to check the result

Each new flow passes three consecutive runs from a clean install, and renaming a tapped element's id produces a clear failure with a screenshot.

The mistake it prevents

Selecting elements by visible text, index or coordinates, so copy changes, locales and screen sizes break the suite.

~/.agents/skills/mobile-e2e-testing/SKILL.md

--- name: mobile-e2e-testing description: Writes and stabilises mobile end-to-end tests with Maestro YAML flows and Detox for React Native, choosing between them and native XCUITest, Espresso… license: MIT ---

A mobile E2E suite is healthy when it covers a handful of critical journeys (sign in, purchase, core create/edit flow), starts every test from a known app state, finds elements by stable ids rather than text or coordinates, and runs green in CI on pinned simulator and emulator images. Device E2E is the most expensive layer; keep it small.

Inspect first

  1. Read AGENTS.md, package.json, app.json/app.config.*, ios/ and android/ folders, pubspec.yaml. Identify: native, React Native (bare or Expo), Flutter.
  2. Look for existing suites: .maestro/, e2e/, .detoxrc.js, *UITests targets, androidTest/.
  3. Check what can run here: iOS simulators need macOS with Xcode (xcrun simctl list devices); Android needs an SDK and emulator (emulator -list-avds). In a Codex sandbox, booting simulators, installing CLIs (curl -Ls "https://get.maestro.mobile.dev" | bash) and Gradle/CocoaPods downloads need network and usually approval. If no device can run, write the tests and the CI job, and say they are unverified locally.
  4. Find how test users and backend data are provisioned (seed endpoint, staging accounts).

Choose the tool

SituationDefaultWhy
Any stack, black-box journeys, quick to writeMaestroYAML flows, built-in tolerance for delays, works for native, RN, Flutter, web views
React Native, need synchronisation with JS, network and animationsDetoxGray-box: waits for the app to be idle, fewer explicit waits
Pure native team already in Xcode / Android StudioXCUITest / Espresso (Compose UI tests)Best tooling and speed per platform
Real-device farm, WebDriver ecosystem, many languagesAppiumBroad device-cloud support; slower and more setup

Default to Maestro unless the app is React Native and the team wants Detox’s idle synchronisation, or the team is native-only.

Make the app testable first

  • Add stable ids to every element a test touches: React Native testID, Flutter Key/Semantics(identifier:), iOS accessibilityIdentifier, Android testTag (Compose, with testTagsAsResourceId = true) or view ids.
  • Provide a test entry: launch arguments or a deep link that sets the API base URL, disables animations and onboarding, and signs in a test user.
  • Seed backend data per run through an API, not by tapping through setup screens.
  • Disable or stub third-party SDK prompts (tracking consent, push permission) in test builds, or grant permissions at launch.

Maestro

# .maestro/checkout.yaml
appId: com.example.shop
tags:
  - smoke
---
- launchApp:
    clearState: true
    arguments:
      e2e: "true"
- runFlow: subflows/sign-in.yaml
- tapOn:
    id: "product-card-0"
- tapOn:
    id: "add-to-cart"
- tapOn:
    id: "cart-tab"
- assertVisible: "1 item"
- tapOn: "Checkout"
- extendedWaitUntil:
    visible: "Order confirmed"
    timeout: 15000
- takeScreenshot: order-confirmed
# .maestro/subflows/sign-in.yaml
appId: com.example.shop
---
- tapOn:
    id: "email-input"
- inputText: ${TEST_EMAIL}
- tapOn:
    id: "password-input"
- inputText: ${TEST_PASSWORD}
- hideKeyboard
- tapOn:
    id: "sign-in-button"
- assertVisible:
    id: "home-screen"
maestro test .maestro/checkout.yaml -e [email protected] -e TEST_PASSWORD="$E2E_PASSWORD"
maestro test .maestro/ --include-tags=smoke --format junit --output build/maestro-junit.xml
maestro studio        # interactive element inspector

Keep test credentials for dedicated test accounts in CI secrets and env vars, never in flow files.

Detox (React Native)

// e2e/checkout.test.js
describe("checkout", () => {
  beforeAll(async () => {
    await device.launchApp({ newInstance: true, launchArgs: { e2e: "true" }, permissions: { notifications: "YES" } });
  });

  beforeEach(async () => {
    await device.reloadReactNative();
  });

  it("buys one item", async () => {
    await element(by.id("product-card-0")).tap();
    await element(by.id("add-to-cart")).tap();
    await element(by.id("cart-tab")).tap();
    await expect(element(by.text("1 item"))).toBeVisible();
    await element(by.text("Checkout")).tap();
    await waitFor(element(by.text("Order confirmed"))).toBeVisible().withTimeout(15000);
  });
});
npx detox build -c ios.sim.release
npx detox test -c ios.sim.release --record-logs failing --take-screenshots failing
npx detox test -c android.emu.release --headless
  • .detoxrc.js defines apps (binary path and build command), devices (simulator type, AVD name) and configurations joining them. Pin the simulator model and OS.
  • Use release builds in CI so tests do not depend on Metro; debug builds for local iteration.
  • Android needs the Detox instrumentation test class and Gradle changes from the Detox setup guide; Expo apps need a prebuild or dev client plus the Detox config plugin.
  • If idle sync hangs on a looping animation or long-polling request, scope the fix (device.setURLBlacklist([...]), disable the animation in e2e mode) before reaching for device.disableSynchronization().

Flake control

CauseFix
Fixed sleepsextendedWaitUntil (Maestro) / waitFor().withTimeout() (Detox) on the target element
Text selectors break with copy or locale changesUse ids; set device locale explicitly
Leftover state from previous testclearState: true / newInstance: true plus device.clearKeychain() where needed (iOS)
System dialogs (permissions, keyboard, rate-app)Grant permissions at launch, hideKeyboard, disable prompts in e2e builds
Shared backend test account mutated by parallel runsOne account per shard or per run
Slow cold emulatorBoot and warm up before tests; snapshot AVDs; disable animations (adb shell settings put global animator_duration_scale 0 and the window/transition scales)

CI

  • iOS: macOS runners with a pinned Xcode version and simulator; cache the built app between build and test jobs.
  • Android: Linux runners with KVM can run the emulator (for example reactivecircus/android-emulator-runner on GitHub Actions); pin API level and system image.
  • Hosted options (Maestro Cloud, EAS Workflows, device farms) avoid simulator upkeep; uploading builds to them needs network and account tokens.
  • Publish JUnit XML, screenshots and videos as artifacts on failure.
  • Run the smoke tag on every PR; the full suite on merge or nightly.

Verification

  • Run each new flow three times in a row locally or in CI; all green.
  • Break the journey on purpose (rename the button id) and confirm a clear failure with a screenshot.
  • Confirm the test leaves no state that changes the next run’s outcome (run the suite twice without reinstalling).

Gotchas

  • Coordinates and index-based taps break on a different screen size; never commit them.
  • React Native testID on a custom component does nothing unless it is passed down to a native view.
  • Expo Go is not a test target; build the actual app (dev client or release build).
  • iOS simulator keychain survives app reinstalls; signed-in state can leak between runs.
  • Running E2E against production backends creates real orders and emails; use a staging or mocked backend and dedicated test accounts.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Mobile E2E Testing"
  short_description: "Few journeys, stable ids, green on every run"
  default_prompt: "$mobile-e2e-testing Add Maestro tests for sign-in and checkout to our Expo app"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem