Write E2E tests in plain language. Replay them for $0.

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
  • No selectors, no test code
  • llm_calls=0 on replay
  • Playwright under the hood
windup run checkout
Terminal recording: a natural-language scenario runs; first run plans with llm_calls=1, the second run replays from cache with llm_calls=0 and $0
Real terminal — first run plans (llm_calls=1), the second run replays from cache at $0.

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

Plan once with the LLM, then replay without it.

The expensive part — figuring out the browser actions — happens a single time and is turned into cached, verifiable data.

  1. 1

    Plan (once)

    A natural-language task goes to the planner. The LLM emits a JSON action plan — one call, on the first run only.

    llm_calls=1 · ~$0.0025
  2. 2

    Execute + verify

    A deterministic executor (Playwright) runs the plan. Cheap DOM/URL post-conditions verify each action — no LLM.

    trusted input events
  3. 3

    Replay (free)

    The trajectory is cached. Every later run replays it: zero LLM calls, ~1 second, $0, same plan every time.

    llm_calls=0 · $0
  • Plans are data, not code — Schema-validated JSON — no runtime improvisation.
  • Verification without LLM — Post-conditions check DOM/URL on every action.
  • Self-healing cache — A failed check invalidates the plan → re-plan.
  • Zero hardcoded site knowledge — The engine knows nothing about your app up front.

Why Windup

The reliability of scripts, the ergonomics of natural language.

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

Everything a QA workflow needs — minus the selectors.

A CLI built for real projects: authoring, secrets, dependencies, cost tracking and CI reporters.

Natural-language scenarios

Describe the test in plain language. No selectors, no page objects, no test code to maintain.

Plan once, replay free

From the 2nd run on: llm_calls=0, ~1s, $0. The cached plan runs the same way every time.

Deterministic execution

Playwright with trusted input events (isTrusted) — reliable clicks, typing and navigation.

Cheap verification

DOM/URL post-conditions checked on every action, with no LLM in the loop.

windup new

LLM-assisted authoring: writes the scenario from your app’s real screens (site map) + project manifest. --validate runs and refines until it passes.

windup scan

Indexes your routes and elements straight from the source (Next.js, react-router).

windup secret

Credentials never enter the scenario, cache, LLM prompt or git — only ENV:* references, resolved at runtime.

depends_on

Dependencies between scenarios (e.g. "create invoice" depends on "login") — same session, cached per dependency.

run --summary

The AI writes a post-run debrief quoting real observed values — prices, messages, confirmations.

run --suggest

On failure, the AI reads the real page and suggests the fix to your scenario.

CI reporters

JUnit, JSON and self-contained HTML (--reporter). Non-zero exit code on any failure.

Multi-provider

Google Gemini and OpenAI, picked per run (--llm openai:gpt-5-mini). windup costs tracks spend by provider and model.

run --concurrency

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.

Cross-browser

Run the same scenarios on Chromium (default), Firefox or WebKit with --browser. A single plan replays across all three — author once, run everywhere.

Example

A scenario is natural language in JSON. Two runs tell the whole story.

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.

checkout.json scenario
{
  "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 output
$ 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

Drop it into your pipeline. Exit codes and reports included.

Run the whole suite in one warm browser, fail the build on any failing scenario, and emit machine- or human-readable reports.

ci — one line bash
$ npx windup run --all --reporter junit \
    --report-file reports/windup.xml
  • --all runs every scenario in a single warm browser for the whole suite.
  • --concurrency <n> runs scenarios in parallel (~2× faster on a mixed suite); --browser firefox|webkit to run cross-browser.
  • Non-zero exit code when any scenario fails.
  • --reporter junit|json|html; windup costs --json tracks AI spend in the pipeline.

Reliability

Deterministic means deterministic.

Replays are measured, not promised. The cached plan produces the same result on every run — no model, no flakes.

60/60
cached replays passed
4 scenarios × 15 replays — zero flakes
llm_calls=0
on every replay
login · multi-step checkout · add/remove · a 2nd site
~$0.0025
per generation
default model, gemini-3.1-flash-lite
106 routes
production dogfood
run against a real production app
Windup mascot holding a green check mark

Get started

Five commands from zero to a replaying test.

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.

Windup mascot running
$ npm i -D windupjs
Chromium is provisioned automatically
$ npx windup init
3 questions → windup.config.ts
$ npx windup scan
indexes your app’s routes from the source
$ npx windup new "log in and create an invoice for ACME"
LLM-assisted authoring
$ npx windup run checkout
1st run plans · then replay ~1s, $0
Node ≥ 20Google Gemini or OpenAIPlaywright bundledMIT

For the AI era

Let your AI wire up the tests.

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.

1

Give your AI the docs

Paste this URL into your assistant (Claude, Cursor, Copilot, …).

https://windup.run/llms.txt
2

Describe what to test

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: ...