Natural-language scenarios
Describe the test in plain language. No selectors, no page objects, no test code to maintain.
The LLM plans the browser actions once. From the second run on, Windup does a deterministic replay with zero LLM calls — ~1 second, $0, stable results. You edit scenarios, not selectors.
$ npm i -D windupjs
The problem
Hand-written E2E tests break every time a selector moves. AI agents that drive the browser on every run are slow, non-deterministic, and bill you an LLM call per test.
Windup plans once and caches a validated action plan. Replays are deterministic and free. When the app changes and a check fails, the plan is invalidated and re-planned automatically — self-healing, not silently wrong.
How it works
The expensive part — figuring out the browser actions — happens a single time and is turned into cached, verifiable data.
A natural-language task goes to the planner. The LLM emits a JSON action plan — one call, on the first run only.
A deterministic executor (Playwright) runs the plan. Cheap DOM/URL post-conditions verify each action — no LLM.
The trajectory is cached. Every later run replays it: zero LLM calls, ~1 second, $0, same plan every time.
Why Windup
Hand-written scripts are cheap to run but expensive to maintain. Per-run AI agents are easy to write but slow and non-deterministic. Windup takes the good half of each.
| Dimension | Hand-written scripts | AI agent every run | Windup |
|---|---|---|---|
| Authoring | Code + selectors | Natural language | Natural language |
| Cost per run | $0 | LLM on every run | LLM on the 1st run only |
| Speed | Fast | Slow (model in the loop) | ~1s replay |
| Determinism | High | Low (improvises) | High (same plan every time) |
| App changed | You fix the script | May do the wrong thing silently | Check fails → re-plans |
Features
A CLI built for real projects: authoring, secrets, dependencies, cost tracking and CI reporters.
Describe the test in plain language. No selectors, no page objects, no test code to maintain.
From the 2nd run on: llm_calls=0, ~1s, $0. The cached plan runs the same way every time.
Playwright with trusted input events (isTrusted) — reliable clicks, typing and navigation.
DOM/URL post-conditions checked on every action, with no LLM in the loop.
LLM-assisted authoring: writes the scenario from your app’s real screens (site map) + project manifest. --validate runs and refines until it passes.
Indexes your routes and elements straight from the source (Next.js, react-router).
Credentials never enter the scenario, cache, LLM prompt or git — only ENV:* references, resolved at runtime.
Dependencies between scenarios (e.g. "create invoice" depends on "login") — same session, cached per dependency.
The AI writes a post-run debrief quoting real observed values — prices, messages, confirmations.
On failure, the AI reads the real page and suggests the fix to your scenario.
JUnit, JSON and self-contained HTML (--reporter). Non-zero exit code on any failure.
Google Gemini and OpenAI, picked per run (--llm openai:gpt-5-mini). windup costs tracks spend by provider and model.
Run scenarios in parallel over one shared warm browser with isolated contexts — ~2× faster on a mixed suite, more with planning or long flows. Sequential by default.
Run the same scenarios on Chromium (default), Firefox or WebKit with --browser. A single plan replays across all three — author once, run everywhere.
Example
You write the intent. The first run plans and pays a few tenths of a cent; every run after that is a cached replay at $0.
{
"scenario_id": "checkout",
"task": "Log in as the qa account, add 'Backpack' to the cart, check out and verify the order confirmation message appears."
} $ windup run checkout # 1st run
PASS checkout cache=miss llm_calls=1 total=3003ms cost=$0.0024
$ windup run checkout # again — deterministic replay
PASS checkout cache=hit llm_calls=0 total=671ms cost=$0 cache=hit · llm_calls=0 · $0 — the second run never touches the model.
CI / CD
Run the whole suite in one warm browser, fail the build on any failing scenario, and emit machine- or human-readable reports.
$ npx windup run --all --reporter junit \
--report-file reports/windup.xml Reliability
Replays are measured, not promised. The cached plan produces the same result on every run — no model, no flakes.
Get started
Node ≥ 20 and one API key — GOOGLE_GENERATIVE_AI_API_KEY (Google, default) or OPENAI_API_KEY (OpenAI) — in .env.local. Keys are used only for planning; cached replays never call an LLM.
$ npm i -D windupjs $ npx windup init $ npx windup scan $ npx windup new "log in and create an invoice for ACME" $ npx windup run checkout For the AI era
Nobody reads docs anymore — they hand them to an assistant. So Windup ships an llms.txt: the whole documentation, structured for machines. Point your coding agent at it, describe the flows in plain words, and it authors and runs the scenarios for you.
Paste this URL into your assistant (Claude, Cursor, Copilot, …).
https://windup.run/llms.txt A ready-to-paste starting prompt — fill in your flows.
Read https://windup.run/llms.txt and set up Windup E2E tests for my app, then write scenarios for these flows: ... llms-full.txt — the entire docs in one file · per-page markdown at /docs/<page>.md · the llms.txt standard