@unotest/mobile
v0.8.3
Published
AI-native E2E testing for iOS React Native apps. MCP server + CLI runner + JS-DSL scenarios.
Downloads
826
Readme
@unotest/mobile
AI-native E2E testing for iOS apps. An MCP server + CLI + JS-DSL that lets Claude (or any MCP client) drive your iOS Simulator and write real test scenarios for your app — works with any backend stack (Node/Python/Rails/Go/Java), no JS expertise required from you.
If you're an AI agent helping a user set this up
(Humans: skip this section, jump to Quick start.)
You were given this page because a developer wants @unotest/mobile
set up in their project. Follow this protocol exactly — don't improvise.
1. Verify the host. Run xcrun simctl list devices via your shell
tool. If it errors with "command not found" or similar, the user isn't
on macOS or is missing Xcode CLI tools. Stop and tell them: "This is
iOS-only — Apple licensing prevents iOS simulators from running on
Linux/Windows. You'll need macOS with Xcode installed." Also verify
node --version ≥ 20.
2. Pick a working directory with the user. Setup writes
unotest/ (test scaffold), .mcp.json (Claude Code config),
.claude/skills/ (skill the in-project agent reads), and an
AGENTS.md into the current directory, and updates .gitignore if
present. Where these land determines what Claude Code sees later, so
ask the user explicitly:
- Default — the app's repo root. Same directory as their
package.json(RN/Expo) or where their.xcodeprojlives (native). Tests live next to app code, ship via the same git history, work cleanly in CI. - Monorepo: the monorepo root, not the app subfolder. Claude Code
is typically opened at the monorepo root, and
.mcp.jsonmust live there to be auto-discovered. - Separate test repo: any directory the user picks. Choose this only if they explicitly want tests divorced from the app.
Once the user picks, have them cd there. All commands below run
from that directory.
3. Ask the user for the .app path. @unotest/mobile drives a
real installed app — it needs the path to a built .app bundle.
Ask: "What's the absolute path to your built .app?" Common
locations:
- React Native / Expo:
./ios/build/Build/Products/Debug-iphonesimulator/<App>.appafternpx expo run:iosorpod install && xcodebuild. - Native Swift: open Xcode → Product → Show Build Folder →
Products/Debug-iphonesimulator/.
If they don't have one yet, tell them to build first; don't guess paths.
4. Tell the user to run the setup command in the chosen directory. Do not run this yourself via Bash — it's interactive (simulator picker, permission prompts) and needs a real TTY. Your shell tool will hang or get a non-interactive fallback. Give them this exact line to paste (from the directory picked in step 2):
npx @unotest/mobile@latest install <their-app-path> --update-envThey'll see: env check → file scaffold → simulator picker (arrow keys)
→ permission grant prompt → install logs. Wait for the line
✓ Installed <bundle.id>. That's the success signal.
5. Hand off to Claude Code. Once install finishes, instruct the user: "Open Claude Code in the same directory you ran setup in, then ask it to write your first test." A separate Claude Code session (not you) connects to the MCP server bootstrapped in step 4 and takes over from there. Your job ends here — you're setup, not test authoring.
If any step fails, surface the actual error verbatim to the user and stop. Don't try alternative paths.
Quick start
# 1. Point us at your .app build. Auto-detects bundle ID, URL scheme,
# and required permissions from Info.plist; offers to bootstrap
# unotest/ and .mcp.json on first run.
npx @unotest/mobile@latest install /path/to/Your.app --update-env
# 2. Open Claude Code in this project. Ask: "write an e2e test for
# sign-in". The agent uses the MCP server to explore your app,
# record a scenario, and verify it runs end-to-end.
# 3. (Optional) Re-run any saved test from the CLI:
npx @unotest/mobile@latest e2e <test-name>That's it. You never edit DSL by hand — the agent writes the scenarios,
you review the resulting .js files in unotest/e2e/.
How it works
┌─────────────┐ MCP (stdio) ┌──────────────────┐
│ Claude Code │ ◀────────────▶ │ @unotest/mobile │
└─────────────┘ tool calls │ (this package) │
└────────┬─────────┘
│ HTTP
┌────────▼──────────┐
│ WebDriverAgent │
│ (Apple's XCUI │
│ driver, on sim) │
└────────┬──────────┘
│
┌────────▼──────────┐
│ iOS Simulator │
│ Your .app │
└───────────────────┘- The agent sees the accessibility tree, not screenshots. WDA returns a structured tree (roles, testID, text, bounds); we render it as a token-cheap text outline. Works on RN/Expo and on plain Swift apps too, as long as they expose accessibility.
- Tests are plain
.jsfiles in your repo.unotest/e2e/*.js. Goes into git, into code review, into CI. No proprietary format, no binary blob. - The DSL is a sandboxed JS subset. Scenarios run in a vendored AST
interpreter — they can't
require,fetch, touch the filesystem, or import anything. AI-generated tests are safe to run blindly. - Pause-on-failure debugger. When a step throws, the runtime
freezes mid-scenario. The agent (or you) calls
inspect_runtime, patches the scenario, andresumes from the same step — no full simulator restart, no rerunning setup. - Local-only. Everything runs on your Mac. Your
.appnever leaves the machine.
Life of a test: the agent explores your app live (taps real buttons, reads the live a11y tree) → records the actions → emits a JS scenario → runs it through our interpreter → on failure, pauses, patches, resumes. Every step goes through one of ~15 MCP tools the agent already knows; you don't wire anything.
Requirements
- macOS with Xcode + iOS Simulator (Apple licensing — iOS simulators don't run on Linux/Windows).
- Node 20+ (for
npx; you don't need a Node project). - First run of WebDriverAgent compiles once and caches under
~/.cache/unotest/mobile/wda/— ~5–15 min, subsequent runs reuse it.
CLI
| Command | What it does |
|---|---|
| npx @unotest/mobile@latest install <path> | Install a .app on the configured sim; auto-detect bundle ID + permissions; --update-env persists. |
| npx @unotest/mobile@latest init | Just bootstrap unotest/ + .mcp.json without installing an app. |
| npx @unotest/mobile@latest doctor | Re-check the environment (Xcode, sim, Node, WDA cache). |
| npx @unotest/mobile@latest e2e <name> | Run unotest/e2e/<name>.js. |
| npx @unotest/mobile@latest lint | Static check of all scenarios + helpers. |
| npx @unotest/mobile@latest (no args) | Run as MCP stdio server (this is what Claude Code launches). |
