@e2edev/e2e 0.5.0 → 0.6.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 +64 -25
- package/dist/agent/act.d.ts +2 -2
- package/dist/agent/act.js +14 -37
- package/dist/agent/act.js.map +1 -1
- package/dist/agent/actions.d.ts +2 -2
- package/dist/agent/actions.d.ts.map +1 -1
- package/dist/agent/actions.js +1 -1
- package/dist/agent/ai-sdk.d.ts +1 -2
- package/dist/agent/ai-sdk.d.ts.map +1 -1
- package/dist/agent/ai-sdk.js +1 -2
- package/dist/agent/ai-sdk.js.map +1 -1
- package/dist/agent/default-agent.d.ts +10 -32
- package/dist/agent/default-agent.d.ts.map +1 -1
- package/dist/agent/default-agent.js +6 -13
- package/dist/agent/default-agent.js.map +1 -1
- package/dist/agent/derived.d.ts +1 -1
- package/dist/agent/derived.js +1 -1
- package/dist/agent/error.d.ts +2 -4
- package/dist/agent/error.d.ts.map +1 -1
- package/dist/agent/error.js +3 -5
- package/dist/agent/error.js.map +1 -1
- package/dist/agent/executor.d.ts +23 -52
- package/dist/agent/executor.d.ts.map +1 -1
- package/dist/agent/executor.js +1 -2
- package/dist/agent/executor.js.map +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +10 -7
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/invocation.d.ts +8 -6
- package/dist/agent/invocation.d.ts.map +1 -1
- package/dist/agent/invocation.js +9 -4
- package/dist/agent/invocation.js.map +1 -1
- package/dist/agent/ledger.d.ts +17 -12
- package/dist/agent/ledger.d.ts.map +1 -1
- package/dist/agent/ledger.js +6 -9
- package/dist/agent/ledger.js.map +1 -1
- package/dist/agent/model/adapter.d.ts +7 -4
- package/dist/agent/model/adapter.d.ts.map +1 -1
- package/dist/agent/model/adapter.js +2 -2
- package/dist/agent/model/adapter.js.map +1 -1
- package/dist/agent/model/router.d.ts +1 -1
- package/dist/agent/model/router.d.ts.map +1 -1
- package/dist/agent/model/router.js +1 -1
- package/dist/agent/model/router.js.map +1 -1
- package/dist/agent/model/sdk.d.ts +11 -1
- package/dist/agent/model/sdk.d.ts.map +1 -1
- package/dist/agent/model/sdk.js +54 -10
- package/dist/agent/model/sdk.js.map +1 -1
- package/dist/agent/observation.d.ts +7 -7
- package/dist/agent/observation.d.ts.map +1 -1
- package/dist/agent/observation.js +7 -7
- package/dist/agent/observation.js.map +1 -1
- package/dist/agent/phases.d.ts +3 -3
- package/dist/agent/phases.d.ts.map +1 -1
- package/dist/agent/phases.js +4 -4
- package/dist/agent/phases.js.map +1 -1
- package/dist/agent/primitives.d.ts +4 -25
- package/dist/agent/primitives.d.ts.map +1 -1
- package/dist/agent/primitives.js +3 -36
- package/dist/agent/primitives.js.map +1 -1
- package/dist/agent/prompts.d.ts +1 -1
- package/dist/agent/prompts.js +1 -1
- package/dist/agent/protocol.d.ts +2 -3
- package/dist/agent/protocol.d.ts.map +1 -1
- package/dist/agent/protocol.js +2 -3
- package/dist/agent/protocol.js.map +1 -1
- package/dist/agent/public.d.ts +8 -10
- package/dist/agent/public.d.ts.map +1 -1
- package/dist/agent/public.js +7 -8
- package/dist/agent/public.js.map +1 -1
- package/dist/agent/replay.d.ts +2 -2
- package/dist/agent/replay.d.ts.map +1 -1
- package/dist/agent/replay.js +1 -1
- package/dist/agent/secrets.d.ts +3 -3
- package/dist/agent/secrets.d.ts.map +1 -1
- package/dist/agent/secrets.js +4 -4
- package/dist/agent/secrets.js.map +1 -1
- package/dist/agent/step-cache.d.ts +1 -1
- package/dist/agent/step-cache.d.ts.map +1 -1
- package/dist/agent/step-cache.js +7 -1
- package/dist/agent/step-cache.js.map +1 -1
- package/dist/agent/tool-loop.d.ts +9 -33
- package/dist/agent/tool-loop.d.ts.map +1 -1
- package/dist/agent/tool-loop.js +15 -38
- package/dist/agent/tool-loop.js.map +1 -1
- package/dist/agent/tool.d.ts +4 -5
- package/dist/agent/tool.d.ts.map +1 -1
- package/dist/agent/tool.js +2 -7
- package/dist/agent/tool.js.map +1 -1
- package/dist/cache/anchors.d.ts +2 -2
- package/dist/cache/anchors.d.ts.map +1 -1
- package/dist/cache/anchors.js +1 -1
- package/dist/cache/context.d.ts +6 -1
- package/dist/cache/context.d.ts.map +1 -1
- package/dist/cache/context.js +2 -1
- package/dist/cache/context.js.map +1 -1
- package/dist/cache/decide.d.ts +1 -1
- package/dist/cache/decide.js +1 -1
- package/dist/cache/identity.d.ts +9 -9
- package/dist/cache/identity.d.ts.map +1 -1
- package/dist/cache/identity.js +6 -6
- package/dist/cache/identity.js.map +1 -1
- package/dist/cache/recorder.d.ts +4 -2
- package/dist/cache/recorder.d.ts.map +1 -1
- package/dist/cache/recorder.js +10 -1
- package/dist/cache/recorder.js.map +1 -1
- package/dist/cache/relocate.d.ts +2 -2
- package/dist/cache/relocate.d.ts.map +1 -1
- package/dist/cache/relocate.js +1 -1
- package/dist/cache/store.d.ts +1 -1
- package/dist/cache/store.js +1 -1
- package/dist/cache/trace.d.ts +16 -1
- package/dist/cache/trace.d.ts.map +1 -1
- package/dist/cache/trace.js +22 -1
- package/dist/cache/trace.js.map +1 -1
- package/dist/cli/bin.js +9 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/cache.d.ts +25 -0
- package/dist/cli/cache.d.ts.map +1 -0
- package/dist/cli/cache.js +249 -0
- package/dist/cli/cache.js.map +1 -0
- package/dist/cli/docs-url.d.ts +3 -0
- package/dist/cli/docs-url.d.ts.map +1 -0
- package/dist/cli/docs-url.js +3 -0
- package/dist/cli/docs-url.js.map +1 -0
- package/dist/cli/guide.d.ts +4 -0
- package/dist/cli/guide.d.ts.map +1 -0
- package/dist/cli/guide.js +16 -0
- package/dist/cli/guide.js.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +211 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/agent-skill.d.ts +34 -0
- package/dist/cli/init/agent-skill.d.ts.map +1 -0
- package/dist/cli/init/agent-skill.js +44 -0
- package/dist/cli/init/agent-skill.js.map +1 -0
- package/dist/cli/init/{backends.d.ts → engines.d.ts} +39 -32
- package/dist/cli/init/engines.d.ts.map +1 -0
- package/dist/cli/init/{backends.js → engines.js} +43 -34
- package/dist/cli/init/engines.js.map +1 -0
- package/dist/cli/init/package.d.ts +19 -3
- package/dist/cli/init/package.d.ts.map +1 -1
- package/dist/cli/init/package.js +20 -20
- package/dist/cli/init/package.js.map +1 -1
- package/dist/cli/init/scaffold.d.ts +3 -3
- package/dist/cli/init/scaffold.d.ts.map +1 -1
- package/dist/cli/init/scaffold.js +13 -12
- package/dist/cli/init/scaffold.js.map +1 -1
- package/dist/cli/init.d.ts +14 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +104 -25
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/signals.d.ts +5 -2
- package/dist/cli/signals.d.ts.map +1 -1
- package/dist/cli/signals.js +9 -3
- package/dist/cli/signals.js.map +1 -1
- package/dist/cli/skill.d.ts +16 -0
- package/dist/cli/skill.d.ts.map +1 -0
- package/dist/cli/skill.js +57 -0
- package/dist/cli/skill.js.map +1 -0
- package/dist/collect/collect.d.ts +40 -3
- package/dist/collect/collect.d.ts.map +1 -1
- package/dist/collect/collect.js +142 -19
- package/dist/collect/collect.js.map +1 -1
- package/dist/collect/registry.d.ts +1 -1
- package/dist/collect/registry.d.ts.map +1 -1
- package/dist/collect/registry.js +2 -2
- package/dist/collect/registry.js.map +1 -1
- package/dist/collect/select.d.ts +1 -1
- package/dist/collect/select.d.ts.map +1 -1
- package/dist/collect/select.js +81 -8
- package/dist/collect/select.js.map +1 -1
- package/dist/config/agent.d.ts +14 -2
- package/dist/config/agent.d.ts.map +1 -1
- package/dist/config/agent.js +36 -11
- package/dist/config/agent.js.map +1 -1
- package/dist/config/app.d.ts +76 -19
- package/dist/config/app.d.ts.map +1 -1
- package/dist/config/app.js +159 -104
- package/dist/config/app.js.map +1 -1
- package/dist/config/diagnose.d.ts +15 -0
- package/dist/config/diagnose.d.ts.map +1 -0
- package/dist/config/diagnose.js +122 -0
- package/dist/config/diagnose.js.map +1 -0
- package/dist/config/load.d.ts +9 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +35 -3
- package/dist/config/load.js.map +1 -1
- package/dist/config/resolve.d.ts +8 -7
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +73 -40
- package/dist/config/resolve.js.map +1 -1
- package/dist/config/validate.d.ts +6 -2
- package/dist/config/validate.d.ts.map +1 -1
- package/dist/config/validate.js +20 -3
- package/dist/config/validate.js.map +1 -1
- package/dist/credentials.d.ts +1 -1
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +1 -1
- package/dist/credentials.js.map +1 -1
- package/dist/{backend → engine}/contract.d.ts +27 -19
- package/dist/engine/contract.d.ts.map +1 -0
- package/dist/{backend → engine}/contract.js +18 -18
- package/dist/engine/contract.js.map +1 -0
- package/dist/{backend → engine}/index.d.ts +141 -79
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/{backend → engine}/index.js +62 -37
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/session.d.ts +23 -0
- package/dist/engine/session.d.ts.map +1 -0
- package/dist/{backend → engine}/session.js +58 -51
- package/dist/engine/session.js.map +1 -0
- package/dist/{backend → engine}/surface.d.ts +11 -11
- package/dist/engine/surface.d.ts.map +1 -0
- package/dist/{backend → engine}/surface.js +2 -2
- package/dist/engine/surface.js.map +1 -0
- package/dist/engine/timing.d.ts.map +1 -0
- package/dist/engine/timing.js +8 -0
- package/dist/engine/timing.js.map +1 -0
- package/dist/expect/async.d.ts +1 -1
- package/dist/expect/async.d.ts.map +1 -1
- package/dist/expect/async.js +37 -1
- package/dist/expect/async.js.map +1 -1
- package/dist/expect/index.js +1 -1
- package/dist/expect/values.d.ts +1 -1
- package/dist/expect/values.d.ts.map +1 -1
- package/dist/expect/values.js +1 -1
- package/dist/expect/values.js.map +1 -1
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/brands.d.ts +1 -1
- package/dist/internal/brands.d.ts.map +1 -1
- package/dist/internal/brands.js +1 -1
- package/dist/internal/brands.js.map +1 -1
- package/dist/internal/errors.d.ts +37 -19
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +94 -44
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/globs.d.ts +2 -2
- package/dist/internal/globs.d.ts.map +1 -1
- package/dist/internal/globs.js +2 -2
- package/dist/internal/globs.js.map +1 -1
- package/dist/internal/ids.d.ts +3 -3
- package/dist/internal/ids.d.ts.map +1 -1
- package/dist/internal/ids.js +3 -3
- package/dist/internal/ids.js.map +1 -1
- package/dist/internal/node-version.d.ts +10 -0
- package/dist/internal/node-version.d.ts.map +1 -0
- package/dist/internal/node-version.js +28 -0
- package/dist/internal/node-version.js.map +1 -0
- package/dist/internal/objects.d.ts +21 -0
- package/dist/internal/objects.d.ts.map +1 -0
- package/dist/internal/objects.js +14 -0
- package/dist/internal/objects.js.map +1 -0
- package/dist/internal/package-manager.d.ts +10 -0
- package/dist/internal/package-manager.d.ts.map +1 -0
- package/dist/internal/package-manager.js +43 -0
- package/dist/internal/package-manager.js.map +1 -0
- package/dist/internal/redact.d.ts +1 -1
- package/dist/internal/redact.d.ts.map +1 -1
- package/dist/internal/redact.js +1 -1
- package/dist/internal/redact.js.map +1 -1
- package/dist/internal/suggest.d.ts +16 -0
- package/dist/internal/suggest.d.ts.map +1 -0
- package/dist/internal/suggest.js +61 -0
- package/dist/internal/suggest.js.map +1 -0
- package/dist/internal/text.d.ts +1 -1
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +1 -1
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/urls.d.ts +3 -3
- package/dist/internal/urls.d.ts.map +1 -1
- package/dist/internal/urls.js +8 -3
- package/dist/internal/urls.js.map +1 -1
- package/dist/locator/engine.d.ts +7 -7
- package/dist/locator/engine.d.ts.map +1 -1
- package/dist/locator/engine.js +14 -14
- package/dist/locator/engine.js.map +1 -1
- package/dist/locator/expression.d.ts +5 -3
- package/dist/locator/expression.d.ts.map +1 -1
- package/dist/locator/expression.js +25 -5
- package/dist/locator/expression.js.map +1 -1
- package/dist/locator/screen.d.ts +1 -1
- package/dist/locator/screen.d.ts.map +1 -1
- package/dist/locator/screen.js +3 -3
- package/dist/locator/screen.js.map +1 -1
- package/dist/report/build.d.ts +10 -9
- package/dist/report/build.d.ts.map +1 -1
- package/dist/report/build.js +25 -22
- package/dist/report/build.js.map +1 -1
- package/dist/report/code-frame.d.ts +8 -4
- package/dist/report/code-frame.d.ts.map +1 -1
- package/dist/report/code-frame.js +23 -18
- package/dist/report/code-frame.js.map +1 -1
- package/dist/report/debug-steps.js +1 -1
- package/dist/report/debug-steps.js.map +1 -1
- package/dist/report/format.d.ts +73 -0
- package/dist/report/format.d.ts.map +1 -0
- package/dist/report/format.js +147 -0
- package/dist/report/format.js.map +1 -0
- package/dist/report/junit.d.ts +1 -1
- package/dist/report/junit.d.ts.map +1 -1
- package/dist/report/junit.js +1 -1
- package/dist/report/junit.js.map +1 -1
- package/dist/report/list.d.ts +96 -47
- package/dist/report/list.d.ts.map +1 -1
- package/dist/report/list.js +489 -244
- package/dist/report/list.js.map +1 -1
- package/dist/report/live-window.d.ts +28 -0
- package/dist/report/live-window.d.ts.map +1 -0
- package/dist/report/live-window.js +102 -0
- package/dist/report/live-window.js.map +1 -0
- package/dist/report/write.d.ts +1 -1
- package/dist/report/write.d.ts.map +1 -1
- package/dist/report/write.js +1 -1
- package/dist/report/write.js.map +1 -1
- package/dist/run/budget.d.ts +2 -2
- package/dist/run/budget.js +2 -2
- package/dist/run/declared-processes.d.ts +30 -0
- package/dist/run/declared-processes.d.ts.map +1 -0
- package/dist/run/declared-processes.js +61 -0
- package/dist/run/declared-processes.js.map +1 -0
- package/dist/run/events.d.ts +30 -2
- package/dist/run/events.d.ts.map +1 -1
- package/dist/run/events.js.map +1 -1
- package/dist/run/execute.d.ts +19 -16
- package/dist/run/execute.d.ts.map +1 -1
- package/dist/run/execute.js +65 -67
- package/dist/run/execute.js.map +1 -1
- package/dist/run/fixture-recording.d.ts +10 -4
- package/dist/run/fixture-recording.d.ts.map +1 -1
- package/dist/run/fixture-recording.js +19 -6
- package/dist/run/fixture-recording.js.map +1 -1
- package/dist/run/fixtures.d.ts +2 -2
- package/dist/run/fixtures.d.ts.map +1 -1
- package/dist/run/fixtures.js +72 -44
- package/dist/run/fixtures.js.map +1 -1
- package/dist/run/in-process.d.ts +1 -1
- package/dist/run/in-process.js +1 -1
- package/dist/run/managed-process.d.ts +46 -9
- package/dist/run/managed-process.d.ts.map +1 -1
- package/dist/run/managed-process.js +136 -50
- package/dist/run/managed-process.js.map +1 -1
- package/dist/run/realm.d.ts +2 -2
- package/dist/run/realm.js +2 -2
- package/dist/run/runner.d.ts +23 -1
- package/dist/run/runner.d.ts.map +1 -1
- package/dist/run/runner.js +111 -40
- package/dist/run/runner.js.map +1 -1
- package/dist/run/scheduler.d.ts +5 -5
- package/dist/run/scheduler.d.ts.map +1 -1
- package/dist/run/scheduler.js +7 -7
- package/dist/run/scheduler.js.map +1 -1
- package/dist/run/serial.d.ts +10 -2
- package/dist/run/serial.d.ts.map +1 -1
- package/dist/run/serial.js +5 -3
- package/dist/run/serial.js.map +1 -1
- package/dist/run/sessions.d.ts +10 -10
- package/dist/run/sessions.d.ts.map +1 -1
- package/dist/run/sessions.js +11 -11
- package/dist/run/sessions.js.map +1 -1
- package/dist/run/steps.d.ts +3 -3
- package/dist/run/steps.d.ts.map +1 -1
- package/dist/run/steps.js +1 -1
- package/dist/run/steps.js.map +1 -1
- package/dist/run/units.d.ts +1 -1
- package/dist/run/units.js +1 -1
- package/dist/run/worker/entry.d.ts +1 -1
- package/dist/run/worker/entry.js +2 -2
- package/dist/run/worker/entry.js.map +1 -1
- package/dist/run/worker/protocol.d.ts +18 -6
- package/dist/run/worker/protocol.d.ts.map +1 -1
- package/dist/run/worker/protocol.js +1 -1
- package/dist/run/worker/protocol.js.map +1 -1
- package/dist/run/worker/session.d.ts +2 -2
- package/dist/run/worker/session.d.ts.map +1 -1
- package/dist/run/worker/session.js +5 -3
- package/dist/run/worker/session.js.map +1 -1
- package/dist/types.d.ts +58 -41
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/package.json +13 -12
- package/skills/e2e/SKILL.md +110 -0
- package/skills/e2e/references/agent.md +210 -0
- package/skills/e2e/references/debugging.md +81 -0
- package/skills/e2e/references/running.md +153 -0
- package/skills/e2e/references/setup.md +238 -0
- package/skills/e2e/references/writing-tests.md +289 -0
- package/dist/backend/contract.d.ts.map +0 -1
- package/dist/backend/contract.js.map +0 -1
- package/dist/backend/index.d.ts.map +0 -1
- package/dist/backend/index.js.map +0 -1
- package/dist/backend/session.d.ts +0 -23
- package/dist/backend/session.d.ts.map +0 -1
- package/dist/backend/session.js.map +0 -1
- package/dist/backend/surface.d.ts.map +0 -1
- package/dist/backend/surface.js.map +0 -1
- package/dist/backend/timing.d.ts.map +0 -1
- package/dist/backend/timing.js +0 -8
- package/dist/backend/timing.js.map +0 -1
- package/dist/cli/init/backends.d.ts.map +0 -1
- package/dist/cli/init/backends.js.map +0 -1
- package/dist/report/live-status.d.ts +0 -51
- package/dist/report/live-status.d.ts.map +0 -1
- package/dist/report/live-status.js +0 -163
- package/dist/report/live-status.js.map +0 -1
- package/dist/run/fixture-legacy.d.ts +0 -36
- package/dist/run/fixture-legacy.d.ts.map +0 -1
- package/dist/run/fixture-legacy.js +0 -143
- package/dist/run/fixture-legacy.js.map +0 -1
- /package/dist/{backend → engine}/timing.d.ts +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Agent steps
|
|
2
|
+
|
|
3
|
+
`agent` is a fixture like `screen`. Each call is one bounded invocation: a
|
|
4
|
+
fresh redacted observation, a deadline, a model-call budget, and no shared
|
|
5
|
+
transcript between calls. Deterministic tests never load a model.
|
|
6
|
+
|
|
7
|
+
## Configure a model
|
|
8
|
+
|
|
9
|
+
There is no default model. Pick one of three shapes:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// e2e.config.ts
|
|
13
|
+
import { createAgent } from '@e2edev/e2e/agent';
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
// 1. The built-in agent; the model comes from E2E_MODEL at run time.
|
|
17
|
+
agent: createAgent({ system: 'You are a thorough QA agent. Verify every outcome on screen.' }),
|
|
18
|
+
|
|
19
|
+
// 2. An options block: a gateway model string plus project vocabulary.
|
|
20
|
+
// agent: { model: 'anthropic/claude-sonnet-4.5', context: 'A billing dashboard. Plans are Free, Team, and Pro.' },
|
|
21
|
+
|
|
22
|
+
// 3. A live AI SDK model instance for a provider called directly.
|
|
23
|
+
// agent: createAgent({ model: openai('gpt-5.4-mini') }),
|
|
24
|
+
} satisfies E2EConfig;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
E2E_MODEL=anthropic/claude-sonnet-4.5 E2E_MODEL_API_KEY=... npx --no-install e2e run
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- A `provider/model-id` string is routed through the Vercel AI Gateway; one
|
|
32
|
+
`E2E_MODEL_API_KEY` (or `AI_GATEWAY_API_KEY`) reaches every provider.
|
|
33
|
+
- With no `agent` key at all, the built-in agent still runs and takes its
|
|
34
|
+
model from `E2E_MODEL`. Use `createAgent` for a `system` prompt, tools, or
|
|
35
|
+
a pinned model.
|
|
36
|
+
- `ai@^7` must be installed for any `agent.*` step; the runner loads it
|
|
37
|
+
lazily and fails without it.
|
|
38
|
+
- `context` in the config and `agentContext` on a test or group add trusted
|
|
39
|
+
project vocabulary to every prompt.
|
|
40
|
+
- `visionModel` (or `E2E_VISION_MODEL`) serves the calls that send pixels.
|
|
41
|
+
- Missing model or key: the `agent` fixture fails with `MODEL_UNAVAILABLE`
|
|
42
|
+
(exit 2) at first use.
|
|
43
|
+
|
|
44
|
+
## act: one goal
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { credentials } from '@e2edev/e2e';
|
|
48
|
+
|
|
49
|
+
await agent.act('add a todo named "Buy milk" and mark it done');
|
|
50
|
+
await agent.act('invite {email} as an editor', { email: 'ada@example.test' });
|
|
51
|
+
|
|
52
|
+
const member = credentials.user('member');
|
|
53
|
+
await agent.act('sign in with the given credentials', {
|
|
54
|
+
username: member.username,
|
|
55
|
+
password: member.password, // a Secret: the model sees its name, the runner fills the field
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`act(instruction, params?, options?)` plans and performs a multi-action flow
|
|
60
|
+
and ends in a verdict. Passed returns `{ ok: true }`. Failed or blocked
|
|
61
|
+
throws an `AgentError` whose `code` says why: `ACTION_FAILED` for a plain
|
|
62
|
+
failure, `STEP_BUDGET_EXHAUSTED` or `STEP_TIMEOUT` when the budget or the
|
|
63
|
+
clock ran out, and a blocked code (`AUTH_CREDENTIAL_UNAVAILABLE`,
|
|
64
|
+
`ENVIRONMENT_UNAVAILABLE`, `SEED_DATA_MISSING`, `TEST_SETUP_FAILED`,
|
|
65
|
+
`AUTOMATION_UNSUPPORTED`) when something outside the product prevented a
|
|
66
|
+
verdict.
|
|
67
|
+
|
|
68
|
+
Options: `timeout` (default the test timeout), `maxSteps` (default 25
|
|
69
|
+
actions), `maxModelCalls` (default 25). Per-call values can only lower the
|
|
70
|
+
configured limits. `schema` and `vision` on `act` are not implemented and
|
|
71
|
+
reject with `UNSUPPORTED_CAPABILITY`.
|
|
72
|
+
|
|
73
|
+
## assert, waitFor, extract: one question
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { z } from 'zod';
|
|
77
|
+
|
|
78
|
+
await agent.assert('the dashboard shows a trial badge'); // one look, one judgment
|
|
79
|
+
|
|
80
|
+
await agent.waitFor('the export finished and a download link appeared', { // polls
|
|
81
|
+
intervalMs: 500,
|
|
82
|
+
timeout: 120_000,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const data = await agent.extract('every todo title and how many remain', { // structured output
|
|
86
|
+
schema: z.object({ titles: z.array(z.string()), remaining: z.number().int() }),
|
|
87
|
+
});
|
|
88
|
+
expect(data.titles).toContain('Buy milk');
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `assert` does not poll. A false judgment is `ASSERTION_FAILED` with the
|
|
92
|
+
model's explanation and a screenshot in the report. Malformed output gets
|
|
93
|
+
one repair round, then `MODEL_OUTPUT_INVALID`.
|
|
94
|
+
- `waitFor` observes every `intervalMs` (default 3 s) and spends a judgment
|
|
95
|
+
only when the screen changed; `STEP_TIMEOUT` after `timeout` (default
|
|
96
|
+
30 s).
|
|
97
|
+
- `extract` accepts any Standard Schema validator (zod works). Invalid output
|
|
98
|
+
gets one repair round, then `MODEL_OUTPUT_INVALID`.
|
|
99
|
+
- Judgments are never cached and always read a fresh observation.
|
|
100
|
+
|
|
101
|
+
`vision` on a judgment controls the evidence: `false` (default) the semantic
|
|
102
|
+
tree; `true` the tree plus a masked screenshot; `'only'` the screenshot
|
|
103
|
+
alone. Use `'only'` for a question about what the screen presents (an
|
|
104
|
+
overlay, a broken layout, a chart), because the tree would otherwise answer
|
|
105
|
+
first. Pixels show the viewport only and are withheld once a secret was
|
|
106
|
+
filled in the attempt.
|
|
107
|
+
|
|
108
|
+
## Write instructions the model can execute
|
|
109
|
+
|
|
110
|
+
- One goal per `act`. The order of goals is the test's; the path inside a
|
|
111
|
+
goal is the model's.
|
|
112
|
+
- Use the words on screen: `'open the Billing tab and choose the Pro plan'`,
|
|
113
|
+
not `'upgrade'` when no control says so.
|
|
114
|
+
- Values go in params, never in the sentence:
|
|
115
|
+
`act('rename the project to {name}', { name })`. The runner does not
|
|
116
|
+
expand `{name}`; the model receives the instruction as written plus the
|
|
117
|
+
params as a separate block and reads the value from there.
|
|
118
|
+
- Give vocabulary once, in `agent.context` or `agentContext`, instead of
|
|
119
|
+
repeating it in every instruction.
|
|
120
|
+
- Do not describe mechanics the runner already handles: waiting, scrolling
|
|
121
|
+
into view, retries.
|
|
122
|
+
- Pin the outcome of every `act` deterministically, right after it:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
await agent.act('create a workspace named "Atlas" on the Pro plan');
|
|
126
|
+
await expect(screen.getByRole('status')).toHaveText('Created "Atlas" on the Pro plan');
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The check makes the test model-portable (the path may differ between models,
|
|
130
|
+
the end state may not), and it is what lets the trace cache record the step.
|
|
131
|
+
|
|
132
|
+
## What the model sees
|
|
133
|
+
|
|
134
|
+
A redacted snapshot of the screen (roles, names, text, states), a summary of
|
|
135
|
+
prior steps, and your context. Never raw HTML, cookies, headers, environment
|
|
136
|
+
values, or a `Secret`'s value; password fields arrive masked. Pixels only
|
|
137
|
+
with `vision`, and only while no secret has been filled. Nothing the model
|
|
138
|
+
returns runs as code or selectors: the runner validates and authorizes every
|
|
139
|
+
tool call before it executes.
|
|
140
|
+
|
|
141
|
+
## Budgets and cost
|
|
142
|
+
|
|
143
|
+
| Call | Model calls | Default timeout |
|
|
144
|
+
| --- | ---: | --- |
|
|
145
|
+
| `act` | up to `agent.maxModelCalls` (25) | the test `timeout`, 120 s |
|
|
146
|
+
| `assert` | 2 | 30 s |
|
|
147
|
+
| `extract` | 2 | 30 s |
|
|
148
|
+
| `waitFor` | up to `agent.maxModelCalls` (25) | 30 s |
|
|
149
|
+
|
|
150
|
+
- Keep `agent.*` for steps whose path or wording varies; `expect` and
|
|
151
|
+
`screen` are free.
|
|
152
|
+
- Slow providers: raise the test `timeout` and `actionTimeout` (each
|
|
153
|
+
observation and action inside a step is bounded by it) rather than reading
|
|
154
|
+
latency as a defect. `STEP_TIMEOUT` and `STEP_BUDGET_EXHAUSTED` are test
|
|
155
|
+
failures: scope the goal smaller or split it.
|
|
156
|
+
- `--debug` prints a per-step table (duration, model calls, tokens, cost)
|
|
157
|
+
after the run and saves each step's transcript as an artifact.
|
|
158
|
+
|
|
159
|
+
## The trace cache
|
|
160
|
+
|
|
161
|
+
Each passing `agent.act` records the actions it performed. The next run
|
|
162
|
+
replays them with zero model calls and hands back to the live agent the
|
|
163
|
+
moment the app no longer matches the recording, or when the recorded end
|
|
164
|
+
state is not on screen after the replay.
|
|
165
|
+
|
|
166
|
+
- On by default (`read-write`), `read-only` in CI, `cache: 'off'` in the
|
|
167
|
+
config or `--no-cache` on a run to disable. Entries live in `.e2e/cache/`;
|
|
168
|
+
deleting the directory only slows the next run.
|
|
169
|
+
- An entry is written only after a later verification step passes
|
|
170
|
+
(`expect`, `locator.waitFor`, `agent.assert`, `agent.waitFor`). An `act`
|
|
171
|
+
nothing checks is never replayed.
|
|
172
|
+
- `e2e init` gitignores `.e2e/cache/`; committing entries is opt-in. Remove
|
|
173
|
+
that line to share replays with CI and teammates (CI stays `read-only`
|
|
174
|
+
unless `cache: 'read-write'` is set explicitly).
|
|
175
|
+
- A failing run evicts the entries it implicates. To rule the cache out of a
|
|
176
|
+
failure, run with `--no-cache`.
|
|
177
|
+
|
|
178
|
+
## Inspect what the model did
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npx --no-install e2e run tests/checkout.e2e.ts --debug # step table, transcripts as artifacts
|
|
182
|
+
npx --no-install e2e run tests/checkout.e2e.ts --ai-trace # writes .e2e/ai-trace.json
|
|
183
|
+
npx unbox-ai runs .e2e/ai-trace.json # one line per agent step
|
|
184
|
+
npx unbox-ai summary .e2e/ai-trace.json --run 0 # turns, tokens, tool calls of one step
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Never read `.e2e/ai-trace.json` directly; it is megabytes of resent context.
|
|
188
|
+
Use `--no-cache` when the whole flow should be traced, since replayed steps
|
|
189
|
+
make no model calls.
|
|
190
|
+
|
|
191
|
+
## Beyond the built-in agent
|
|
192
|
+
|
|
193
|
+
- `createAgent({ tools: { seedCart } })` adds AI SDK tools wrapped with
|
|
194
|
+
`defineTool(tool({ ... }), { mutates: true })` from `@e2edev/e2e/agent`, so
|
|
195
|
+
a flow can call a test API mid-step.
|
|
196
|
+
- `createToolLoopExecutor` keeps the loop and replaces the prompt and the
|
|
197
|
+
tool vocabulary.
|
|
198
|
+
- Any object implementing `StepExecutor` (`{ name, version, cache, runStep(ctx) }`)
|
|
199
|
+
can be the `agent`; the runner still owns observations, actions, budgets,
|
|
200
|
+
and the report.
|
|
201
|
+
|
|
202
|
+
Full reference: https://e2e.docs.buildwithfern.com/agents
|
|
203
|
+
|
|
204
|
+
## In CI
|
|
205
|
+
|
|
206
|
+
Deterministic tests gate merges; agentic tests are opt-in. Keep them in a
|
|
207
|
+
separate config (`e2e.agent.config.ts` with its own `tests` glob and a larger
|
|
208
|
+
`timeout`), run them on a schedule or `workflow_dispatch`, and pass the model
|
|
209
|
+
through `env: { E2E_MODEL, E2E_MODEL_API_KEY }` from CI variables and
|
|
210
|
+
secrets.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Debugging a failing run
|
|
2
|
+
|
|
3
|
+
## Read the failure
|
|
4
|
+
|
|
5
|
+
1. The `list` reporter ends with a `Failed Tests` section: each failure shows
|
|
6
|
+
the error code, the message, the test's failing line, and a code frame.
|
|
7
|
+
The summary's `Report` line names `.e2e/report.json`.
|
|
8
|
+
2. `.e2e/report.json` has everything:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
jq '.run | {status, exitCode, errors}' .e2e/report.json # run-level failures
|
|
12
|
+
jq '.run.results[] | select(.status != "passed") | {titlePath, file, status}' .e2e/report.json
|
|
13
|
+
jq '.run.results[] | select(.status != "passed") | .attempts[-1]
|
|
14
|
+
| {status, error, steps: [.steps[] | select(.status != "passed") | {api, label, status, error}], artifacts}' .e2e/report.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
3. Artifacts named there live under `.e2e/artifacts/`: screenshots, a
|
|
18
|
+
Playwright `trace.zip` per attempt (`npx playwright show-trace <file>`),
|
|
19
|
+
downloads, and with `--debug` the transcript of every agent step.
|
|
20
|
+
|
|
21
|
+
## Error codes and what to do
|
|
22
|
+
|
|
23
|
+
| Code | Usual cause | Fix |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `CONFIG_LOAD_FAILED` mentioning ES modules | `package.json` lacks `"type": "module"` | `npm pkg set type=module`, or rename the config to `.mts` and keep the tests in an ESM package |
|
|
26
|
+
| `INVALID_CONFIG` | Unknown key or a stale shape: a top-level `app`, `defineConfig`, a `backend` key, `json` combined with `list` reporters | Move app options into `playwright({ ... })`; use `satisfies E2EConfig`; the message names the key |
|
|
27
|
+
| `CONFIG_NOT_FOUND`, `CONFIG_AMBIGUOUS` | Wrong `--config` path; both `.ts` and `.mts` present | Fix the path; keep one config file |
|
|
28
|
+
| `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 |
|
|
29
|
+
| `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` |
|
|
30
|
+
| `APP_UNREACHABLE` | `command` never answered `readyUrl` within `startupTimeout`; a service exited non-zero | Set `command.log` and read it; check the port and `url`; pass the env the app needs through `command.env`; raise `startupTimeout` |
|
|
31
|
+
| `APP_ALREADY_RUNNING` | Something already serves `url` when the runner wanted to start `command` | Stop it, or set `reuseExisting: true` for local runs |
|
|
32
|
+
| `APP_URL_REQUIRED` | A navigation on an engine without `url` | Add `url` to `playwright({ ... })` |
|
|
33
|
+
| `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 |
|
|
34
|
+
| `LOCATOR_AMBIGUOUS` | Two matches: a hidden duplicate, a repeated label | Add `{ name }`, scope under a container, `filter`, `first()`, or `{ visible: true }` |
|
|
35
|
+
| `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 |
|
|
36
|
+
| `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` |
|
|
37
|
+
| `TEST_TIMEOUT` | The attempt exceeded `timeout` (120 s) | Split the test, or raise `timeout` for slow flows and agent steps |
|
|
38
|
+
| `MODEL_UNAVAILABLE` | No `E2E_MODEL`, no key, or an unknown provider | Export `E2E_MODEL=provider/model-id` and `E2E_MODEL_API_KEY`; confirm the model id |
|
|
39
|
+
| `MODEL_PROVIDER_FAILED` | Network, 5xx, rate limit, or no credits after the transport retries | Check the key and the quota; retry; exit code 3 |
|
|
40
|
+
| `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 |
|
|
41
|
+
| `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 secrets only into password inputs; assert the outcome instead of the value |
|
|
42
|
+
| `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 |
|
|
43
|
+
| `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` |
|
|
44
|
+
| `ONLY_IN_CI` | `test.only` reached CI | Remove it |
|
|
45
|
+
| `BROWSER_INSTALL_FAILED`, `LAUNCH_TIMEOUT` | Browser download or launch failed | `npx playwright install chromium --with-deps`; raise `launchTimeout` on slow machines |
|
|
46
|
+
| `AUTH_CREDENTIAL_UNAVAILABLE` | `credentials.user('x')` for an undeclared name | Add it to `config.credentials` |
|
|
47
|
+
|
|
48
|
+
## Tools
|
|
49
|
+
|
|
50
|
+
| Do | When |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `--headed` | Watch the flow; confirm what the page shows at the failing step |
|
|
53
|
+
| `--workers 1 --retries 0` | Remove parallelism and retries from the picture |
|
|
54
|
+
| `--no-cache` | Rule out a stale `agent.act` replay |
|
|
55
|
+
| `--debug` | Read each agent step's duration, model calls, cost, and transcript |
|
|
56
|
+
| `--ai-trace`, then `npx unbox-ai runs .e2e/ai-trace.json` | See exactly what the model was shown and called |
|
|
57
|
+
| `command.log: '.e2e/logs/app.log'` | Read the app's own output when it never becomes ready or errors mid-test |
|
|
58
|
+
| `await app.screenshot('before-submit')` | Attach evidence at a chosen point |
|
|
59
|
+
| `CI=1 npx --no-install e2e run` | Reproduce CI-only behaviour: `ONLY_IN_CI`, read-only cache, `reuseExisting` ignored |
|
|
60
|
+
|
|
61
|
+
## Flaky tests
|
|
62
|
+
|
|
63
|
+
- A read (`textContent()`, `count()`) captured a value mid-update: replace
|
|
64
|
+
it with a matcher.
|
|
65
|
+
- Shared data between tests: unique names per run, cleanup in `afterEach`,
|
|
66
|
+
or a `serial` group.
|
|
67
|
+
- The app is not ready: assert on the element you are about to use rather
|
|
68
|
+
than on the previous page.
|
|
69
|
+
- An agent judgment asserts an exact phrasing: judge the fact, and add a
|
|
70
|
+
deterministic `expect` beside it.
|
|
71
|
+
- Timing under load: `retries` masks the cause; `--workers 1` and
|
|
72
|
+
`--headed` usually show it.
|
|
73
|
+
|
|
74
|
+
## Is it the app?
|
|
75
|
+
|
|
76
|
+
A deterministic step that fails every run at the same place with the same
|
|
77
|
+
code is a product bug or a changed screen, not flakiness. Reproduce once
|
|
78
|
+
with `--headed`, then fix the app, or update the locator and the expectation
|
|
79
|
+
together. A blocked agent step (`AUTH_CREDENTIAL_UNAVAILABLE`,
|
|
80
|
+
`ENVIRONMENT_UNAVAILABLE`, `SEED_DATA_MISSING`) exits 2 or 3 on purpose: fix
|
|
81
|
+
the environment, not the test.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Running tests
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx --no-install e2e run [files...] [options] # run tests
|
|
7
|
+
npx --no-install e2e list [files...] [options] # print what run would select, without running
|
|
8
|
+
npx --no-install e2e init [--yes] # scaffold a project, refresh the agent skill
|
|
9
|
+
npx --no-install e2e guide [topic] # print this skill: setup, writing-tests, agent, running, debugging
|
|
10
|
+
npx --no-install e2e cache ls|clear|stats # read or empty the trace cache
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`run` flags:
|
|
14
|
+
|
|
15
|
+
| Flag | Effect |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `[files...]` | Files, directories, or quoted globs relative to the project root. They narrow the config `tests` glob, never bypass it. |
|
|
18
|
+
| `--config <path>` | Explicit config file. Default: `e2e.config.ts` or `.mts` found upward from the working directory. |
|
|
19
|
+
| `--target <ids>` | Comma-separated target names. |
|
|
20
|
+
| `--tag <tag>` | Repeatable tag filter; `--tag-mode all` requires every tag. |
|
|
21
|
+
| `--headed` | Visible browser or simulator when the engine supports it. |
|
|
22
|
+
| `--workers <n>`, `--retries <n>` | Override the resolved values. |
|
|
23
|
+
| `--reporter <ids>` | `list`, `json`, `junit`, comma-separated. `json` cannot combine with `list`. |
|
|
24
|
+
| `--artifacts <dir>` | Artifact root, default `.e2e/artifacts`. |
|
|
25
|
+
| `--no-cache` | Run with the trace cache off. |
|
|
26
|
+
| `--pass-with-no-tests` | Exit 0 when nothing matches instead of `NO_TESTS`. |
|
|
27
|
+
| `--debug` | Phase timings and an agent step table on stderr; step transcripts saved as artifacts. |
|
|
28
|
+
| `--ai-trace` | Record every model call to `.e2e/ai-trace.json`. |
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx --no-install e2e run tests/signup.e2e.ts
|
|
32
|
+
npx --no-install e2e run tests/agent --tag smoke
|
|
33
|
+
npx --no-install e2e run 'tests/**/*.smoke.e2e.ts' --target chromium --workers 1 --retries 0
|
|
34
|
+
CI=1 npx --no-install e2e run # reproduce the CI defaults locally
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`list` takes the same files and the selection flags (`--config`, `--target`,
|
|
38
|
+
`--tag`, `--tag-mode`, `--pass-with-no-tests`) and prints one line per
|
|
39
|
+
test-target pair, `file › title [target]`, then exits without starting the
|
|
40
|
+
app, an engine, or a worker. `--reporter json` prints `{ "pairs": [...] }`.
|
|
41
|
+
Use it to check a filter before a run.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx --no-install e2e list --tag smoke
|
|
45
|
+
npx --no-install e2e list tests/signup.e2e.ts --reporter json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A `package.json` script keeps it short: `"test:e2e": "e2e run"`, then
|
|
49
|
+
`pnpm test:e2e tests/signup.e2e.ts`.
|
|
50
|
+
|
|
51
|
+
## The trace cache
|
|
52
|
+
|
|
53
|
+
Entries live under `.e2e/cache/`, one file per key, named after the key
|
|
54
|
+
digest. `cache` commands read the same config as `run`, so `--config` and
|
|
55
|
+
`cache.dir` point them at the right store.
|
|
56
|
+
|
|
57
|
+
| Command | Prints |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `e2e cache ls` | One row per entry: test, target, instruction digest, age, action count. |
|
|
60
|
+
| `e2e cache stats` | Directory, entry count, total size. |
|
|
61
|
+
| `e2e cache clear` | Deletes the entries and the directory; files the runner never wrote stay. |
|
|
62
|
+
|
|
63
|
+
Use `ls` to see what a committed cache would replay, and `clear` when a
|
|
64
|
+
recorded flow is stale — `--no-cache` only skips the cache for one run.
|
|
65
|
+
|
|
66
|
+
## Output
|
|
67
|
+
|
|
68
|
+
- `list` (default): one line per file and target, a `Failed Tests` section
|
|
69
|
+
with each error, its code, the failing line and a code frame, then a
|
|
70
|
+
summary (`Test Files`, `Tests`, `AI`, `Duration`, `Report`).
|
|
71
|
+
- `.e2e/report.json` is written on every run whatever the reporters:
|
|
72
|
+
`run.status`, `run.exitCode`, `run.errors[]` (run-level failures such as
|
|
73
|
+
`APP_UNREACHABLE`), and `run.results[]`, one per test and target, with
|
|
74
|
+
`titlePath`, `file`, `source`, `status`, and `attempts[]` holding `steps[]`,
|
|
75
|
+
`artifacts[]`, and `error`.
|
|
76
|
+
- `junit`: `.e2e/junit.xml` beside the report, for CI test summaries.
|
|
77
|
+
Combine it with the terminal output: `--reporter list,junit`.
|
|
78
|
+
- `json`: the report document on stdout.
|
|
79
|
+
- Artifacts (screenshots, Playwright traces, `--debug` transcripts,
|
|
80
|
+
downloads) live under `.e2e/artifacts/`; every path is recorded in the
|
|
81
|
+
report.
|
|
82
|
+
|
|
83
|
+
## Exit codes
|
|
84
|
+
|
|
85
|
+
| Code | Meaning |
|
|
86
|
+
| ---: | --- |
|
|
87
|
+
| 0 | Every selected test passed, was flaky, or was skipped |
|
|
88
|
+
| 1 | A test or setup test failed or timed out |
|
|
89
|
+
| 2 | CLI, config, collection, credential, model-config, or policy error |
|
|
90
|
+
| 3 | Engine, app process, model provider, artifact, or cleanup failure |
|
|
91
|
+
| 4 | Internal runner error |
|
|
92
|
+
| 130 | Interrupted |
|
|
93
|
+
|
|
94
|
+
The highest code present wins for a mixed run (`130 > 4 > 3 > 2 > 1 > 0`).
|
|
95
|
+
Do not retry a job on exit 2: it is deterministic. Exit 3 is the only one
|
|
96
|
+
where a job-level retry makes sense.
|
|
97
|
+
|
|
98
|
+
Ctrl-C once interrupts and still writes the report; twice forces teardown;
|
|
99
|
+
three times exits at once.
|
|
100
|
+
|
|
101
|
+
## Continuous integration
|
|
102
|
+
|
|
103
|
+
CI mode is on when `CI` is set (and not `0` or `false`). It changes
|
|
104
|
+
`retries` to 1, `workers` to 1, rejects `test.only` with `ONLY_IN_CI`, makes
|
|
105
|
+
the trace cache `read-only`, and ignores `reuseExisting`.
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
# .github/workflows/e2e.yml
|
|
109
|
+
name: e2e
|
|
110
|
+
on:
|
|
111
|
+
pull_request:
|
|
112
|
+
push:
|
|
113
|
+
branches: [main]
|
|
114
|
+
permissions:
|
|
115
|
+
contents: read
|
|
116
|
+
jobs:
|
|
117
|
+
e2e:
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
steps:
|
|
120
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
121
|
+
- uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
|
|
122
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
123
|
+
with:
|
|
124
|
+
node-version: 26
|
|
125
|
+
cache: pnpm
|
|
126
|
+
- run: pnpm install --frozen-lockfile
|
|
127
|
+
- run: npx playwright install chromium --with-deps
|
|
128
|
+
- run: npx --no-install e2e run --reporter list,junit
|
|
129
|
+
env:
|
|
130
|
+
E2E_USER_ADMIN_USERNAME: ${{ secrets.E2E_USER_ADMIN_USERNAME }}
|
|
131
|
+
E2E_USER_ADMIN_PASSWORD: ${{ secrets.E2E_USER_ADMIN_PASSWORD }}
|
|
132
|
+
- if: ${{ !cancelled() }}
|
|
133
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
134
|
+
with:
|
|
135
|
+
name: e2e-report
|
|
136
|
+
path: |
|
|
137
|
+
.e2e/report.json
|
|
138
|
+
.e2e/junit.xml
|
|
139
|
+
- if: failure()
|
|
140
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
141
|
+
with:
|
|
142
|
+
name: e2e-artifacts
|
|
143
|
+
path: .e2e/artifacts
|
|
144
|
+
retention-days: 7
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- Install browsers as their own step so the download never counts against a
|
|
148
|
+
launch timeout.
|
|
149
|
+
- Start the app through the engine's `command`; the runner tears it down on
|
|
150
|
+
every exit path.
|
|
151
|
+
- Agentic suites: a separate config, run on `schedule` or
|
|
152
|
+
`workflow_dispatch`, `E2E_MODEL` as a CI variable and `E2E_MODEL_API_KEY`
|
|
153
|
+
as a secret, never a required check.
|