Testa
About
Drive the iOS Simulator from an AI agent: real HID gestures, token-efficient accessibility snapshots plus OCR, and deterministic replay flows for CI.
Details
- Author
- valewnrt
- Categories
- Developer Tools, Other
Jump to
Setup
Install Testa in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/valewnrt/testa
Follow the installation instructions in the repository README, then restart your MCP client.
Drive the iOS Simulator like a human — from an AI agent.
Real HID touches (every gesture), screen reading via the accessibility treeor on-device OCR, token-efficient, fast, andzero third-party dependencies. Test React Native / Expo and native SwiftUI apps end-to-end —without adding a singletestID— then save the run as a flow file and let CI replay it for free.
An agent tapping, pinching, rotating, dragging-and-dropping, and typing — all verified through the accessibility tree, no screenshots.
Agents are great at writing iOS apps and clumsy at the part that comes next:actually exercising them in the simulator.Testa is built for that — and three things set it apart from the rest of the field: it drives screens that exposezero accessibility(via on-device OCR), it turns an agent session into adeterministic flow file that CI replays with no model in the loop, and it's asingle, fully-open, dependency-free binary.
- 🧠No app setup required.Reads the accessibility tree, and falls back toApple Vision OCRto tap anyvisible text— so it drives canvas, games, WebViews and vibe-coded apps that never added atestID.
- 🪙Token-efficient.One compact line per element (e5 Button "Save" #save @120,300) — auisnapshot of a real screen measures~203 tokensagainst~1,500for the same screen as an image.Measure it yourself →
- 🔁Acting commands answer with the UI diff.Everytap/type/swipereply ends with-- ui changes --, so the loop isact → read the diff, notact → full snapshot.
- 🎬Record once, replay forever.testa flow record save smoke.flowwrites what the agent just did as plain text;testa flow run smoke.flowreplays it deterministically —zero tokens, JUnit output, failure artifact bundles.
- ⚡Fast.A warm daemon keeps the connection, accessibility translator and HID client hot:~60 msper snapshot (measured 24–39 ms marginal, seebench/).
- 👆Every gesture, for real.Tap, long-press, swipe,drag-and-drop,pinch/zoom,rotate, multi-touch, hardware buttons, key combos, unicode/emoji text — genuine HID events.
- 🌍Control the device, not just the app.Push notifications, GPS location, Face ID / Touch ID, dark mode, Dynamic Type, status bar, locale, pasteboard, photo library, launch env & args.
- ♿testa audit— accessibility audit (missing labels, sub-44 pt tap targets, duplicate labels) that fails a CI job.
- 🖼testa vdiff— visual regression with an antialiasing-tolerant pixel diff, a red heatmap, andOCR-aware- lost:/+ new:linesso a percentage becomes a sentence.
- 🔌Agent-native.Ships anMCP server(testa mcp), a Claude Code skill, a Claude Code plugin manifest, and a composite GitHub Action.
- 🔒Local & private.A0600per-user Unix socket. No network, no telemetry.
- 📦Zero third-party runtime deps.Talks straight to Apple'sCoreSimulator,SimulatorKit,AccessibilityPlatformTranslation, Vision andsimctl.
High-level summary, checked against each project's public documentation. These are all good tools.⚠️ means "partial, plugin-only, commercial-tier, or not documented"— it is not a criticism, and where a project's docs did not settle the question we marked it ⚠️ rather than guess. The closest tool isArgent(Software Mansion): broader than Testa (cross-platform, deep debugging & profiling), but accessibility-only (no OCR), Node-based, and Apache-2.0 sourceplus proprietary binaries.Maestro's YAML flows are the reference for declarative mobile flows and are excellent;Appium's XCUITest driver has the richest device environment surface of any of these. Testa's niche: fully-open, dependency-free, OCR-driven, iOS-focused, and the same tool for both the agent loop and the token-free CI replay. Corrections welcome — open an issue.
# Install — builds from source, installs the skill, registers the MCP server brew tap valewnrt/testa brew install testa testa setup # …or from source git clone https://github.com/valewnrt/testa && cd testa && ./install.sh
testa boot "iPhone 17 Pro" testa install ./MyApp.app && testa launch com.example.myapp testa ui # what's on screen (token-efficient) testa tap "Continue" # by visible text — falls back to OCR testa typein "#email" "a@b.co" testa assert "#welcome" exists # → PASS / FAIL (exit 0/1)
First call boots a background daemon and warms accessibility (a few seconds, once). Every call after is ~60 ms. Requires macOS + Xcode 26 (iOS 26 sims), Swift 6.
- Observe—testa ui(on-screen elements) ·testa see(OCR every visible text) ·testa find <q>·testa scrollto <sel>.
- Act—tap · typein · setvalue · clear · swipe · drag · dragdrop · pinch · rotate · keycombo · button. Address things byeNref,#identifier,"label", orx y.The reply carries the settled UI diff— no follow-upuineeded.
- Verify—testa assert <sel> [exists|gone|value=…|label=…](exit 0/1),testa wait <sel> [gone] [timeoutMs]. Both — andfind— fall back to OCR when the tree has no match, and say which source answered:PASS exists (ocr) "Settings" @200,703.--ocrskips the tree entirely.
- Keep it—testa flow record save smoke.flow, then CI replays it forever.
$ testa ui 25 elements (on screen) e1 Application "Testa Native" @201,437 e5 Button "Tap me" #tapButton @102,171 e16 TextField #textInput =type here @201,673 … $ testa tap "#tapButton" tapped e5 Button Tap me -- ui changes -- ~ e2 StaticText "tap:1" #status @197,86 ~ e6 StaticText "count: 1" #tapCount @41,205 $ testa pinch "#map" 2.0 → pinched $ testa dragdrop "#card" "#trash" → drag-and-dropped $ testa assert "#status" label=done → PASS exists e2 …
Observe ui [diff|full] on-screen snapshot (diff = changes, full = incl. off-screen) see OCR every visible text + tap coords (any app) find <query> [--ocr] elements matching label/id/value/role (OCR fallback) scrollto <sel> scroll until an element is visible (vertical or horizontal) assert <sel> [exists|gone|value=..|label=..] [--ocr] wait <sel> [gone] [timeoutMs] [--ocr] wait until it appears — or disappears audit accessibility audit (labels, 44pt targets, dupes) vdiff <baseline.png> [tolerancePct] visual regression, OCR-aware screenshot [path.png] Act (sel = eN ref · #identifier · "label"; tap falls back to OCR text) tap <sel> | tap <x> <y> | tapocr <text> typein <sel> <text> | type <text> | setvalue <sel> <text> | clear <sel> key <hidUsage> | keycombo <cmd+shift+a> | button <home|lock|siri|apple-pay> swipe|drag|dragdrop <x1 y1 x2 y2> [secs] (also <fromSel> <toSel>) longpress <sel | x y> [secs] | pinch <sel | x y> <scale> | rotate <sel | x y> <radians> App / device devices | boot <udid|name> | shutdown <udid|all> install <app> | terminate <bundle> | apps | open <url> launch <bundle> [--env K=V ...] [--args <a> ...] logs [bundle] [seconds] | crashes [bundle] permission <grant|revoke|reset> <service> <bundle> record <start [path] | stop> Environment push <bundle> <file.json | '{"aps":{"alert":"hi"}}'> location <lat> <lon> | location clear statusbar time 9:41 [battery 100 charged] [wifi 3] [cell 4] | statusbar clear appearance <dark|light> | contentsize <size|increment|decrement> locale <en_US> [lang] (apps need a relaunch to pick it up) addmedia <file...> | pbcopy <text> | pbpaste biometry <enroll|unenroll|match|nomatch> Flows / CI (deterministic replay — no agent, no tokens) flow run <file.flow ...> [--junit <out.xml>] [--artifacts <dir>] [--quiet] flow record start mark "record from here" flow record save <file.flow> [--all] write what you just did as a flow matrix "<dev1,dev2,…>" -- flow run <file.flow ...> Setup / daemon setup | start | stop | status | info | version | mcp (target a sim: --udid <udid>)
…
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.





