Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

e2e — Claude Code skill: what it does, how to install

Writes, runs, and debugs web and mobile end-to-end tests with the e2e test runner, combining agent goals with exact Playwright assertions and device checks.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add tester-army/e2e --skill e2e -a claude-code
Open on GitHub

What e2e does

This skill configures and drives end-to-end testing with the e2e runner. Web applications rely on @e2e-dev/web backed by Playwright, while iOS simulators and Android emulators run through @e2e-dev/mobile. Test suites can mix high-level goals like agent.act and agent.assert with exact assertions using screen, browser, and expect.

It can also drive live running applications over the e2e mcp server. That lets agents inspect screen elements, verify locators with locate, capture screenshots, or explore paths using e2e explore without an existing test file. When tests fail, the workflow inspects trace pages saved in .e2e/results/<test>/trace.md.

Keep in mind that agent.* steps require an AI model in e2e.config.ts and active credentials such as AI_GATEWAY_API_KEY, but locator-only tests run without any model.

What it helps with

  • write end-to-end tests combining agent.act goals with Playwright checks
  • run UI tests on iOS simulators and Android emulators
  • inspect live app screens and locators over e2e mcp
  • hunt for UI bugs with exploratory e2e explore sessions
  • debug failed test steps using generated trace.md logs

Example prompts for the agent

  • Set up e2e.config.ts for our web app and write a billing upgrade test
  • Connect to e2e mcp, inspect /settings/billing, and verify the button locator
  • Debug the failing test in tests/billing.e2e.ts using the trace report

How to install e2e

1Skills CLI (Vercel)

npx skills add tester-army/e2e --skill e2e -a claude-code

2Manually: put the files in place

~/.claude/skills/e2e/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/e2e/

Similar

rewardkit harbor-framework A skill for writing Harbor task verifiers with Reward Kit: programmatic criteria, LLM judges and a single reward score built from a tests/ directory. Claude Code CX Codex Cursor +1 1,300installs debug-mantra thananon Sets a debugging sequence from reproducing a failure through testing hypotheses and recording experiments. Use it for errors, logs, and stack traces. Claude Code CX Codex Cursor +1 4,800installs ads-test AgriciDaniel Walks a paid-media experiment from hypothesis to readout: randomization unit, sample size, stopping rule, and a plain-language decision. Claude Code CX Codex Cursor +1 3,600installs post-mortem thananon Skill writes an engineering post-mortem after debugging: root cause with code identifiers, symptom, mechanism, the fix, and why it slipped through. Use after validation, before closing the ticket. Claude Code CX Codex Cursor +1 4,700installs legacy-modernizer https://github.com/Jeffallan Plans incremental legacy-code migration from dependency maps to facades, flags, and rollback steps. Use it when splitting a monolith or upgrading technology without a wholesale replacement. Claude Code CX Codex Cursor +1 4,300installs stream-chain ruvnet Chains sequential agent steps together using streaming JSON flow and provides ready-to-run pipelines. Claude Code CX Codex Cursor +1 1,200installs improve-codebase-architecture Matt Pocock This skill scans a codebase for architectural friction, then builds a visual HTML report with refactoring candidates. Use it before a deeper architecture review. Claude Code 1,163,035installs design-taste-frontend Leonxlnx This skill directs the design of public pages and work screens, then checks the result in a browser through screenshots. Use it when an interface feels generic or unfinished. Claude Code CX Codex Cursor +1 673,100installs

Questions about e2e

How do I install e2e in Claude Code?

Run npx skills add tester-army/e2e --skill e2e -a claude-code. You can also store the file directly at ~/.claude/skills/e2e/SKILL.md.

What does the e2e skill do?

It guides the coding agent in authoring, running, and fixing browser and mobile UI tests using the e2e test runner.

Do I need an AI API key to run e2e tests?

An API key such as AI_GATEWAY_API_KEY is only required when your tests contain agent.* steps. Tests that rely purely on screen and expect do not require an AI model.

Source text

SKILL.md · Apache-2.0
---
name: e2e
description: Writes, runs, and debugs end-to-end tests for web and mobile apps with e2e, the agentic test runner, which mixes agent.act goals with exact locator assertions on a Playwright browser or an iOS or Android device. Also drives a project's running app live over the e2e MCP server to look at a screen, check a UI change, take a screenshot or record a video, and try a locator; and runs bug bashes, parallel e2e explore runs with each bug proven by a repro test. Use when a project depends on e2e or has an e2e.config.ts, when asked for end-to-end, browser, mobile, or agentic UI tests, to check a change in the running app, to QA or bug bash an app or branch, or when an e2e run fails.
---

