Your agent writes the E2E tests.
You review and commit.
unotest is AI-native end-to-end testing. Your AI agent drives your real app over MCP and writes clean, reviewable tests in your repo — for web and for iOS.
Works with Claude Code · Cursor · Codex
unotest web
E2E for web apps. Playwright-vocabulary DSL on a sandboxed engine, driven by your agent over MCP.
unotest mobile
E2E for iOS apps (React Native + native Swift). The agent drives the Simulator through the accessibility tree.
Full control
AI does the work. You keep control.
The whole point: speed from the agent, ownership stays with you.
Plain .js in your repo
Tests are ordinary JavaScript in unotest/e2e/*.js. Git, code review, CI — no proprietary format, no binary blob.
No silent fixes
agent_fix composes context and a suggestion — it never calls an LLM itself and never applies a patch on its own. You read the diff and commit.
Runs locally
Everything runs on your machine. Your app never leaves it. No cloud, no account to start.
Safe to run blindly
Scenarios execute in a sandboxed AST interpreter — no require, no fetch, no filesystem. AI-generated tests can run without surprises.
What's new
unotest 0.46.0
Released 2 October 2026.
Assertions that read like Playwright's
assertText takes a regex and {not: true} waits for a label to go away. assertTexts checks a whole list in order, assertAttribute waits for a value such as aria-expanded, and assertSchema checks a response against the JSON Schema your app generates from its zod schemas, naming every broken field.
A clean browser for every test
launch: { profile: 'per-test' } starts each test on a new, empty profile, so an extension, its service worker and its storage start from scratch every time. consoleEntries, pageErrors and assertNoConsoleErrors read what the page logged, and webServer starts a front and its fake backends together.
Tests that decide for themselves
skip('needs LIVE_API', env('LIVE_API') == null) ends a test as skipped with its reason. testTimeoutMs and setTestTimeout give a test a deadline, as in Playwright. A misspelled option is now refused with the list of the right ones, and file reads stay inside the project unless the config lists more.