Testa

by valewnrt

Not rated
GitHub

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

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, see
bench/).
- 👆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.
- Observetesta ui(on-screen elements) ·testa see(OCR every visible text) ·testa find <q>·testa scrollto <sel>.
- Acttap · 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.
- Verifytesta 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 ittesta 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>)

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.