# e2e: agentic end-to-end tests

e2e runs UI tests that mix agent goals with exact steps. `agent.act` drives
one goal; `agent.assert`, `agent.waitFor`, and `agent.extract` judge the
screen; `screen`, `app`, `browser`, and `expect` act and check exactly. UI
targets use `@e2e-dev/web` for browsers or `@e2e-dev/mobile` for iOS
simulators, Android emulators, and connected phones.

## Start here: pick the reference for the job

Read the one reference that matches the request before doing anything else.
Each stands on its own; a reference names another topic only for a side
trip. `npx e2e guide <topic>` prints the installed package's version of a
reference; prefer it when it differs from this copy.
The Rules and Feedback sections below still apply.

| When you are | Topic | Read |
| --- | --- | --- |
| Adding e2e to a project, writing `e2e.config.ts`, starting the app from the config, signing in to a model, setting up mobile targets | `setup` | [references/setup.md](references/setup.md) |
| Looking at the running app, checking a UI change, taking a screenshot or video, or trying a locator before writing it, over `e2e mcp` | `mcp` | [references/mcp.md](references/mcp.md) |
| Writing or fixing a test: fixtures, locators, actions, matchers, sign-in sessions, the `browser` fixture, API-only tests | `writing-tests` | [references/writing-tests.md](references/writing-tests.md) |
| Adding `agent.*` steps, picking a model, cutting cost, budgets, the replay cache | `agent` | [references/agent.md](references/agent.md) |
| Running tests: CLI flags, CI, reporters, trace pages, `report.json` for scripts, exit codes | `running` | [references/running.md](references/running.md) |
| Handling a failed run: error codes and their fixes, `--headed`, `--debug`, `--ai-trace` | `debugging` | [references/debugging.md](references/debugging.md) |
| Exploring an app toward a goal without a test file: `e2e explore`, its budgets and verdict | `explore` | [references/explore.md](references/explore.md) |
| Asked to bug bash, QA, or hunt for bugs across an app or a branch | `bug-bash` | [references/bug-bash.md](references/bug-bash.md) |

For anything the references do not cover, the full documentation ships in
the `docs/` directory of the installed `e2e` package
(`node_modules/e2e/docs` in a single-package project); a link such as
`/reference/cli` is `docs/reference/cli.mdx`. Complete projects for Vite,
Next.js, Astro, Expo, SwiftUI, Jetpack Compose, Kotlin Multiplatform,
Flutter, and .NET MAUI are in https://github.com/tester-army/e2e/tree/main/examples.

## Quick start

A config with one web target and a model, and a test that drives one goal
and pins the outcome:

```ts
// e2e.config.ts
import type { E2EConfig } from 'e2e';
import { web } from '@e2e-dev/web';
import { gateway } from 'ai';

export default {
  targets: [
    {
      engine: web(),
      app: {
        url: 'http://127.0.0.1:3000',
        command: { executable: 'pnpm', args: ['dev'], log: '.e2e/logs/app.log' },
      },
    },
  ],
  // The model behind every agent.* step: an AI SDK instance; gateway() from 'ai' reads AI_GATEWAY_API_KEY or a Vercel OIDC token.
  agents: {
    default: {
      model: gateway('openai/gpt-6-luna-fast'),
      system: 'You are a thorough QA agent. Verify every outcome on screen.',
    },
  },
} satisfies E2EConfig;
```

```ts
// tests/billing.e2e.ts
import { test } from '@e2e-dev/web';
import { expect } from 'e2e';

test('a member upgrades to Pro', async ({ app, agent, screen, browser }) => {
  await app.open('/settings/billing');
  await agent.act('upgrade the workspace to the Pro plan');
  await expect(screen.getByRole('status')).toContainText('Pro');
  await expect(browser).toHaveURL('/settings/billing');
});
```

## Workflow: writing a test

1. Look at what exists: `e2e.config.ts`, `.mts`, `.js`, or `.mjs`, the `tests` glob
   (default `tests/**/*.e2e.ts`), `e2e` in `package.json`. Nothing there:
   follow `setup` when the user has requested e2e (see Rules).
2. Learn the screens before writing a test: routes, labels, roles, button
   text. Semantic locators need the accessible names the app renders, so read
   the components, open the page with `--headed`, or drive the live app over
   a connected `e2e mcp` server (topic `mcp`): `open_session`, `observe`,
   and `locate` show exact names and check a locator before you write it,
   and each action prints the test line that repeats it.
