@e2edev/e2e 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/agent/act.d.ts +13 -7
- package/dist/agent/act.d.ts.map +1 -1
- package/dist/agent/act.js +125 -730
- package/dist/agent/act.js.map +1 -1
- package/dist/agent/action-dispatcher.d.ts +98 -0
- package/dist/agent/action-dispatcher.d.ts.map +1 -0
- package/dist/agent/action-dispatcher.js +319 -0
- package/dist/agent/action-dispatcher.js.map +1 -0
- package/dist/agent/actions.d.ts +16 -1
- package/dist/agent/actions.d.ts.map +1 -1
- package/dist/agent/actions.js +5 -1
- package/dist/agent/actions.js.map +1 -1
- package/dist/agent/default-agent.d.ts +1 -1
- package/dist/agent/default-agent.d.ts.map +1 -1
- package/dist/agent/default-agent.js +30 -9
- package/dist/agent/default-agent.js.map +1 -1
- package/dist/agent/error.d.ts.map +1 -1
- package/dist/agent/error.js +1 -0
- package/dist/agent/error.js.map +1 -1
- package/dist/agent/executor.d.ts +33 -5
- package/dist/agent/executor.d.ts.map +1 -1
- package/dist/agent/executor.js.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/invocation.d.ts +4 -9
- package/dist/agent/invocation.d.ts.map +1 -1
- package/dist/agent/invocation.js +1 -6
- package/dist/agent/invocation.js.map +1 -1
- package/dist/agent/model/sdk.d.ts +1 -1
- package/dist/agent/model/sdk.js +1 -1
- package/dist/agent/observation-feed.d.ts +123 -0
- package/dist/agent/observation-feed.d.ts.map +1 -0
- package/dist/agent/observation-feed.js +281 -0
- package/dist/agent/observation-feed.js.map +1 -0
- package/dist/agent/observation.d.ts +14 -1
- package/dist/agent/observation.d.ts.map +1 -1
- package/dist/agent/observation.js +58 -2
- package/dist/agent/observation.js.map +1 -1
- package/dist/agent/operation-queue.d.ts +21 -0
- package/dist/agent/operation-queue.d.ts.map +1 -0
- package/dist/agent/operation-queue.js +27 -0
- package/dist/agent/operation-queue.js.map +1 -0
- package/dist/agent/phases.js +1 -1
- package/dist/agent/phases.js.map +1 -1
- package/dist/agent/pixels.d.ts +21 -0
- package/dist/agent/pixels.d.ts.map +1 -0
- package/dist/agent/pixels.js +85 -0
- package/dist/agent/pixels.js.map +1 -0
- package/dist/agent/point-tap.d.ts +52 -0
- package/dist/agent/point-tap.d.ts.map +1 -0
- package/dist/agent/point-tap.js +88 -0
- package/dist/agent/point-tap.js.map +1 -0
- package/dist/agent/primitives.d.ts +7 -1
- package/dist/agent/primitives.d.ts.map +1 -1
- package/dist/agent/primitives.js +99 -27
- package/dist/agent/primitives.js.map +1 -1
- package/dist/agent/replay.d.ts +8 -3
- package/dist/agent/replay.d.ts.map +1 -1
- package/dist/agent/replay.js +44 -15
- package/dist/agent/replay.js.map +1 -1
- package/dist/agent/screen-update.d.ts +50 -18
- package/dist/agent/screen-update.d.ts.map +1 -1
- package/dist/agent/screen-update.js +76 -94
- package/dist/agent/screen-update.js.map +1 -1
- package/dist/agent/secrets.d.ts.map +1 -1
- package/dist/agent/secrets.js +13 -9
- package/dist/agent/secrets.js.map +1 -1
- package/dist/agent/step-accounting.d.ts +106 -0
- package/dist/agent/step-accounting.d.ts.map +1 -0
- package/dist/agent/step-accounting.js +216 -0
- package/dist/agent/step-accounting.js.map +1 -0
- package/dist/agent/step-cache.d.ts.map +1 -1
- package/dist/agent/step-cache.js +2 -2
- package/dist/agent/step-cache.js.map +1 -1
- package/dist/agent/tool-loop.d.ts.map +1 -1
- package/dist/agent/tool-loop.js +9 -2
- package/dist/agent/tool-loop.js.map +1 -1
- package/dist/agent/transcript-compaction.d.ts +38 -0
- package/dist/agent/transcript-compaction.d.ts.map +1 -0
- package/dist/agent/transcript-compaction.js +165 -0
- package/dist/agent/transcript-compaction.js.map +1 -0
- package/dist/cache/recorder.d.ts.map +1 -1
- package/dist/cache/recorder.js +20 -0
- package/dist/cache/recorder.js.map +1 -1
- package/dist/cache/trace.d.ts +28 -1
- package/dist/cache/trace.d.ts.map +1 -1
- package/dist/cache/trace.js +44 -0
- package/dist/cache/trace.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +55 -32
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/engine-versions.json +1 -1
- package/dist/cli/init/engines.d.ts +29 -10
- package/dist/cli/init/engines.d.ts.map +1 -1
- package/dist/cli/init/engines.js +34 -12
- package/dist/cli/init/engines.js.map +1 -1
- package/dist/cli/init/gateways.js +3 -3
- package/dist/cli/init/mcp-config.js +1 -1
- package/dist/cli/init/mcp-config.js.map +1 -1
- package/dist/cli/init/scaffold.d.ts.map +1 -1
- package/dist/cli/init/scaffold.js +1 -3
- package/dist/cli/init/scaffold.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +3 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/collect/collect.d.ts +8 -3
- package/dist/collect/collect.d.ts.map +1 -1
- package/dist/collect/collect.js +29 -5
- package/dist/collect/collect.js.map +1 -1
- package/dist/collect/registry.d.ts +20 -5
- package/dist/collect/registry.d.ts.map +1 -1
- package/dist/collect/registry.js +139 -54
- package/dist/collect/registry.js.map +1 -1
- package/dist/collect/select.js +3 -1
- package/dist/collect/select.js.map +1 -1
- package/dist/config/agent.d.ts +1 -7
- package/dist/config/agent.d.ts.map +1 -1
- package/dist/config/agent.js +5 -8
- package/dist/config/agent.js.map +1 -1
- package/dist/config/app.d.ts +24 -2
- package/dist/config/app.d.ts.map +1 -1
- package/dist/config/app.js +82 -15
- package/dist/config/app.js.map +1 -1
- package/dist/config/diagnose.d.ts.map +1 -1
- package/dist/config/diagnose.js +2 -0
- package/dist/config/diagnose.js.map +1 -1
- package/dist/config/resolve.d.ts +26 -3
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +66 -23
- package/dist/config/resolve.js.map +1 -1
- package/dist/config/secrets.d.ts +89 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +67 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/engine/contract.d.ts +17 -2
- package/dist/engine/contract.d.ts.map +1 -1
- package/dist/engine/contract.js.map +1 -1
- package/dist/engine/index.d.ts +18 -4
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +8 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/session.d.ts.map +1 -1
- package/dist/engine/session.js +3 -0
- package/dist/engine/session.js.map +1 -1
- package/dist/engine/surface.d.ts +6 -2
- package/dist/engine/surface.d.ts.map +1 -1
- package/dist/engine/surface.js.map +1 -1
- package/dist/expect/attempt.d.ts +15 -0
- package/dist/expect/attempt.d.ts.map +1 -0
- package/dist/expect/attempt.js +33 -0
- package/dist/expect/attempt.js.map +1 -0
- package/dist/expect/index.d.ts +7 -5
- package/dist/expect/index.d.ts.map +1 -1
- package/dist/expect/index.js +8 -1
- package/dist/expect/index.js.map +1 -1
- package/dist/expect/poll.d.ts +5 -0
- package/dist/expect/poll.d.ts.map +1 -0
- package/dist/expect/poll.js +118 -0
- package/dist/expect/poll.js.map +1 -0
- package/dist/expect/values.d.ts.map +1 -1
- package/dist/expect/values.js +6 -1
- package/dist/expect/values.js.map +1 -1
- package/dist/explore/body.d.ts.map +1 -1
- package/dist/explore/body.js +3 -1
- package/dist/explore/body.js.map +1 -1
- package/dist/explore/executor.js +16 -11
- package/dist/explore/executor.js.map +1 -1
- package/dist/explore/index.d.ts +3 -2
- package/dist/explore/index.d.ts.map +1 -1
- package/dist/explore/index.js +5 -5
- package/dist/explore/index.js.map +1 -1
- package/dist/explore/plan.d.ts.map +1 -1
- package/dist/explore/plan.js +16 -5
- package/dist/explore/plan.js.map +1 -1
- package/dist/explore/progress.d.ts +43 -0
- package/dist/explore/progress.d.ts.map +1 -0
- package/dist/explore/progress.js +9 -0
- package/dist/explore/progress.js.map +1 -0
- package/dist/explore/state.d.ts +13 -12
- package/dist/explore/state.d.ts.map +1 -1
- package/dist/explore/state.js +28 -11
- package/dist/explore/state.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/brands.d.ts +2 -0
- package/dist/internal/brands.d.ts.map +1 -1
- package/dist/internal/brands.js +2 -0
- package/dist/internal/brands.js.map +1 -1
- package/dist/internal/geometry.d.ts +13 -0
- package/dist/internal/geometry.d.ts.map +1 -0
- package/dist/internal/geometry.js +16 -0
- package/dist/internal/geometry.js.map +1 -0
- package/dist/internal/urls.d.ts +6 -0
- package/dist/internal/urls.d.ts.map +1 -1
- package/dist/internal/urls.js +28 -0
- package/dist/internal/urls.js.map +1 -1
- package/dist/locator/engine.d.ts.map +1 -1
- package/dist/locator/engine.js +1 -0
- package/dist/locator/engine.js.map +1 -1
- package/dist/mcp/catalog.d.ts +5 -4
- package/dist/mcp/catalog.d.ts.map +1 -1
- package/dist/mcp/catalog.js +5 -36
- package/dist/mcp/catalog.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +1 -1
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +12 -6
- package/dist/mcp/session.js.map +1 -1
- package/dist/report/builtin.d.ts +14 -0
- package/dist/report/builtin.d.ts.map +1 -0
- package/dist/report/builtin.js +22 -0
- package/dist/report/builtin.js.map +1 -0
- package/dist/report/explore-text.d.ts +17 -0
- package/dist/report/explore-text.d.ts.map +1 -0
- package/dist/report/explore-text.js +37 -0
- package/dist/report/explore-text.js.map +1 -0
- package/dist/report/format.d.ts +2 -0
- package/dist/report/format.d.ts.map +1 -1
- package/dist/report/format.js +1 -1
- package/dist/report/format.js.map +1 -1
- package/dist/report/junit.d.ts +2 -4
- package/dist/report/junit.d.ts.map +1 -1
- package/dist/report/junit.js +7 -40
- package/dist/report/junit.js.map +1 -1
- package/dist/report/list-explore.d.ts +109 -0
- package/dist/report/list-explore.d.ts.map +1 -0
- package/dist/report/list-explore.js +368 -0
- package/dist/report/list-explore.js.map +1 -0
- package/dist/report/list.d.ts +21 -0
- package/dist/report/list.d.ts.map +1 -1
- package/dist/report/list.js +79 -3
- package/dist/report/list.js.map +1 -1
- package/dist/report/markdown.d.ts +39 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +426 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/outcome.d.ts +29 -0
- package/dist/report/outcome.d.ts.map +1 -0
- package/dist/report/outcome.js +36 -0
- package/dist/report/outcome.js.map +1 -0
- package/dist/report/running-tree.d.ts +7 -7
- package/dist/report/running-tree.d.ts.map +1 -1
- package/dist/report/running-tree.js +12 -13
- package/dist/report/running-tree.js.map +1 -1
- package/dist/report/write.d.ts +10 -1
- package/dist/report/write.d.ts.map +1 -1
- package/dist/report/write.js +22 -1
- package/dist/report/write.js.map +1 -1
- package/dist/run/app-ports.d.ts +17 -0
- package/dist/run/app-ports.d.ts.map +1 -0
- package/dist/run/app-ports.js +74 -0
- package/dist/run/app-ports.js.map +1 -0
- package/dist/run/events.d.ts +10 -5
- package/dist/run/events.d.ts.map +1 -1
- package/dist/run/events.js.map +1 -1
- package/dist/run/execute.d.ts.map +1 -1
- package/dist/run/execute.js +24 -7
- package/dist/run/execute.js.map +1 -1
- package/dist/run/extended-fixtures.d.ts +33 -0
- package/dist/run/extended-fixtures.d.ts.map +1 -0
- package/dist/run/extended-fixtures.js +91 -0
- package/dist/run/extended-fixtures.js.map +1 -0
- package/dist/run/fixtures.d.ts.map +1 -1
- package/dist/run/fixtures.js +39 -11
- package/dist/run/fixtures.js.map +1 -1
- package/dist/run/managed-process.d.ts +18 -0
- package/dist/run/managed-process.d.ts.map +1 -1
- package/dist/run/managed-process.js +101 -6
- package/dist/run/managed-process.js.map +1 -1
- package/dist/run/realm.d.ts +5 -4
- package/dist/run/realm.d.ts.map +1 -1
- package/dist/run/realm.js +8 -7
- package/dist/run/realm.js.map +1 -1
- package/dist/run/runner.d.ts +9 -3
- package/dist/run/runner.d.ts.map +1 -1
- package/dist/run/runner.js +15 -12
- package/dist/run/runner.js.map +1 -1
- package/dist/run/secrecy.d.ts +3 -3
- package/dist/run/secrecy.d.ts.map +1 -1
- package/dist/run/secrecy.js +4 -4
- package/dist/run/secrecy.js.map +1 -1
- package/dist/run/standalone.js +6 -6
- package/dist/run/standalone.js.map +1 -1
- package/dist/run/worker/entry.js +4 -3
- package/dist/run/worker/entry.js.map +1 -1
- package/dist/run/worker/protocol.d.ts +7 -1
- package/dist/run/worker/protocol.d.ts.map +1 -1
- package/dist/run/worker/protocol.js.map +1 -1
- package/dist/secrets.d.ts +21 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +66 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +90 -50
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -3
- package/skills/e2e/SKILL.md +42 -25
- package/skills/e2e/references/agent.md +80 -24
- package/skills/e2e/references/debugging.md +6 -5
- package/skills/e2e/references/explore.md +13 -7
- package/skills/e2e/references/mcp.md +4 -4
- package/skills/e2e/references/running.md +32 -21
- package/skills/e2e/references/setup.md +42 -16
- package/skills/e2e/references/writing-tests.md +32 -16
- package/dist/agent/model/router.d.ts +0 -36
- package/dist/agent/model/router.d.ts.map +0 -1
- package/dist/agent/model/router.js +0 -45
- package/dist/agent/model/router.js.map +0 -1
- package/dist/credentials.d.ts +0 -13
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js +0 -51
- package/dist/credentials.js.map +0 -1
- package/dist/explore/reporter.d.ts +0 -11
- package/dist/explore/reporter.d.ts.map +0 -1
- package/dist/explore/reporter.js +0 -54
- package/dist/explore/reporter.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Agent steps
|
|
2
2
|
|
|
3
|
-
`agent` is a fixture like `screen
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`agent` is a fixture like `screen` and the main way a test drives the app.
|
|
4
|
+
Each call is one bounded invocation: a fresh redacted observation, a
|
|
5
|
+
deadline, a model-call budget, and no shared transcript between calls. A
|
|
6
|
+
test with no agent step never loads a model.
|
|
6
7
|
|
|
7
8
|
## Configure a model
|
|
8
9
|
|
|
@@ -15,18 +16,18 @@ import { gateway } from 'ai';
|
|
|
15
16
|
|
|
16
17
|
export default {
|
|
17
18
|
// 1. The built-in agent with an AI SDK model: gateway() from 'ai' is the Vercel AI Gateway and reads AI_GATEWAY_API_KEY.
|
|
18
|
-
agents: { default: createAgent({ model: gateway('openai/gpt-5.
|
|
19
|
+
agents: { default: createAgent({ model: gateway('openai/gpt-5.6-luna'), system: 'You are a thorough QA agent. Verify every outcome on screen.' }) },
|
|
19
20
|
|
|
20
21
|
// 2. An options block: a model plus project vocabulary. openrouter() from '@openrouter/ai-sdk-provider' reads OPENROUTER_API_KEY.
|
|
21
22
|
// agents: { default: { model: openrouter('anthropic/claude-sonnet-4.5'), context: 'A billing dashboard. Plans are Free, Team, and Pro.' } },
|
|
22
23
|
|
|
23
24
|
// 3. A live AI SDK model instance for a provider called directly.
|
|
24
|
-
// agents: { default: createAgent({ model: openai('gpt-5.
|
|
25
|
+
// agents: { default: createAgent({ model: openai('gpt-5.6-luna') }) },
|
|
25
26
|
} satisfies E2EConfig;
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
```bash
|
|
29
|
-
AI_GATEWAY_API_KEY=... npx
|
|
30
|
+
AI_GATEWAY_API_KEY=... npx e2e run
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
- The model is always an AI SDK instance the config constructs; the runner
|
|
@@ -42,7 +43,6 @@ AI_GATEWAY_API_KEY=... npx --no-install e2e run
|
|
|
42
43
|
lazily and fails without it.
|
|
43
44
|
- `context` in the config and `agentContext` on a test or group add trusted
|
|
44
45
|
project vocabulary to every prompt.
|
|
45
|
-
- `visionModel` serves the calls that send pixels.
|
|
46
46
|
- The model passed to `createAgent({ model })` is the one model for every
|
|
47
47
|
`agent.*` call, `act` and the judgments alike.
|
|
48
48
|
An agent `model` naming a different model is `INVALID_CONFIG`.
|
|
@@ -80,8 +80,15 @@ verdict.
|
|
|
80
80
|
Options: `params` (the values the instruction refers to; a `Secret` is filled
|
|
81
81
|
by the runner), `timeout` (default the test timeout), `maxSteps` (default 25
|
|
82
82
|
actions), `maxModelCalls` (default 25). Per-call budgets can only lower the
|
|
83
|
-
configured limits. `act` takes no `schema` and no `vision
|
|
84
|
-
is `extract({ schema })`, and `
|
|
83
|
+
configured limits. `act` takes no `schema` and no `vision` option: structured
|
|
84
|
+
output is `extract({ schema })`, and pixels reach an `act` step through the
|
|
85
|
+
`screenshot` and `tap_at` tools the agent offers while no secret has been
|
|
86
|
+
filled. `screenshot` attaches the viewport's pixels to the result and turns
|
|
87
|
+
on pixel mode, where every action result carries a fresh screenshot; `tap_at`
|
|
88
|
+
taps a point in the latest screenshot (a canvas shape, a map pin, an image
|
|
89
|
+
region, a control in a system sheet), hit-tested against the tree first so a
|
|
90
|
+
listed control is tapped by id. A screen with nothing to tap by id opens with
|
|
91
|
+
a screenshot already attached.
|
|
85
92
|
|
|
86
93
|
## assert, waitFor, extract: one question
|
|
87
94
|
|
|
@@ -142,6 +149,14 @@ await expect(screen.getByRole('status')).toHaveText('Created "Atlas" on the Pro
|
|
|
142
149
|
The check makes the test model-portable (the path may differ between models,
|
|
143
150
|
the end state may not), and it is what lets the trace cache record the step.
|
|
144
151
|
|
|
152
|
+
State the step writes off screen (a database row, an API read) can land after
|
|
153
|
+
`act` returns; poll the read instead of sleeping:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
await agent.act('create a test named "AI checkout regression"');
|
|
157
|
+
await expect.poll(() => getTest(workspace).then((row) => row?.title), { timeout: 15_000 }).toBe('AI checkout regression');
|
|
158
|
+
```
|
|
159
|
+
|
|
145
160
|
## What the model sees
|
|
146
161
|
|
|
147
162
|
A redacted snapshot of the screen (roles, names, text, states), a summary of
|
|
@@ -150,8 +165,9 @@ every action result after it reports what changed, keyed by node ids that
|
|
|
150
165
|
stay stable while an element exists, or the whole screen again when most of
|
|
151
166
|
it changed, and is read after the action's effect landed. Never raw HTML,
|
|
152
167
|
cookies, headers, environment
|
|
153
|
-
values, or a `Secret`'s value; password fields arrive masked. Pixels
|
|
154
|
-
|
|
168
|
+
values, or a `Secret`'s value; password fields arrive masked. Pixels reach a
|
|
169
|
+
model only through `vision` on a judgment or the act loop's `screenshot` and
|
|
170
|
+
pixel mode, masked, and only while no secret has been filled. Nothing the model
|
|
155
171
|
returns runs as code or selectors: the runner validates and authorizes every
|
|
156
172
|
tool call before it executes.
|
|
157
173
|
|
|
@@ -164,8 +180,9 @@ tool call before it executes.
|
|
|
164
180
|
| `extract` | 2 | 30 s |
|
|
165
181
|
| `waitFor` | up to `agent.maxModelCalls` (25) | 30 s |
|
|
166
182
|
|
|
167
|
-
-
|
|
168
|
-
|
|
183
|
+
- With the cache on, a passing `act` costs model calls once and replays on
|
|
184
|
+
later runs until the app changes. Budget for the runs where the UI moved,
|
|
185
|
+
not for every run.
|
|
169
186
|
- Slow providers: raise the test `timeout` and `actionTimeout` (each
|
|
170
187
|
observation and action inside a step is bounded by it) rather than reading
|
|
171
188
|
latency as a defect. `STEP_TIMEOUT` and `STEP_BUDGET_EXHAUSTED` are test
|
|
@@ -178,14 +195,25 @@ tool call before it executes.
|
|
|
178
195
|
Each passing `agent.act` records the actions it performed. The next run
|
|
179
196
|
replays them with zero model calls and hands back to the live agent the
|
|
180
197
|
moment the app no longer matches the recording, or when the recorded end
|
|
181
|
-
state is not on screen after the replay.
|
|
198
|
+
state is not on screen after the replay. Replayed actions run as a test's
|
|
199
|
+
own steps do, without the agent's settle wait, so a replay is as fast as
|
|
200
|
+
the deterministic equivalent.
|
|
182
201
|
|
|
183
202
|
- On by default (`read-write`), `read-only` in CI, `cache: 'off'` in the
|
|
184
203
|
config or `--no-cache` on a run to disable. Entries live in `.e2e/cache/`;
|
|
185
204
|
deleting the directory only slows the next run.
|
|
186
|
-
- An entry is written only after a later verification step passes
|
|
187
|
-
|
|
188
|
-
|
|
205
|
+
- An entry is written only after a later verification step passes: a
|
|
206
|
+
locator or engine `expect` matcher, `locator.waitFor`, `web.waitForURL`,
|
|
207
|
+
`agent.assert`, or `agent.waitFor`. A plain-value `expect`, `expect.poll`,
|
|
208
|
+
`agent.extract`, another `act`, or the attempt passing confirms nothing.
|
|
209
|
+
An `act` nothing checks is never replayed.
|
|
210
|
+
- A replay needs the app on the path the step was recorded on, unless the
|
|
211
|
+
recording opens with a navigation. It re-finds each control by role, name,
|
|
212
|
+
test id, placeholder, and input purpose, and passes on its own only when
|
|
213
|
+
the recorded end path and the controls that appeared during the step are
|
|
214
|
+
back. Otherwise the agent takes over mid-step. The report's
|
|
215
|
+
`step.cache.reason` says why: `no-entry`, `wrong-context`,
|
|
216
|
+
`target-not-found`, `target-ambiguous`, `end-mismatch`, and so on.
|
|
189
217
|
- A step that records no actions creates no entry and skips the cache's
|
|
190
218
|
end-state observation.
|
|
191
219
|
- `e2e init` gitignores `.e2e/cache/`; committing entries is opt-in. Remove
|
|
@@ -197,8 +225,8 @@ state is not on screen after the replay.
|
|
|
197
225
|
## Inspect what the model did
|
|
198
226
|
|
|
199
227
|
```bash
|
|
200
|
-
npx
|
|
201
|
-
npx
|
|
228
|
+
npx e2e run tests/checkout.e2e.ts --debug # step table, transcripts as artifacts
|
|
229
|
+
npx e2e run tests/checkout.e2e.ts --ai-trace # writes .e2e/ai-trace.json
|
|
202
230
|
npx unbox-ai runs .e2e/ai-trace.json # one line per agent step
|
|
203
231
|
npx unbox-ai summary .e2e/ai-trace.json --run 0 # turns, tokens, tool calls of one step
|
|
204
232
|
```
|
|
@@ -211,6 +239,32 @@ The trace replaces inline bytes and base64 in SDK image and file message
|
|
|
211
239
|
parts with decoded byte counts. URLs and text remain; encoded strings in
|
|
212
240
|
arbitrary tool result JSON or other fields are preserved.
|
|
213
241
|
|
|
242
|
+
## Make the agent yours
|
|
243
|
+
|
|
244
|
+
The agent in the config is a starting point. The best agent for an app is
|
|
245
|
+
the one that knows its screens, and that comes from iterating on it:
|
|
246
|
+
|
|
247
|
+
1. **The goal.** A failed step usually means the goal named something the
|
|
248
|
+
screen does not. Reword it with the labels on screen. Check the step's
|
|
249
|
+
transcript with `--debug` to see what the model saw and tried.
|
|
250
|
+
2. **`context`.** Vocabulary every step needs: what the plans are called,
|
|
251
|
+
what a "workspace" is, which tab holds billing. Set it once on the agent
|
|
252
|
+
or per test with `agentContext`, not in every instruction.
|
|
253
|
+
3. **`system` on `createAgent`.** How the agent works: how carefully it
|
|
254
|
+
verifies, what it never does, how it treats a modal. A UX reviewer, a
|
|
255
|
+
cautious QA persona, and a fast smoke agent are three `system` prompts on
|
|
256
|
+
the same model.
|
|
257
|
+
4. **Tools.** A test API the agent may call mid-flow (seed a cart, mint a
|
|
258
|
+
coupon) via `createAgent({ tools })`; see below.
|
|
259
|
+
5. **The model and its options.** `providerOptions` for reasoning effort,
|
|
260
|
+
or a different model for one persona. `npx e2e run --agent <name>` runs
|
|
261
|
+
the suite as any configured agent, so two candidates can be compared on
|
|
262
|
+
the same tests; every result records which agent ran it.
|
|
263
|
+
|
|
264
|
+
Personas are agents by name under `agents`, pinned with `{ agent }` on a
|
|
265
|
+
test or block, or swept with `--agent buyer,admin`. The trace cache records
|
|
266
|
+
per agent step, so a specialised agent gets the same replay benefit.
|
|
267
|
+
|
|
214
268
|
## Beyond the built-in agent
|
|
215
269
|
|
|
216
270
|
- `createAgent({ tools: { seedCart } })` adds AI SDK tools wrapped with
|
|
@@ -226,8 +280,10 @@ Full reference: https://e2e.mintlify.app/agents
|
|
|
226
280
|
|
|
227
281
|
## In CI
|
|
228
282
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
the
|
|
233
|
-
|
|
283
|
+
One suite, one job, on every pull request, agent steps included. Pass the
|
|
284
|
+
key the config's model reads (`env: { AI_GATEWAY_API_KEY }` for `gateway()`)
|
|
285
|
+
from secrets. Commit `.e2e/cache/` so CI replays recorded steps with no
|
|
286
|
+
model call and consults the model only where the app changed; keep every
|
|
287
|
+
`act` followed by a check so the recording is trusted. CI retries once and
|
|
288
|
+
reports a pass-on-retry as flaky, so the report keeps naming the goals that
|
|
289
|
+
need tightening.
|
|
@@ -28,7 +28,7 @@ jq '.run.results[] | select(.status != "passed") | .attempts[-1]
|
|
|
28
28
|
| `CONFIG_NOT_FOUND`, `CONFIG_AMBIGUOUS` | Wrong `--config` path; both `.ts` and `.mts` present | Fix the path; keep one config file |
|
|
29
29
|
| `NO_TESTS` | The glob or a positional matched nothing | The message names each positional that matched nothing. Check `tests` in the config and the `.e2e.ts` suffix |
|
|
30
30
|
| `COLLECTION_ERROR` | `async` describe body, `test.setup` inside `describe`, an option forbidden in a serial group, registration outside collection | Rework the structure per `writing-tests` |
|
|
31
|
-
| `APP_UNREACHABLE` | `command` never answered `readyUrl` within `startupTimeout`; a service exited non-zero |
|
|
31
|
+
| `APP_UNREACHABLE` | `command` never answered `readyUrl` within `startupTimeout`; a service exited non-zero | Read the last log lines quoted under the error; set `command.log` if it says to; check the port and `url`; pass the env the app needs through `command.env`; raise `startupTimeout` |
|
|
32
32
|
| `APP_ALREADY_RUNNING` | Something already serves `url` when the runner wanted to start `command` | Stop it, or set `reuseExisting: true` for local runs |
|
|
33
33
|
| `APP_URL_REQUIRED` | A navigation on an engine without `url` | Add `url` to `playwright({ ... })` |
|
|
34
34
|
| `LOCATOR_NOT_FOUND` | Wrong role or name, text not exact, element off screen or inside an iframe, page not open | Read the markup for the accessible name; try `exact: false` or a RegExp; `web.frameLocator` for iframes; `app.open()` first; `--headed` to look |
|
|
@@ -36,16 +36,17 @@ jq '.run.results[] | select(.status != "passed") | .attempts[-1]
|
|
|
36
36
|
| `ASSERTION_FAILED` | The expectation is wrong, or the state settles later than 5 s; for `agent.assert`, the judgment was false (explanation in the report) | Compare with the actual text in the report or screenshot; `{ timeout }` on the matcher; rewrite the question |
|
|
37
37
|
| `ACTION_FAILED` | Element not actionable (covered, disabled, detached) or an operation timed out | Wait on the right condition with `expect` first; close overlays; check `actionTimeout` |
|
|
38
38
|
| `TEST_TIMEOUT` | The attempt exceeded `timeout` (120 s) | Split the test, or raise `timeout` for slow flows and agent steps |
|
|
39
|
-
| `MODEL_UNAVAILABLE` | No model: neither `createAgent({ model })` nor `agent.model` holds an AI SDK instance. Reported once for the run under `run.errors`; the run stops | Construct one in the config, e.g. `gateway('openai/gpt-5.
|
|
39
|
+
| `MODEL_UNAVAILABLE` | No model: neither `createAgent({ model })` nor `agent.model` holds an AI SDK instance. Reported once for the run under `run.errors`; the run stops | Construct one in the config, e.g. `gateway('openai/gpt-5.6-luna')` from `ai`, and export the key its provider reads (`AI_GATEWAY_API_KEY`) |
|
|
40
40
|
| `MODEL_PROVIDER_FAILED` | Network, 5xx, rate limit, or no credits after the transport retries | Check the key and the quota; retry; exit code 3 |
|
|
41
41
|
| `STEP_TIMEOUT`, `STEP_BUDGET_EXHAUSTED` | The goal was too big or ambiguous, or the provider slow | Split the goal, use on-screen wording, add `context`, raise `timeout` and `actionTimeout`, `--debug` to read the transcript |
|
|
42
42
|
| `CONTEXT_OVERFLOW` | The screen plus the step's history did not fit the model's context window, even after the loop shrank the history and retried once | Lower `agent.maxObservationBytes`, split the step, or pick a model with a larger window |
|
|
43
|
-
| `POLICY_DENIED` | Navigation outside `allowedOrigins`; a `Secret` given to a sink that is not a password field; reading a secure field | Add the origin to `allowedOrigins`; fill
|
|
43
|
+
| `POLICY_DENIED` | Navigation outside `allowedOrigins`; a password `Secret` given to a sink that is not a password field; reading a secure field; `app.screenshot()` after a secret fill | Add the origin to `allowedOrigins`; fill passwords only into password inputs; assert the outcome instead of the value; capture screenshots before filling secrets |
|
|
44
44
|
| `UNSUPPORTED_CAPABILITY` | A fixture the engine does not contribute (`web` on a device), `schema` or `vision` on `act`, an action the surface lacks | Declare `requires: ['web']`; drop the option; use a supported action |
|
|
45
45
|
| `SESSION_UNAVAILABLE`, `SESSION_CONTRACT` | `session: 'x'` with no setup saving `x`; a setup that did not save every declared name | Add or fix the `test.setup` |
|
|
46
46
|
| `ONLY_IN_CI` | `test.only` reached CI | Remove it |
|
|
47
47
|
| `BROWSER_INSTALL_FAILED`, `LAUNCH_TIMEOUT` | Browser download or launch failed | `npx playwright install chromium --with-deps`; raise `launchTimeout` on slow machines |
|
|
48
48
|
| `AUTH_CREDENTIAL_UNAVAILABLE` | `credentials.user('x')` for an undeclared name | Add it to `config.credentials` |
|
|
49
|
+
| `SECRET_UNAVAILABLE` | `secrets.get('x')` for an undeclared name | Add it to `config.secrets` or set `E2E_SECRET_X` |
|
|
49
50
|
|
|
50
51
|
## Tools
|
|
51
52
|
|
|
@@ -58,8 +59,8 @@ jq '.run.results[] | select(.status != "passed") | .attempts[-1]
|
|
|
58
59
|
| `--ai-trace`, then `npx unbox-ai runs .e2e/ai-trace.json` | See exactly what the model was shown and called |
|
|
59
60
|
| `--video` | Watch the failed attempt; `step.startedAt` minus the video artifact's `startedAt` is the step's offset into it |
|
|
60
61
|
| `command.log: '.e2e/logs/app.log'` | Read the app's own output when it never becomes ready or errors mid-test |
|
|
61
|
-
| `await app.screenshot('before-submit')` | Attach evidence
|
|
62
|
-
| `CI=1 npx
|
|
62
|
+
| `await app.screenshot('before-submit')` | Attach evidence before any secret is filled; later calls fail with `POLICY_DENIED` |
|
|
63
|
+
| `CI=1 npx e2e run` | Reproduce CI-only behaviour: `ONLY_IN_CI`, read-only cache, `reuseExisting` ignored |
|
|
63
64
|
|
|
64
65
|
## Flaky tests
|
|
65
66
|
|
|
@@ -7,10 +7,10 @@ test. It needs a config with a target and an agent that holds a model,
|
|
|
7
7
|
nothing else.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx
|
|
11
|
-
npx
|
|
12
|
-
npx
|
|
13
|
-
npx
|
|
10
|
+
npx e2e explore # goal: "Explore the app and find bugs"
|
|
11
|
+
npx e2e explore 'Explore checkout like a first-time buyer and report anything off'
|
|
12
|
+
npx e2e explore --target web --max-steps 4 --headed
|
|
13
|
+
npx e2e explore 'Hunt for broken forms' --video
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## What a run does
|
|
@@ -53,9 +53,15 @@ Exit code `0`: steps ran and no `issue` was reported (warnings allowed). Exit
|
|
|
53
53
|
code `1`: at least one `issue`, or no step ran and nothing was found (the run
|
|
54
54
|
is `blocked`). `2` and `3` as for `run`.
|
|
55
55
|
|
|
56
|
-
The terminal
|
|
57
|
-
|
|
58
|
-
`.
|
|
56
|
+
The terminal shows the exploration step by step: each step by its title with
|
|
57
|
+
its duration, actions, and findings, and each finding the moment it is
|
|
58
|
+
reported as `⚑ high issue Title (/path)`. At the end a `Findings` section
|
|
59
|
+
lists every finding, issues first and the most severe first, each with where
|
|
60
|
+
it was seen, its screenshot path, what was expected against what the screen
|
|
61
|
+
showed, and the steps that reach it; then the `Assessment` and the summary
|
|
62
|
+
(`Findings`, `Steps`, `AI`, `Duration`, `Report`). Severity words: critical 5,
|
|
63
|
+
high 4, medium 3, low 2, trivial 1. `.e2e/report.json` has the record under
|
|
64
|
+
`run.explore`:
|
|
59
65
|
|
|
60
66
|
```json
|
|
61
67
|
{
|
|
@@ -12,14 +12,14 @@ The server ships with `@e2edev/e2e`. `e2e init` offers to register it; by
|
|
|
12
12
|
hand:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
claude mcp add e2e -- npx
|
|
15
|
+
claude mcp add e2e -- npx e2e mcp # Claude Code
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Or declare it in the client's project config (`.mcp.json` for Claude Code,
|
|
19
19
|
`.cursor/mcp.json` for Cursor, `.vscode/mcp.json` for VS Code):
|
|
20
20
|
|
|
21
21
|
```json
|
|
22
|
-
{ "mcpServers": { "e2e": { "command": "npx", "args": ["
|
|
22
|
+
{ "mcpServers": { "e2e": { "command": "npx", "args": ["e2e", "mcp"] } } }
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Flags: `--config <path>` names the default config file, `--target <name>`
|
|
@@ -45,7 +45,7 @@ The catalog, per session:
|
|
|
45
45
|
| --- | --- |
|
|
46
46
|
| `observe` | A fresh observation: one node per line as `#id role "name" ...`, plus the current path. |
|
|
47
47
|
| `tap`, `type`, `press`, `select`, `scroll`, `navigate` | The grammar verbs, exactly as the testing agent gets them. Each reports what changed on screen; `observe` shows the whole screen. A verb the engine cannot honor is not listed and fails with `UNSUPPORTED_CAPABILITY`. |
|
|
48
|
-
| `type_secret` | Fills a configured credential's password into a
|
|
48
|
+
| `type_secret` | Fills a configured secret by name: a credential's password into a password field, a `secrets` entry into any editable input; the plaintext never reaches the agent. Listed when the config declares `credentials` or `secrets`. |
|
|
49
49
|
| `locate` | Tries a semantic locator (`role` + `name`, `text`, `label`, `placeholder`, `testId`, `exact`) and returns how many nodes match, which, and the `screen.*` call to write. |
|
|
50
50
|
| `screenshot` | The masked pixels as an image, withheld once a secret was filled in the session. |
|
|
51
51
|
| Project tools | Every `defineTool` passed to `createAgent({ tools })` that applies to the target's platform, under its own name; an engine pack such as `agentDeviceTools` adds `open_app`, `swipe`, `type_text`, `alert`. |
|
|
@@ -65,7 +65,7 @@ Resources: `e2e://guide` and `e2e://guide/<topic>` hold this skill.
|
|
|
65
65
|
`LOCATOR_NOT_FOUND` or `LOCATOR_AMBIGUOUS`.
|
|
66
66
|
3. Write `tests/<feature>.e2e.ts` (topic `writing-tests`). Deterministic steps
|
|
67
67
|
where you saw exact names; `agent.act` where the flow varies.
|
|
68
|
-
4. Run it from the shell: `npx
|
|
68
|
+
4. Run it from the shell: `npx e2e run tests/<feature>.e2e.ts`,
|
|
69
69
|
read the failure (topic `debugging`), fix, repeat.
|
|
70
70
|
5. `close_session` when you are done exploring; an idle session closes on its
|
|
71
71
|
own after 30 minutes and never outlives 4 hours. To look at another
|
|
@@ -3,28 +3,28 @@
|
|
|
3
3
|
## Commands
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx
|
|
7
|
-
npx
|
|
8
|
-
npx
|
|
9
|
-
npx
|
|
10
|
-
npx
|
|
11
|
-
npx
|
|
12
|
-
npx
|
|
13
|
-
npx
|
|
6
|
+
npx e2e run [files...] [options] # run tests
|
|
7
|
+
npx e2e explore [goal] [options] # explore the app toward a goal, no test file (see the explore topic)
|
|
8
|
+
npx e2e list [files...] [options] # print what run would select, without running
|
|
9
|
+
npx e2e init [--yes] # scaffold a project, refresh the agent skill
|
|
10
|
+
npx e2e guide [topic] # print this skill: setup, writing-tests, agent, running, explore, debugging, mcp
|
|
11
|
+
npx e2e cache ls|clear|stats # read or empty the trace cache
|
|
12
|
+
npx e2e mcp [--target <name>] # serve the project to a coding agent over MCP (topic mcp)
|
|
13
|
+
npx e2e telemetry [disable|enable] # anonymous usage telemetry: status, or the switch
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
`run` flags:
|
|
17
17
|
|
|
18
18
|
| Flag | Effect |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| `[files...]` | Files, directories, or quoted globs relative to the project root. They narrow the config `tests` glob, never bypass it. |
|
|
20
|
+
| `[files...]` | Files, directories, or quoted globs relative to the project root, or a bare file name (`signup.e2e.ts`, `signup`, `agent/signup.e2e.ts` all select `tests/agent/signup.e2e.ts`). They narrow the config `tests` glob, never bypass it. |
|
|
21
21
|
| `--config <path>` | Explicit config file. Default: `e2e.config.ts` or `.mts` found upward from the working directory. |
|
|
22
22
|
| `--target <ids>` | Comma-separated target names. Only selected targets start app commands and services; unknown names fail before startup. |
|
|
23
23
|
| `--tag <tag>` | Repeatable tag filter; `--tag-mode all` requires every tag. |
|
|
24
24
|
| `--headed` | Visible browser or simulator when the engine supports it. |
|
|
25
25
|
| `--agent <names>` | Run unpinned tests as other configured agents (`agents.<name>`), comma-separated or repeated; several names run each such test once per agent. Default is `agents.default`. |
|
|
26
26
|
| `--workers <n>`, `--retries <n>` | Override the resolved values. |
|
|
27
|
-
| `--reporter <ids>` | `list`, `json`, `junit`, comma-separated. `json` cannot combine with `list`. |
|
|
27
|
+
| `--reporter <ids>` | `list`, `json`, `junit`, `markdown`, comma-separated. `json` cannot combine with `list`. |
|
|
28
28
|
| `--artifacts <dir>` | Artifact root, default `.e2e/artifacts`. |
|
|
29
29
|
| `--no-cache` | Run with the trace cache off. |
|
|
30
30
|
| `--pass-with-no-tests` | Exit 0 when nothing matches instead of `NO_TESTS`. |
|
|
@@ -33,10 +33,11 @@ npx --no-install e2e telemetry [disable|enable] # anonymous usage telemetry: sta
|
|
|
33
33
|
| `--video` | Record every attempt (WebM on a browser engine, MP4 on a device engine) under its artifact directory; the failure recap names the file. Fails with `UNSUPPORTED_ARTIFACT` when the engine cannot record. |
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
npx
|
|
37
|
-
npx
|
|
38
|
-
npx
|
|
39
|
-
|
|
36
|
+
npx e2e run tests/signup.e2e.ts
|
|
37
|
+
npx e2e run signup.e2e.ts # the same file by name, from any directory the config globs cover
|
|
38
|
+
npx e2e run tests/agent --tag smoke
|
|
39
|
+
npx e2e run 'tests/**/*.smoke.e2e.ts' --target chromium --workers 1 --retries 0
|
|
40
|
+
CI=1 npx e2e run # reproduce the CI defaults locally
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
`list` takes the same files and the selection flags (`--config`, `--target`,
|
|
@@ -46,12 +47,15 @@ app, an engine, or a worker. `--reporter json` prints `{ "pairs": [...] }`.
|
|
|
46
47
|
Use it to check a filter before a run.
|
|
47
48
|
|
|
48
49
|
```bash
|
|
49
|
-
npx
|
|
50
|
-
npx
|
|
50
|
+
npx e2e list --tag smoke
|
|
51
|
+
npx e2e list tests/signup.e2e.ts --reporter json
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
A `package.json` script keeps it short: `"test:e2e": "e2e run"`, then
|
|
54
|
-
`pnpm test:e2e tests/signup.e2e.ts`.
|
|
55
|
+
`pnpm test:e2e tests/signup.e2e.ts`. pnpm forwards a `--` separator literally,
|
|
56
|
+
so `pnpm test:e2e -- --headed` reaches e2e as `run -- --headed` and is
|
|
57
|
+
rejected with exit 2 rather than run headless; write `pnpm test:e2e --headed`
|
|
58
|
+
or `pnpm exec e2e run --headed` instead.
|
|
55
59
|
|
|
56
60
|
## The trace cache
|
|
57
61
|
|
|
@@ -80,6 +84,12 @@ recorded flow is stale — `--no-cache` only skips the cache for one run.
|
|
|
80
84
|
`artifacts[]`, and `error`.
|
|
81
85
|
- `junit`: `.e2e/junit.xml` beside the report, for CI test summaries.
|
|
82
86
|
Combine it with the terminal output: `--reporter list,junit`.
|
|
87
|
+
- `markdown`: `.e2e/summary.md` beside the report: the counts and what the
|
|
88
|
+
run spent, a block per failed or flaky test with its error, the step it went
|
|
89
|
+
wrong at, the agent's explanation, and evidence paths, a row per file, every
|
|
90
|
+
test folded; or an exploration's findings and assessment. Paste it into a
|
|
91
|
+
pull request or a handoff rather than retelling the result:
|
|
92
|
+
`--reporter list,markdown`.
|
|
83
93
|
- `json`: the report document on stdout.
|
|
84
94
|
- `github()` from `@e2edev/github`: on GitHub Actions, one pull request comment per
|
|
85
95
|
run (edited on rerun) and the job summary; needs `pull-requests: write` and
|
|
@@ -133,7 +143,7 @@ jobs:
|
|
|
133
143
|
cache: pnpm
|
|
134
144
|
- run: pnpm install --frozen-lockfile
|
|
135
145
|
- run: npx playwright install chromium --with-deps
|
|
136
|
-
- run: npx
|
|
146
|
+
- run: npx e2e run --reporter list,junit
|
|
137
147
|
env:
|
|
138
148
|
E2E_USER_ADMIN_USERNAME: ${{ secrets.E2E_USER_ADMIN_USERNAME }}
|
|
139
149
|
E2E_USER_ADMIN_PASSWORD: ${{ secrets.E2E_USER_ADMIN_PASSWORD }}
|
|
@@ -156,6 +166,7 @@ jobs:
|
|
|
156
166
|
launch timeout.
|
|
157
167
|
- Start the app through the engine's `command`; the runner tears it down on
|
|
158
168
|
every exit path.
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
169
|
+
- Agent steps run in the same job as everything else. Pass the key the
|
|
170
|
+
config's model reads (`AI_GATEWAY_API_KEY` for `gateway()` from `ai`) as
|
|
171
|
+
a secret in the run step's `env`, and commit `.e2e/cache/` so recorded
|
|
172
|
+
steps replay in CI with no model call.
|
|
@@ -7,9 +7,17 @@
|
|
|
7
7
|
whatever the nearest `package.json` `type` says, so a CommonJS package (a
|
|
8
8
|
Next.js app, for instance) needs no change. Write them with `import`, never
|
|
9
9
|
`require` or `module.exports`.
|
|
10
|
-
- For browser tests, `@e2edev/playwright
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
- For browser tests, `@e2edev/playwright` plus `playwright` (`>=1.63.0 <2`),
|
|
11
|
+
a peer dependency the engine does not install itself: an app that already
|
|
12
|
+
depends on Playwright keeps its version and its browser cache. A version
|
|
13
|
+
outside the range may be rejected by the package manager as an unmet peer
|
|
14
|
+
(npm's `ERESOLVE`); upgrade `playwright` within the range.
|
|
15
|
+
Missing browsers download when the engine first boots. In CI install them
|
|
16
|
+
up front: `npx playwright install chromium --with-deps`.
|
|
17
|
+
- For mobile tests, `@e2edev/agent-device` plus `agent-device` (`0.21.x`),
|
|
18
|
+
likewise a peer dependency: a project that already drives devices with the
|
|
19
|
+
agent-device CLI keeps its version. The range pins the tested minor because
|
|
20
|
+
agent-device minors break; it moves with each engine release.
|
|
13
21
|
|
|
14
22
|
## Scaffold
|
|
15
23
|
|
|
@@ -18,8 +26,8 @@ npx @e2edev/e2e@beta init # npm
|
|
|
18
26
|
pnpm dlx @e2edev/e2e@beta init # pnpm
|
|
19
27
|
```
|
|
20
28
|
|
|
21
|
-
When `@e2edev/e2e` is already installed, run `npx
|
|
22
|
-
|
|
29
|
+
When `@e2edev/e2e` is already installed, run `npx e2e init` instead, so the
|
|
30
|
+
installed version scaffolds.
|
|
23
31
|
|
|
24
32
|
The wizard asks for the engine (Web with Playwright by default; Mobile with
|
|
25
33
|
agent-device and None are the alternatives), which model gateway agent steps
|
|
@@ -42,7 +50,7 @@ project.
|
|
|
42
50
|
Without the wizard:
|
|
43
51
|
|
|
44
52
|
```bash
|
|
45
|
-
npm install --save-dev @e2edev/e2e@beta @e2edev/playwright@beta ai@^7
|
|
53
|
+
npm install --save-dev @e2edev/e2e@beta @e2edev/playwright@beta playwright ai@^7
|
|
46
54
|
```
|
|
47
55
|
|
|
48
56
|
`ai` (the Vercel AI SDK, v7) is only needed for `agent.*` steps.
|
|
@@ -69,10 +77,10 @@ export default {
|
|
|
69
77
|
}),
|
|
70
78
|
},
|
|
71
79
|
],
|
|
72
|
-
//
|
|
80
|
+
// The model behind every agent.* step: an AI SDK instance; gateway() from 'ai' reads AI_GATEWAY_API_KEY.
|
|
73
81
|
agents: {
|
|
74
82
|
default: createAgent({
|
|
75
|
-
model: gateway('openai/gpt-5.
|
|
83
|
+
model: gateway('openai/gpt-5.6-luna'),
|
|
76
84
|
system: 'You are a thorough QA agent. Verify every outcome on screen.',
|
|
77
85
|
}),
|
|
78
86
|
},
|
|
@@ -91,10 +99,11 @@ export default {
|
|
|
91
99
|
| `assertionTimeout` | `5000` | `expect` polling window. |
|
|
92
100
|
| `retries` | `0`, `1` in CI | 0 to 10. |
|
|
93
101
|
| `workers` | half the cores, `1` in CI | Test files run in parallel across workers, at most the `workers` the engine declares per target (a device target: one per device). |
|
|
94
|
-
| `reporters` | `['list']` | `list`, `json`, `junit`, and reporter objects (`{ name, onEvent?, onRunFinished? }`) that receive the finished run. `json` excludes `list`; `--reporter` keeps the objects. |
|
|
102
|
+
| `reporters` | `['list']` | `list`, `json`, `junit`, `markdown`, and reporter objects (`{ name, onEvent?, onRunFinished? }`) that receive the finished run. `json` excludes `list`; `--reporter` keeps the objects. |
|
|
95
103
|
| `cache` | `'read-write'`, `'read-only'` in CI | The trace cache for `agent.act`; `'off'` disables it. |
|
|
96
|
-
| `agents` | `{ default: built-in }` | Agents by name. `default` is what tests run with; `e2e run --agent <name>` runs with another. Each entry is `createAgent(...)`, an options block `{ model, context,
|
|
104
|
+
| `agents` | `{ default: built-in }` | Agents by name. `default` is what tests run with; `e2e run --agent <name>` runs with another. Each entry is `createAgent(...)`, an options block `{ model, context, maxSteps, maxModelCalls, vision, providerOptions }`, or a custom `StepExecutor`. `model` is an AI SDK instance; without one anywhere, acquiring `agent` is `MODEL_UNAVAILABLE`. |
|
|
97
105
|
| `credentials` | `{}` | Named `{ username, password, allowedOrigins? }` entries; `password` may be a function returning the value. |
|
|
106
|
+
| `secrets` | `{}` | Named values the model never sees (API keys, tokens): a string, a function returning the value, or `{ value, allowedOrigins? }`. A name cannot also be a credential. |
|
|
98
107
|
| `screen.testIdAttribute` | `'data-testid'` | Attribute read by `getByTestId`. |
|
|
99
108
|
| `artifacts` | `['screenshot', 'trace']` | Kinds to keep (`screenshot`, `trace`, and the opt-in `video`), or `{ kinds, store, video }`; `video: { retain: 'on-failure' }` keeps only the recordings of attempts that did not pass. |
|
|
100
109
|
| `projectId` | the package name | Report and cache identity. |
|
|
@@ -106,9 +115,9 @@ process, and identity. `playwright()` accepts:
|
|
|
106
115
|
|
|
107
116
|
| Option | Meaning |
|
|
108
117
|
| --- | --- |
|
|
109
|
-
| `url` | Base URL for `app.open()` and relative navigation. A missing scheme becomes `https://`, or `http://` for a loopback host. Required once a test navigates. |
|
|
110
|
-
| `command` | The process that serves `url`. See below. |
|
|
111
|
-
| `readyUrl` | Readiness probe when it differs from `url`. |
|
|
118
|
+
| `url` | Base URL for `app.open()` and relative navigation. A missing scheme becomes `https://`, or `http://` for a loopback host. Required once a test navigates. Port `0` on `127.0.0.1` or `[::1]` asks the run for a free port. |
|
|
119
|
+
| `command` | The process that serves `url`. `{port}` in `args` and `env` expands to the port of `url`. See below. |
|
|
120
|
+
| `readyUrl` | Readiness probe when it differs from `url`. `{port}` expands here too. |
|
|
112
121
|
| `services` | Dependency processes started before `command`, in order. |
|
|
113
122
|
| `allowedOrigins` | Origins tests and the agent may navigate to. Default: the origin of `url`. |
|
|
114
123
|
| `environment` | `'test'`, `'staging'`, `'production'`. Inferred from the host; a label for the report and the cache key. |
|
|
@@ -181,6 +190,15 @@ How it behaves:
|
|
|
181
190
|
then runs their `teardown` commands in reverse.
|
|
182
191
|
- `executable` is resolved on `PATH` and never shell-interpreted. Point it at
|
|
183
192
|
the server itself rather than at a wrapper script.
|
|
193
|
+
- `url: 'http://127.0.0.1:0'` (or `[::1]:0`, never `localhost:0`) asks the
|
|
194
|
+
run for a free port, so two checkouts can run at once. The command must take
|
|
195
|
+
it through `{port}` in `args` or `env`; the token also expands in `readyUrl`
|
|
196
|
+
and the services:
|
|
197
|
+
`command: { executable: 'pnpm', args: ['dev', '--port', '{port}'], env: { PORT: '{port}' } }`.
|
|
198
|
+
Tests read the allocated URL from `app.baseUrl`; the cache identity keeps
|
|
199
|
+
the declared `:0`. Services keep their own ports. A port another process
|
|
200
|
+
grabs between allocation and spawn fails the start with `APP_UNREACHABLE`;
|
|
201
|
+
rerun.
|
|
184
202
|
|
|
185
203
|
To test an app started elsewhere, point `url` at it and start it yourself,
|
|
186
204
|
or read the address from the environment:
|
|
@@ -193,6 +211,7 @@ or read the address from the environment:
|
|
|
193
211
|
| --- | --- |
|
|
194
212
|
| `AI_GATEWAY_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, ... | Read by the provider package the config constructs the model with (`gateway()` from `ai`, `openrouter()`, `openai()`), not by the runner. The runner reads no model variable; the model is always an AI SDK instance in the config. |
|
|
195
213
|
| `E2E_USER_<NAME>_USERNAME`, `E2E_USER_<NAME>_PASSWORD` | Override `credentials.<name>`. `<NAME>` is the credential name uppercased, other characters as `_`. |
|
|
214
|
+
| `E2E_SECRET_<NAME>` | Overrides the value of `secrets.<name>`, same uppercasing rule. |
|
|
196
215
|
| `CI` | Turns on CI defaults: `retries: 1`, `workers: 1`, `test.only` rejected, cache read-only, `reuseExisting` ignored. |
|
|
197
216
|
| `E2E_TELEMETRY_DISABLED`, `DO_NOT_TRACK` | Turn anonymous usage telemetry off; `e2e telemetry disable` saves the same choice. `E2E_TELEMETRY_DEBUG=1` prints each event instead of sending it. |
|
|
198
217
|
|
|
@@ -227,6 +246,11 @@ export default {
|
|
|
227
246
|
single `device` runs one worker whatever `workers` says; a list,
|
|
228
247
|
`device: ['iPhone 17', 'iPhone 17 Pro']`, is an explicit pool. Devices boot
|
|
229
248
|
in `prepare`, before the run's clock starts.
|
|
249
|
+
- A test's steps never wait for the screen to settle; `expect` verifies the
|
|
250
|
+
outcome. Only a control that appeared or moved with the previous action
|
|
251
|
+
waits out the `transition` budget (default 500 ms) before it is acted on.
|
|
252
|
+
Agent actions settle for `settle` ms (default 150) before the agent
|
|
253
|
+
observes again; `settle: false` skips that wait.
|
|
230
254
|
- Cancelled device commands keep running; the next attempt waits for them.
|
|
231
255
|
Raw screenshot files are removed when capture finishes, including after
|
|
232
256
|
cancellation.
|
|
@@ -244,8 +268,10 @@ export default {
|
|
|
244
268
|
|
|
245
269
|
## Done when
|
|
246
270
|
|
|
247
|
-
- `npx
|
|
271
|
+
- `npx e2e run tests/example.e2e.ts` passes against the app.
|
|
248
272
|
- `package.json` has a script such as `"test:e2e": "e2e run"`.
|
|
249
273
|
- `.gitignore` lists the `.e2e/` outputs (init adds them). Committing
|
|
250
|
-
`.e2e/cache/` is opt-in: remove that line
|
|
251
|
-
|
|
274
|
+
`.e2e/cache/` is opt-in: remove that line so CI and teammates replay
|
|
275
|
+
`agent.act` steps instead of re-running the model.
|
|
276
|
+
- CI runs the whole suite on pull requests, agent steps included; see
|
|
277
|
+
`running`.
|