Skip to main content
A test mixes two kinds of calls. Agent calls state a goal or ask a question. Deterministic calls pin down the parts that must not drift.
tests/todos.e2e.ts

Goals: agent.act

Hand the model one goal. It plans and executes the steps under a deadline and a model-call budget, then your next line runs.
Real values travel through params, never from the model’s imagination. A Secret param stays host-side: the model sees only its name, and the runner fills the field itself. See Signing in.
Write instructions as you would say them out loudName what is visible and prefer the exact wording on screen. One goal per call. The flow inside a goal is the model’s job; the order of goals is yours.
A step ends passed, failed, or blocked. A block means the step could not reach a product verdict. Configuration blocks such as missing credentials exit 2, and infrastructure blocks such as an unreachable environment exit 3. Budget exhaustion, timeouts, and unsupported automation still count as test failures and exit 1. How agent steps work lists the codes.

Checks: assert, waitFor, extract

assert asks one yes/no question about the current screen. A false answer is an ASSERTION_FAILED with the model’s explanation and a screenshot in the report.
assert looks once. To wait for something to become true, poll with waitFor. In tree-only mode, unchanged observations skip the next judgment. Vision polling judges every interval because pixels can change while the tree stays the same.
extract pulls structured data off the screen so you can branch in TypeScript. Any Standard Schema validator works, zod included. Invalid output gets one repair attempt, then MODEL_OUTPUT_INVALID.
Assert on meaning, not on wordingDifferent models phrase things differently. expect(x.plan).toContain('Pro') survives a model swap; toBe('Pro plan') does not.

Judging pixels

The model reads a redacted text snapshot of the screen: roles, names, text, states. Add vision: true when the answer lives in pixels the snapshot cannot describe, such as a chart or a layout, or vision: 'only' to judge the screenshot alone.
Images cost input tokens on every call. The agent reference lists every mode.

Deterministic APIs: screen and expect

When a step or a check is too important to leave to a model, write it exactly. These calls cost no tokens and never vary.
Two rules keep these honest. A query matching two nodes is an error, not a first-match guess; narrow with .filter(), .first(), or .nth(). Locator assertions retry while direct reads like textContent() do not, so assert with the matcher. Browser-only power lives on the web fixture: routes, cookies, dialogs, frames, downloads. Import test from @e2edev/playwright to have it typed. See the Playwright reference and the screen reference. A resource every test needs, a seeded workspace or a signed-in account, is a fixture of your own: test.extend({ workspace: async ({ web }, use) => { ... } }) returns a test that sets it up before each body and tears it down after, failed or not. See Your own fixtures.

Structure

Common options: timeout (default 120 s), retries, tags (filter with --tag), session (state from a setup test), agentContext (extra context for this test’s agent calls), and agent (which configured agent runs the test). Group options apply to their tests. The test reference has every rule. Tests are independent and start from clean state. When a flow must span several tests, mark the group { serial: true }: members share one app state, run in order on one worker, and retry as a whole.

Next

Signing in

Sessions and secrets.

Agents and personas

Several agents in one suite, and running a flow as each of them.