3. Write `tests/<feature>.e2e.ts`. Drive the flow with `agent.act`, one
   atomic goal per call, and pin each outcome right after with `expect` or
   `agent.assert`. A test with three goals makes three calls.
   Start it on the screen it covers, since every rerun pays for the steps
   before: in a browser a saved session instead of the sign-in form and
   `app.open` on that screen's path, on a device `device.openLink`, and data
   from the app's API (topic `writing-tests`).
   Exact values go through `screen`: a sign-in form in a setup test, a field
   that must receive one specific string, a count that must be one number.
4. Run only the test you are working on:
   `npx e2e run tests/<feature>.e2e.ts:<line>`, the line its `test(` opens
   on, with the flags in "Rerunning a test while you fix it" below. Agent
   steps need a model in the config and that provider's authentication (a
   saved subscription login, an API key); a local endpoint may need none.
   Tests without agent steps need no model.
5. When it fails, fix it from what the run recorded (next section).

## Workflow: fixing a failed test

The run already saw every screen up to the failure. Fix from what it
recorded; never walk the flow again from the start over MCP just to see a
screen.

1. Read the failure block in the terminal: the failing line with its code
   frame, the message (an assertion's `expected` and `observed`), where the
   screen was (`❯ at`: a URL on the web, the app and screen title on a
   device), and the nodes nearest a failed locator (`❯ on screen`). Then
   read the trace page it names (`❯ trace .e2e/results/<test>/trace.md`):
   every step with what it did, what the app logged, the agent's turns, and
   `## Screen at failure`, the screen one node per line (topic `debugging`).
2. Fix every line that evidence covers, not only the one that failed: the
   screen at failure shows the names the rest of that screen's lines need.
   Fix the locator, the expectation, or the app. Never add a sleep or a
   fixed delay (`setTimeout`, `waitForTimeout`): wait for the state you
   need with `agent.waitFor('<that state>')` instead (topic `agent`; it
   needs a model).
3. A test stops at its first failed step (an `expect.soft` lets it go on),
   and every wrong line after it costs another run. Before rerunning, check
   the lines after it as far as you can: against the screen at failure, the
   app source, or a live session.
4. For a screen the evidence and the source leave out, look live (topic
   `mcp`): open or reuse a session, go straight to that screen, then
   `observe` and `locate`. On the web, `navigate` to the path of the `❯ at`
   URL or the screen's route, and tap only to reach a state no URL opens. A
   device session starts on the app's launch screen: take the shortest path
   from there.
5. Rerun only that test, the way the next section says (topic `mcp` says
   when a session must close first). Repeat with the next failure.

## Rerunning a test while you fix it

Every run of a failing test waits out its failure, and every `agent.act`
that runs live spends model calls. Keep each rerun to the one test and the
lines you changed:

- Run by `file:line`, not the file or the suite. After an edit above the
  test, look its `test(` line up again: a stale line runs another test, or
  fails with `NO_TESTS` listing the lines tests start at.
- Add `--action-timeout 5000`: a locator that matches nothing fails in 5 s
  instead of the default 30 s. It also bounds each action inside an agent
  step, so keep it longer than the slowest screen takes to load. A `screen`
  call's own `{ timeout }` still wins. An `expect` already gives up after
  5 s by default (`assertionTimeout`).
- Keep the replay cache on. A failed run keeps the recording of every goal
  whose check passed, and the next run replays those with no model call.
  New, changed, and failed goals run live, and so does a replay the trace
  page shows as handed off. `agent.assert`, `agent.waitFor`, and
  `agent.extract` always call the model. Leave `--no-cache` off: it sends every goal to the model
  again. Only a `read-write` cache records: the local default, but not with
  `CI` set and no cache mode in the config, nor in strict mode. A stopped
  run (Ctrl-C, a command your tool's timeout kills, `--max-duration`) saves
  nothing, so give the command a timeout longer than the test.
- Don't rename the test, its `describe`, or its file, and don't reword a
  goal, change its params, or change the agent's `context` or the test's
  `agentContext` once a goal passes. Each is part of a recording's key, so
  every goal it touches records again live. Fixing a locator or an
  expectation keeps the recordings.
- Add `--retries 0` when retries are on (`retries` in the config, or `CI`
  set, where the default is 1); a `retries` option on the test or its
  `describe` wins over the flag. Outside strict mode (`--strict-cache`, `cache.strict`), a
  retry never replays: it runs every goal live and waits out the failure
  again.
- Once the test passes, drop `--action-timeout` and run it one time with
  `--no-cache`: every goal runs live at the config's timeouts, which shows
  the agent still reaches it without its recordings.

## Rules

- In a project without e2e, set it up when the user requests it. Otherwise
  ask before adding a test framework, and name any existing framework.
- Live MCP tools need the project's own `e2e mcp` server registered in the
  client (topic `mcp`); writing and running tests use the CLI directly.
  Keep a session open across runs while you write or fix a web test; topic
  `mcp` says when a run can go beside it, and what to do on
  `APP_ALREADY_RUNNING`.
- Run the CLI as `npx e2e ...` (or `pnpm exec e2e ...`).
- The config is `export default { ... } satisfies E2EConfig` with
  `import type { E2EConfig } from 'e2e'`. `targets` is required; a UI target
  names an engine and declares the app beside it: `{ engine: web(), app: { url, command } }`.
  A tools-only target can omit the engine and set `platform`.
- Import `test`, `describe`, the hooks, `expect`, `credentials`, and
  `secrets` from `e2e`. A test that uses the `browser` fixture imports `test`,
  `describe`, and the hooks from `@e2e-dev/web`: the same runtime functions,
  typed with `browser`.
- Config and tests are ES modules whatever `package.json` sets as `type`.
- Locators resolve when used. Actions wait for readiness and `expect` retries
  assertions. Reads such as `textContent()` wait for one match until the
  action timeout, then answer the current value; `count()` and `isVisible()`
  answer from the current screen. Nothing waits for a value to change, so use
  a matcher when a value has to settle.
- A locator that matches two nodes fails with `LOCATOR_AMBIGUOUS`; narrow it
  (topic `writing-tests`).
- Secrets never appear in test code. Declare accounts under `credentials` and
  every other sensitive value under `secrets` in the config; resolve with
  `credentials.user(name).password` or `secrets.get(name)` (separate
  namespaces: `secrets.get` never returns a password), and hand the opaque
  `Secret` only to `fill()` or `agent.act` params.
- Agent instructions: one goal per `act`, the wording on screen, real values
  in params. A goal is atomic: one outcome that one check confirms. Two
  outcomes worth checking are two goals, so split them into two `act` calls
  (topic `agent`). Bad: `'add a todo and mark it done'`. Good: `'add a
  todo'`, check, then `'mark the todo as done'`, check. Judge meaning, not
  phrasing: `toContain('Pro')`, not an exact sentence a model produced.
- Check each agent goal's outcome. A passing `act` with a recorded check can
  be cached and replayed without model calls (topic `agent`).
- Shape the agent for this app: `context` for vocabulary the screens use,
  `system` for how it works, tools for a test API, named personas under
  `agents`. When a step fails, tighten the goal first, then the context, then
  the agent.
- `.e2e/` is output (`report.json`, `results/`, `cache/`, `logs/`; the
  config's `output` moves the report and artifacts, never `cache/` or the
  app's log). Read it, never edit it.

## Feedback

When e2e itself gets in your way, offer to tell the e2e team: a command or
API that broke (`bug`), docs or this skill that misled you (`docs`), or a
capability you needed and did not find (`feature`). Once per problem, after
you worked around it or gave up. Never report:

- failures of the app under test, or its own behavior;
- a problem you saw once and could not see again;
- anything on an outdated e2e: the command refuses it, so upgrade
  (`npx e2e@latest upgrade`) and check the problem still happens first.

Search [existing issues](https://github.com/tester-army/e2e/issues) first
(`gh issue list -R tester-army/e2e --search "<words>"`); comment on a match
instead of sending a new report.

A `bug` needs `--repro`, steps that reproduce it every time without app
content. Without such steps, or when the bug needs your app to show, omit
`--repro`: the command then prints a prefilled GitHub issue and sends
nothing; show the user the link and tell them to add a minimal reproduction
and submit it. Never file the issue yourself.

Run it with `--dry-run` first, show the user what would be sent, and send
it only when they agree.

```bash
npx e2e feedback --type bug -m "<one or two sentences>" \
  --repro "<steps that reproduce it every time>" --error-code LOCATOR_AMBIGUOUS \
  --task "<what you were doing>" --expected "<...>" --actual "<error message>" \
  --approach "<what you tried>" --command "<e2e command>" --agent "<agent / model>"
```

Describe e2e's behavior only, briefly: never paste app content, page text,
test files, URLs of private apps, or credentials. Secret-named environment
variables and common token shapes are redacted, but do not rely on it. After
sending, give the user the reference id it prints.

© tester-army · Apache-2.0 · tester-army/e2e

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor