e2e 0.17.0 → 0.18.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 +10 -0
- package/dist/.build.json +1 -1
- package/dist/agent/ai-sdk.js +5 -4
- package/dist/agent/error.js +3 -3
- package/dist/agent/invocation.js +3 -4
- package/dist/agent/model/tool-choice.d.ts +9 -0
- package/dist/agent/model/tool-choice.js +14 -0
- package/dist/agent/observation-feed.js +3 -4
- package/dist/agent/phases.d.ts +7 -1
- package/dist/agent/phases.js +12 -3
- package/dist/agent/prompts.js +3 -0
- package/dist/agent/replay.js +18 -15
- package/dist/agent/step-cache.d.ts +2 -1
- package/dist/agent/step-cache.js +9 -5
- package/dist/agent/tool-loop.js +92 -5
- package/dist/cache/trace.js +6 -2
- package/dist/cli/bin.js +2 -2
- package/dist/cli/index.js +35 -24
- package/dist/cli/init/mcp-config.d.ts +2 -1
- package/dist/cli/init/mcp-config.js +8 -3
- package/dist/cli/init/sibling-versions.json +1 -1
- package/dist/cli/mcp.d.ts +3 -2
- package/dist/cli/mcp.js +52 -16
- package/dist/cli/run-output.d.ts +17 -0
- package/dist/cli/run-output.js +45 -0
- package/dist/collect/collect.js +8 -4
- package/dist/collect/registry.js +27 -16
- package/dist/config/compiled-files.d.ts +36 -0
- package/dist/config/compiled-files.js +50 -0
- package/dist/config/diagnose.js +21 -19
- package/dist/config/esm-hooks.d.ts +39 -44
- package/dist/config/esm-hooks.js +238 -63
- package/dist/config/load.d.ts +1 -7
- package/dist/config/load.js +23 -66
- package/dist/config/register.d.ts +8 -0
- package/dist/config/register.js +9 -0
- package/dist/config/specifiers.d.ts +19 -0
- package/dist/config/specifiers.js +33 -0
- package/dist/config/tsconfig.d.ts +56 -0
- package/dist/config/tsconfig.js +115 -0
- package/dist/config/typescript.d.ts +19 -0
- package/dist/config/typescript.js +253 -0
- package/dist/engine/contract.d.ts +1 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/session.js +2 -2
- package/dist/expect/async.js +2 -1
- package/dist/expect/poll.js +2 -2
- package/dist/internal/errors.d.ts +10 -3
- package/dist/internal/errors.js +23 -8
- package/dist/internal/node-version.d.ts +16 -7
- package/dist/internal/node-version.js +50 -15
- package/dist/internal/package-manager.js +1 -1
- package/dist/internal/realm-slot.d.ts +5 -4
- package/dist/internal/redact.d.ts +2 -0
- package/dist/internal/redact.js +4 -0
- package/dist/internal/time.d.ts +21 -5
- package/dist/internal/time.js +35 -3
- package/dist/locator/engine.js +47 -11
- package/dist/locator/screen.js +31 -5
- package/dist/mcp/output.d.ts +59 -0
- package/dist/mcp/output.js +129 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +9 -12
- package/dist/mcp/session.d.ts +8 -1
- package/dist/mcp/session.js +55 -14
- package/dist/mcp/tools.js +7 -5
- package/dist/report/list.d.ts +11 -0
- package/dist/report/list.js +23 -6
- package/dist/run/artifacts.d.ts +4 -4
- package/dist/run/artifacts.js +3 -2
- package/dist/run/execute.d.ts +25 -5
- package/dist/run/execute.js +44 -20
- package/dist/run/failure-evidence.js +6 -2
- package/dist/run/in-process.js +51 -10
- package/dist/run/process-output.d.ts +73 -0
- package/dist/run/process-output.js +130 -0
- package/dist/run/provision.d.ts +2 -0
- package/dist/run/provision.js +1 -0
- package/dist/run/records.d.ts +4 -4
- package/dist/run/retry.d.ts +7 -0
- package/dist/run/retry.js +27 -11
- package/dist/run/runner.d.ts +8 -0
- package/dist/run/runner.js +32 -9
- package/dist/run/scheduler.js +255 -19
- package/dist/run/secrecy.d.ts +22 -19
- package/dist/run/secrecy.js +22 -18
- package/dist/run/serial.d.ts +73 -2
- package/dist/run/serial.js +239 -58
- package/dist/run/standalone.js +1 -1
- package/dist/run/trace-redaction.d.ts +5 -6
- package/dist/run/trace-redaction.js +5 -6
- package/dist/run/worker/entry.js +35 -14
- package/dist/run/worker/handle.js +10 -5
- package/dist/run/worker/protocol.d.ts +83 -3
- package/dist/run/worker/session.js +28 -0
- package/dist/telemetry/environment.d.ts +2 -1
- package/dist/telemetry/environment.js +12 -2
- package/dist/telemetry/project.d.ts +5 -6
- package/dist/telemetry/project.js +6 -11
- package/dist/telemetry/store.d.ts +5 -5
- package/dist/telemetry/store.js +5 -7
- package/dist/telemetry/telemetry.d.ts +2 -3
- package/dist/telemetry/telemetry.js +2 -4
- package/dist/types.d.ts +6 -3
- package/docs/agent-steps.mdx +1 -0
- package/docs/agents.mdx +1 -0
- package/docs/api-testing.mdx +2 -1
- package/docs/authentication.mdx +3 -2
- package/docs/browser.mdx +7 -6
- package/docs/bug-bash.mdx +2 -1
- package/docs/cache.mdx +2 -1
- package/docs/ci.mdx +3 -2
- package/docs/coding-agents.mdx +2 -1
- package/docs/core-concepts.mdx +208 -0
- package/docs/debugging.mdx +19 -9
- package/docs/decision-models.mdx +142 -0
- package/docs/example-projects.mdx +32 -0
- package/docs/executors.mdx +3 -0
- package/docs/explore.mdx +1 -0
- package/docs/github.mdx +2 -1
- package/docs/index.mdx +9 -5
- package/docs/integrations/eas.mdx +1 -0
- package/docs/integrations/index.mdx +1 -0
- package/docs/integrations/kernel.mdx +2 -1
- package/docs/migrate/cypress.mdx +26 -3
- package/docs/migrate/detox.mdx +24 -1
- package/docs/migrate/maestro.mdx +28 -4
- package/docs/migrate/playwright.mdx +27 -4
- package/docs/migrate/selenium.mdx +27 -3
- package/docs/mobile-ci.mdx +6 -5
- package/docs/mobile.mdx +79 -8
- package/docs/models.mdx +7 -2
- package/docs/quickstart.mdx +26 -15
- package/docs/reference/agent.mdx +13 -3
- package/docs/reference/app.mdx +1 -0
- package/docs/reference/cli.mdx +11 -5
- package/docs/reference/config.mdx +42 -6
- package/docs/reference/engine.mdx +22 -10
- package/docs/reference/environment.mdx +2 -1
- package/docs/reference/errors.mdx +11 -8
- package/docs/reference/expect.mdx +2 -1
- package/docs/reference/mcp.mdx +21 -8
- package/docs/reference/mobile.mdx +1 -0
- package/docs/reference/reporters.mdx +1 -0
- package/docs/reference/screen.mdx +19 -9
- package/docs/reference/test.mdx +21 -1
- package/docs/reference/web.mdx +8 -2
- package/docs/security.mdx +33 -27
- package/docs/subscriptions.mdx +1 -0
- package/docs/telemetry.mdx +8 -5
- package/docs/tools.mdx +1 -0
- package/docs/{starting-your-app.mdx → web.mdx} +157 -11
- package/docs/writing-an-engine.mdx +2 -1
- package/package.json +11 -7
- package/skills/e2e/SKILL.md +3 -1
- package/skills/e2e/references/agent.md +11 -0
- package/skills/e2e/references/debugging.md +1 -1
- package/skills/e2e/references/mcp.md +8 -3
- package/skills/e2e/references/running.md +2 -1
- package/skills/e2e/references/setup.md +9 -1
- package/skills/e2e/references/writing-tests.md +1 -1
- package/docs/assertions.mdx +0 -94
- package/docs/goals.mdx +0 -176
- package/docs/locators.mdx +0 -64
- package/docs/physical-devices.mdx +0 -76
- package/docs/viewports.mdx +0 -146
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: E2EConfig
|
|
3
3
|
description: Every configuration key, its default, its valid range, and the environment variables.
|
|
4
|
+
icon: "sliders"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Export an object from `e2e.config.ts` and use `satisfies E2EConfig` to check
|
|
@@ -69,10 +70,45 @@ It throws when the file is missing; guard it with `existsSync` from
|
|
|
69
70
|
`node:fs` when only some machines have one.
|
|
70
71
|
|
|
71
72
|
Config and test files run as ESM whatever the nearest `package.json` `type`
|
|
72
|
-
says, and so does
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
says, and so does every `.ts`, `.mts`, `.tsx`, and `.jsx` file they reach: a
|
|
74
|
+
helper by path, a package that exports its `.ts` source by name, or a `.ts`
|
|
75
|
+
file a CommonJS file requires. A `.cts` file is CommonJS, written with
|
|
76
|
+
`require`, `import x = require()`, and `export =`; its `import` statements are
|
|
77
|
+
not turned into `require` calls. A `.ts` file written with `require` or
|
|
78
|
+
`module.exports` fails to load: name it `.cts`. JavaScript keeps the format
|
|
79
|
+
its package declares.
|
|
80
|
+
|
|
81
|
+
e2e compiles TypeScript and JSX itself, and an import written in such a file
|
|
82
|
+
resolves the way TypeScript resolves it: `./helper.js` and `./helper` load
|
|
83
|
+
`helper.ts`, `./view.jsx` loads `view.tsx`, `./lib` loads the file
|
|
84
|
+
`lib/package.json` names as `main`, else `lib/index.ts`, a bare import that
|
|
85
|
+
`tsconfig.json` maps through `paths` or `baseUrl` loads the mapped file, and
|
|
86
|
+
a `#` import or package export that names a missing `./x.js`, `./x.jsx`, or
|
|
87
|
+
`./x` loads its `x.ts` or `x.tsx` source when imported (a `require()` of it fails as Node.js reports
|
|
88
|
+
it). An import written in a JavaScript file resolves
|
|
89
|
+
as Node.js resolves it.
|
|
90
|
+
|
|
91
|
+
The `tsconfig.json` is the nearest one above the file, or, when that one has
|
|
92
|
+
`references` and does not include the file itself (as Vite writes it), the
|
|
93
|
+
directly referenced one that does. e2e reads its `paths`, `baseUrl`, `jsx`, `jsxFactory`,
|
|
94
|
+
`jsxFragmentFactory`, `jsxImportSource`, `experimentalDecorators`,
|
|
95
|
+
`emitDecoratorMetadata`, `useDefineForClassFields` (and `target`, which sets
|
|
96
|
+
its default), and `verbatimModuleSyntax`, once per process and again for each
|
|
97
|
+
session `e2e mcp` opens. A `tsconfig.json` e2e cannot read, such as one that
|
|
98
|
+
`extends` a package that is not installed, is skipped with a warning.
|
|
99
|
+
TypeScript inside an installed package compiles without the project's
|
|
100
|
+
`tsconfig.json`.
|
|
101
|
+
|
|
102
|
+
e2e refuses, naming the file and line, two things it cannot compile:
|
|
103
|
+
auto-accessors (`accessor x = 1`) and decorators without
|
|
104
|
+
`experimentalDecorators`. Each file compiles on its own, without type
|
|
105
|
+
information, so a type imported without `import type` fails to load when the
|
|
106
|
+
import stays in the output (a decorated member's type under
|
|
107
|
+
`emitDecoratorMetadata`, a re-exported type); the error names the import to
|
|
108
|
+
change. A JSON file imported without `with { type: 'json' }` loads with its
|
|
109
|
+
parsed contents as the default export. Stack traces and failure locations
|
|
110
|
+
point at the TypeScript source, through another loader's source map too
|
|
111
|
+
(`NODE_OPTIONS=--import tsx`).
|
|
76
112
|
|
|
77
113
|
## Top level
|
|
78
114
|
|
|
@@ -895,7 +931,7 @@ See [Secrets that are not passwords](/authentication#secrets-that-are-not-passwo
|
|
|
895
931
|
In `read-write` mode the replay cache records an `agent.act()` only after a
|
|
896
932
|
later verification passes, such as a locator assertion or `agent.assert()`.
|
|
897
933
|
`read-only` replays existing entries and writes none; `off` disables both.
|
|
898
|
-
See [Caching
|
|
934
|
+
See [Caching](/cache).
|
|
899
935
|
|
|
900
936
|
```ts
|
|
901
937
|
/**
|
|
@@ -950,7 +986,7 @@ instead of a hand-off to the agent; see
|
|
|
950
986
|
`e2e init` adds `.e2e/cache/` to `.gitignore`, so committing entries is
|
|
951
987
|
opt-in. Remove that line to share replays with CI and teammates; CI stays
|
|
952
988
|
`read-only` unless `cache: 'read-write'` is set. See
|
|
953
|
-
[Caching
|
|
989
|
+
[Caching](/cache#commit-the-replay-cache).
|
|
954
990
|
|
|
955
991
|
## Run limits
|
|
956
992
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: e2e/engine
|
|
3
3
|
description: Build an engine with observations, actions, lifecycle hooks, and fixtures.
|
|
4
|
+
icon: "gear"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Import engine types and helpers from `e2e/engine`. `defineEngine` validates
|
|
@@ -709,7 +710,11 @@ export type PointerAction =
|
|
|
709
710
|
readonly direction: ScrollDirection;
|
|
710
711
|
readonly momentum?: Momentum;
|
|
711
712
|
}
|
|
712
|
-
| {
|
|
713
|
+
| {
|
|
714
|
+
readonly kind: 'swipeTo';
|
|
715
|
+
readonly target: ViewportPoint;
|
|
716
|
+
readonly durationMs?: number;
|
|
717
|
+
};
|
|
713
718
|
|
|
714
719
|
/** Every pointer action kind; what an engine declares in `pointerActions`. */
|
|
715
720
|
export type PointerActionKind = PointerAction['kind'];
|
|
@@ -923,6 +928,13 @@ export const RETRYABLE_ENGINE_ERROR_CODES: ReadonlySet<EngineErrorCode> = new Se
|
|
|
923
928
|
| `INVALID_STATE` | `APP_NOT_OPEN` | no |
|
|
924
929
|
| `ENGINE_FAILURE`, any non-`EngineError` | infrastructure `ENGINE_FAILURE` | no |
|
|
925
930
|
|
|
931
|
+
An `OPERATION_TIMEOUT` on a locate or read that started with under 100 ms of
|
|
932
|
+
a waiting call's deadline left, after one that completed, ends the wait
|
|
933
|
+
instead: an action's or `waitFor`'s `LOCATOR_NOT_FOUND`, or an assertion's
|
|
934
|
+
`ASSERTION_FAILED`, with the timeout as its cause. A negated assertion passes
|
|
935
|
+
there if its negation had held long enough by the time the read was cut off, as
|
|
936
|
+
`pollCondition` below describes.
|
|
937
|
+
|
|
926
938
|
An `EngineError` a contributed fixture method throws is mapped by the table
|
|
927
939
|
above, so a fixture over a session that ended reports `APP_NOT_OPEN` the way
|
|
928
940
|
an observation would. A method can also fail a step with the runner's own
|
|
@@ -961,7 +973,7 @@ Use these helpers to keep behavior consistent across engines:
|
|
|
961
973
|
| `matchesText(actual, pattern)`, `toTextPattern(match, { exact? })`, `describePattern(pattern)` | `TextPattern` matching for `locate` and text matchers; `toTextPattern` throws `INVALID_ARGUMENT` for a `match` that is neither a string nor a `RegExp` |
|
|
962
974
|
| `resolveExpression(expression, nodes, { selector? })` | The reference `locate`. Resolves one `LocatorExpression` over a semantic tree in document order, with `text` and `label` answering the innermost match when a container echoes a descendant's text. An engine whose tree is the whole truth (a device) calls it; a browser engine reproduces it. A `selector` needs the hook, a `frame` is `FRAME_NOT_FOUND` |
|
|
963
975
|
| `urlMatches(current, expected, baseHref, ignoreCase?)` | `toHaveURL`-style comparison, `ignoreCase` as on the locator matchers; an `expected` that is neither a string nor a `RegExp` throws `INVALID_ARGUMENT`, as in `toTextPattern` |
|
|
964
|
-
| `pollCondition({ deadline, signal, negated, evaluate, onTimeout })`, `Deadline` | Assertion polling and the negation grace window |
|
|
976
|
+
| `pollCondition({ deadline, signal, negated, evaluate, onTimeout })`, `Deadline` | Assertion polling and the negation grace window. An `OPERATION_TIMEOUT` from an `evaluate` that started with under 100 ms of the deadline left, after one that completed, ends the poll: `onTimeout(cause)` builds the failure with it, and a negation passes if it had held long enough by the time that read was cut off. Bound each read `evaluate` makes by `deadline` (`context.operation(deadline.remaining())`): one on the action timeout can outlast the assertion's own |
|
|
965
977
|
| `validateJsonValue(value, label, rules?)` | Checks a value is plain JSON: a top-level `undefined` passes; a non-finite number, a cycle, an object that is not a plain object or array, or a function, symbol, bigint, or `undefined` inside is `INVALID_ARGUMENT` naming `label`. A `Secret` is `POLICY_DENIED` unless `rules.onSecret` throws its own; `rules.maxDepth` caps the nesting |
|
|
966
978
|
| `rejectUnknownKeys(label, value, keys, code?)` | Throws `INVALID_CONFIG` for the first own key of `value` outside `keys`, naming the nearest known key when one is a plausible typo (`web() has unknown key "viewprt"; did you mean "viewport"?`) and the known keys otherwise: the check the runner runs on its own config objects, for an engine factory's options. Pass `'INVALID_ARGUMENT'` as `code` for an object a test passes to a fixture method |
|
|
967
979
|
| `rejectUnknownOptions(api, options, known, code?)` | `INVALID_ARGUMENT` (or `code`, `INVALID_LOCATOR` for a locator option) for an option key a fixture method or matcher does not take, so a JavaScript caller's unsupported option fails instead of running on the default. `options` must be a plain object or `undefined`; a class instance or array is refused, and non-enumerable own keys count as keys |
|
|
@@ -1151,16 +1163,16 @@ startedAt }` for `stopVideo` to hand back. A `stop` that throws or returns
|
|
|
1151
1163
|
anything else is `ENGINE_FAILURE`, naming the provider and the lease. The
|
|
1152
1164
|
target argument's type is exported as `ProviderRecordingTarget`.
|
|
1153
1165
|
|
|
1154
|
-
|
|
1155
|
-
archive
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1166
|
+
When the session knows a secret value, the runner redacts every text entry in
|
|
1167
|
+
each trace archive, so a value a test spelled into a URL or the options the
|
|
1168
|
+
engine opened the attempt with (`resolveSecret`) never stay in it. After a
|
|
1169
|
+
secret fill it also drops the screencast frames: every `screencast/` entry,
|
|
1170
|
+
each `screencast-frame` record in a `.trace` entry, and any image a record
|
|
1171
|
+
names.
|
|
1160
1172
|
If redaction fails, it deletes the archives and records
|
|
1161
1173
|
`TRACE_WITHHELD`. Videos are unmasked and carry `redaction: 'incomplete'`.
|
|
1162
|
-
A `download` an engine attaches is the app's bytes:
|
|
1163
|
-
|
|
1174
|
+
A `download` an engine attaches is the app's bytes: when the session knows
|
|
1175
|
+
a secret value, the runner rewrites a text one (`.txt`,
|
|
1164
1176
|
`.csv`, `.json`, `.html`) through the ledger and labels it `complete`; every
|
|
1165
1177
|
other download keeps its bytes and carries `redaction: 'incomplete'`.
|
|
1166
1178
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Environment variables
|
|
3
3
|
description: Every variable the runner and the first-party engines read, and what each one does.
|
|
4
|
+
icon: "dollar-sign"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
These variables configure the runner and first-party packages. In the tables,
|
|
@@ -57,7 +58,6 @@ short would take ordinary text with it.
|
|
|
57
58
|
| --- | --- | --- |
|
|
58
59
|
| `NO_COLOR` | set | Plain CLI output. Colors are otherwise on for a TTY or in CI and off when piped |
|
|
59
60
|
| `FORCE_COLOR` | set | Colored CLI output even when piped |
|
|
60
|
-
| `NODE_OPTIONS` | value | Inspected for a TypeScript `--import` loader, which decides how the runner registers its own module hooks for `.ts` config and test files |
|
|
61
61
|
| `TERM` | value | `dumb` draws the `e2e init` wordmark at rest, without animation, as in CI |
|
|
62
62
|
| `GITHUB_SHA`, `GITHUB_HEAD_REF`, `GITHUB_REF_NAME` | value | The commit and branch the report records under `run.vcs` when `git` cannot read the project root. `GITHUB_HEAD_REF` wins over `GITHUB_REF_NAME` |
|
|
63
63
|
|
|
@@ -107,6 +107,7 @@ What is sent and how to read it is on the [Telemetry](/telemetry) page.
|
|
|
107
107
|
| Variable | Reading | Effect |
|
|
108
108
|
| --- | --- | --- |
|
|
109
109
|
| `PLAYWRIGHT_BROWSERS_PATH` | value | Playwright's browser cache, honored by the engine's `prepare` hook |
|
|
110
|
+
| `PLAYWRIGHT_SKIP_BROWSER_GC` | value | Defaults to `1` for the engine's browser install, so it keeps other browsers in the cache. Set `0` to let the install remove them |
|
|
110
111
|
| `KERNEL_API_KEY` | value | The API key `kernel()` from `@e2e-dev/kernel` creates browsers with; unset, the browser lease fails. See [Kernel](/integrations/kernel) |
|
|
111
112
|
| `EXPO_TOKEN` | value | The Expo access token `easSimulators()` from `@e2e-dev/eas` starts and stops simulator sessions with; unset, it uses the eas-cli login in `.expo/state.json` under `HOME` (`USERPROFILE` on Windows), and with neither the device lease fails. See [EAS Simulators](/integrations/eas) |
|
|
112
113
|
| `HOME`, `USERPROFILE` | value | The home `easSimulators()` finds the eas-cli login under when `EXPO_TOKEN` is unset: `USERPROFILE` on Windows, `HOME` elsewhere, the OS user's home when unset |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Errors
|
|
3
3
|
description: Every code you can see, its class, and its exit code.
|
|
4
|
+
icon: "triangle-exclamation"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Every error carries a class. The class determines the exit code; the code
|
|
@@ -56,8 +57,8 @@ try {
|
|
|
56
57
|
| `TEST_SETUP_FAILED` | Test prerequisites outside the product failed to arrange (blocked: test_setup). Also a `test.extend()` fixture that broke its contract: returned without calling `use()`, called it twice, or redefined a fixture the target's engine contributes | configuration | 2 |
|
|
57
58
|
| `AUTOMATION_UNSUPPORTED` | The step needs an interaction the available toolset cannot perform, e.g. drag-and-drop (blocked: automation) | test | 1 |
|
|
58
59
|
| `MODEL_UNAVAILABLE` | No model configured: the built-in agent's `agents.<name>.model` holds no AI SDK model instance; the message names the agent that was selected. Checked once per run when the `agent` fixture is first acquired; recorded as one run-level error that stops the run. `e2e explore` checks before anything starts, a custom executor's entry included, since explore runs the built-in agent. A custom executor without a model is not checked: its `act` and `assert` steps run, and `waitFor` or `extract` fails with this code on its first call. Evicts no [recording](/cache). | configuration | 2 |
|
|
59
|
-
| `POLICY_DENIED` | A denied destination (a navigation scheme other than `http:` or `https:`, `about:blank` aside, a malformed URL), a forbidden fill (an undeclared secret, a non-editable sink, a password into a field that is not a password field), or pixel evidence after a secret fill (`vision: 'only'` with pixels withheld). Also reading a value, text, or attribute from a secure field, the getters and the `toHaveValue`, `toHaveText`, `toContainText`, and `toHaveAttribute` matchers alike. An unknown tool or an undeclared field is refused to the model as the call's result and is not this code | configuration | 2 |
|
|
60
|
-
| `APP_UNREACHABLE` | The app URL could not be reached, or an app command exited early or exceeded `startupTimeout`. The startup error includes recent log output with `command.env` values redacted, and suggests adding a `log` if none is configured. Under the agent, also any engine fault with no more specific code: on a device, an iOS automation runner that is busy or wedged reports here with a message that names the runner, the session and device, and the recovery; see [
|
|
60
|
+
| `POLICY_DENIED` | A denied destination (a navigation scheme other than `http:` or `https:`, `about:blank` aside, a malformed URL), a forbidden fill (an undeclared secret, a non-editable sink, a password into a field that is not a password field), or pixel evidence after a secret fill (`vision: 'only'` with pixels withheld, or an `act` or `vision: true` step whose semantic capture timed out before the step did). Also reading a value, text, or attribute from a secure field, the getters and the `toHaveValue`, `toHaveText`, `toContainText`, and `toHaveAttribute` matchers alike. An unknown tool or an undeclared field is refused to the model as the call's result and is not this code | configuration | 2 |
|
|
61
|
+
| `APP_UNREACHABLE` | The app URL could not be reached, or an app command exited early or exceeded `startupTimeout`. The startup error includes recent log output with `command.env` values redacted, and suggests adding a `log` if none is configured. Under the agent, also any engine fault with no more specific code: on a device, an iOS automation runner that is busy or wedged reports here with a message that names the runner, the session and device, and the recovery; see [Mobile](/mobile#troubleshooting) | infrastructure | 3 |
|
|
61
62
|
| `APP_ALREADY_RUNNING` | `readyUrl` already answered before the target's `app.command` was spawned and `reuseExisting` was not in effect; stop that process or set `reuseExisting: true` locally | infrastructure | 3 |
|
|
62
63
|
| `MODEL_PROVIDER_FAILED` | Provider transport failure (network error, 5xx, rate limit, or a request with no response within 120 s) after the adapter's transport retries (up to 5 per model call, honoring `retry-after`, bounded by the remaining step timeout). A request that stalls is aborted at 120 s and sent again, and an `agent.act` step's `--debug` transcript notes it. The bound covers the whole request, so a model that needs longer than 120 s for one answer fails here too. Evicts no [recording](/cache) | infrastructure | 3 |
|
|
63
64
|
| `CANCELLED` | The runner cancelled the operation, without a process signal: an `e2e mcp` `open_session` whose request the client cancelled or whose connection closed, or that the server's shutdown cut short, after stopping what it had started | infrastructure | 3 |
|
|
@@ -83,8 +84,8 @@ These appear in terminal output and in the report as plain string codes.
|
|
|
83
84
|
| --- | --- | --- | ---: |
|
|
84
85
|
| `CONFIG_NOT_FOUND` | `--config <path>` names a file that does not exist, or no `e2e.config.ts` or `e2e.config.mts` exists from the working directory up to the repository root | configuration | 2 |
|
|
85
86
|
| `CONFIG_AMBIGUOUS` | Both `e2e.config.ts` and `e2e.config.mts` in one directory | configuration | 2 |
|
|
86
|
-
| `CONFIG_LOAD_FAILED` | The config module failed to import: a package that is declared but not installed, an undeclared package, a wrong subpath, a removed export, or any other throw while it evaluates. The message names the fix. A config error raised while it evaluates keeps its own code: an option a factory the config calls (`web()`, `mobile()`, `defineEngine()`) refuses, or a `secrets.get()` handle turned into a string, is `INVALID_CONFIG` | configuration | 2 |
|
|
87
|
-
| `INVALID_CONFIG` | A factory the config module calls (`web()`, `mobile()`, `defineEngine()`) refused its options, with the factory's own message; or an unknown key, out-of-range value, invalid enum, missing `targets`, a target key outside `{ name, platform, engine, app, trace, video }`, a target `app` key that is unknown or malformed, `app` on a target without an engine, a `web()` target without `app.url` or with a device field, a `mobile()` target without `app.bundleId` or `app.appPath`, with `app.url` (not supported yet), a link in `bundleId`, or an unknown permission name or state (the message names the nearest name), an unknown key in a `web()` or `mobile()` option, including `web({ connect })` and `web({ basicAuth })` (the message names the nearest one), a `trace` or `video` that is not a recording mode, the removed artifact kinds list (`artifacts: [...]` or `artifacts.kinds`) and `artifacts.trace` block (the message names the `trace` mode they meant), the removed `artifacts.video` block or `video` artifact kind (the message names the `video` option), the removed `specVersion` key, a `secrets.get()` in an engine option that names no `secrets` entry (a credential's password is not one), a `secrets.get()` reference turned into a string while the config evaluates (a template literal, `String()`) or given where a string is needed (an `app.command` `args` entry or `env` value; only an engine option that declares secrets takes one), an `app.command` `args` entry or `env` value that is not a string (an `undefined` `env` value is dropped instead), an unknown key in `app.command` (the message names the nearest one), an `engine` that is not a `defineEngine` handle, `json` reporter combined with `list`, duplicate or malformed target name, a secret or credential password shorter than 6 characters (code points), two credentials or two secrets whose names map to one `E2E_USER_*` or `E2E_SECRET_*` override variable (`api-key` and `api_key`), oversized `agents.<name>.context`, an `expect.poll` `timeout` or `interval` that is not a finite number of milliseconds, a `tests` value that is not a glob or a list of globs, a `tests` list of only `!` exclusions, the removed top-level `limits` key, an agents entry that is a bare `StepExecutor` (the message names `{ executor }`) or a model instance (the message names `{ model }`), an unknown agent key (the message names the nearest one), the removed agent keys `timeout` and `maxTurns` (the message names `judgmentTimeout` and `maxModelCalls`), `system` or `tools` beside `executor`, a `tools` entry `defineTool` did not build or that takes a built-in tool name (a grammar verb, `complete_step`, `locate`, `start_recording`, `stop_recording`, or `report_finding`), `createToolLoopExecutor({ maxTurns })`, `reuseExisting` on an app whose `app.url` asks for a free port, or a port-0 `app.url` with no `app.command` to serve it | configuration | 2 |
|
|
87
|
+
| `CONFIG_LOAD_FAILED` | The config module failed to import: a package that is declared but not installed, an undeclared package, a wrong subpath, a removed export, or any other throw while it evaluates. The message names the fix. A config error raised while it evaluates keeps its own code: an option a factory the config calls (`web()`, `mobile()`, `defineEngine()`, `decisionExecutor()`) refuses, or a `secrets.get()` handle turned into a string, is `INVALID_CONFIG` | configuration | 2 |
|
|
88
|
+
| `INVALID_CONFIG` | A factory the config module calls (`web()`, `mobile()`, `defineEngine()`, `decisionExecutor()`) refused its options, with the factory's own message; or an unknown key, out-of-range value, invalid enum, missing `targets`, a target key outside `{ name, platform, engine, app, trace, video }`, a target `app` key that is unknown or malformed, `app` on a target without an engine, a `web()` target without `app.url` or with a device field, a `mobile()` target without `app.bundleId` or `app.appPath`, with `app.url` (not supported yet), a link in `bundleId`, or an unknown permission name or state (the message names the nearest name), an unknown key in a `web()` or `mobile()` option, including `web({ connect })` and `web({ basicAuth })` (the message names the nearest one), a `trace` or `video` that is not a recording mode, the removed artifact kinds list (`artifacts: [...]` or `artifacts.kinds`) and `artifacts.trace` block (the message names the `trace` mode they meant), the removed `artifacts.video` block or `video` artifact kind (the message names the `video` option), the removed `specVersion` key, a `secrets.get()` in an engine option that names no `secrets` entry (a credential's password is not one), a `secrets.get()` reference turned into a string while the config evaluates (a template literal, `String()`) or given where a string is needed (an `app.command` `args` entry or `env` value; only an engine option that declares secrets takes one), an `app.command` `args` entry or `env` value that is not a string (an `undefined` `env` value is dropped instead), an unknown key in `app.command` (the message names the nearest one), an `engine` that is not a `defineEngine` handle, `json` reporter combined with `list`, duplicate or malformed target name, a secret or credential password shorter than 6 characters (code points), two credentials or two secrets whose names map to one `E2E_USER_*` or `E2E_SECRET_*` override variable (`api-key` and `api_key`), oversized `agents.<name>.context`, an `expect.poll` `timeout` or `interval` that is not a finite number of milliseconds, a `tests` value that is not a glob or a list of globs, a `tests` list of only `!` exclusions, the removed top-level `limits` key, an agents entry that is a bare `StepExecutor` (the message names `{ executor }`) or a model instance (the message names `{ model }`), an unknown agent key (the message names the nearest one), the removed agent keys `timeout` and `maxTurns` (the message names `judgmentTimeout` and `maxModelCalls`), `system` or `tools` beside `executor`, a `tools` entry `defineTool` did not build or that takes a built-in tool name (a grammar verb, `complete_step`, `locate`, `start_recording`, `stop_recording`, or `report_finding`), `createToolLoopExecutor({ maxTurns })`, `reuseExisting` on an app whose `app.url` asks for a free port, or a port-0 `app.url` with no `app.command` to serve it | configuration | 2 |
|
|
88
89
|
| `CONFIG_NOT_DETERMINISTIC` | A worker re-resolved the config module and got a different digest than the runner | configuration | 2 |
|
|
89
90
|
| `APP_URL_REQUIRED` | A navigation or relative URL on a target that declares no `app.url`, or an `app.command` with no `app.url` or `app.readyUrl` to poll | configuration | 2 |
|
|
90
91
|
| `INVALID_APP_URL` | Base URL with userinfo, a query, a fragment, a non-HTTP scheme, plain HTTP on a non-loopback host, port 0 on a host other than a literal loopback address (`127.0.0.1`, `[::1]`), a loopback address the run could not bind a free port on, or unparseable | configuration | 2 |
|
|
@@ -104,24 +105,26 @@ These appear in terminal output and in the report as plain string codes.
|
|
|
104
105
|
| `NO_LAST_RUN` | `--last-failed` found no `report.json` where the run writes it, or a file there that is not a `report-1` document. Run once without the flag first. | configuration | 2 |
|
|
105
106
|
| `ONLY_IN_CI` | A focused test reached a CI run | configuration | 2 |
|
|
106
107
|
| `COLLECTION_ERROR` | Registration outside collection, an invalid title, a non-function body, an async `describe` body, nested serial groups, an option forbidden inside a serial group, a setup test inside `describe`, an invalid session name, a malformed test option such as `tags` that is not a list of distinct names, a test or `describe` option key the call does not take (the message names the nearest one), a test file that fails to import (in a run with no positionals, or a file the positionals selected), a session that a selected test consumes or `e2e explore --session` names but no collected setup test produces (the message lists the declared sessions), a consumed session on a target whose engine has no `state` capability to save it, or a positional path outside the project root | configuration | 2 |
|
|
107
|
-
| `POLICY_DENIED` | A denied destination (a navigation scheme other than `http:` or `https:`, `about:blank` aside, a malformed URL), a forbidden fill (an undeclared secret, a non-editable sink, a mismatched purpose, a `Secret` reaching a JSON sink), or tainted pixels (`app.screenshot()` after a secret fill, `vision: 'only'` with pixels withheld); see the [security model](/security). An unknown tool or an undeclared field goes back to the model as the call's result, never this code | configuration | 2 |
|
|
108
|
+
| `POLICY_DENIED` | A denied destination (a navigation scheme other than `http:` or `https:`, `about:blank` aside, a malformed URL), a forbidden fill (an undeclared secret, a non-editable sink, a mismatched purpose, a `Secret` reaching a JSON sink), or tainted pixels (`app.screenshot()` after a secret fill, `vision: 'only'` with pixels withheld, an `act` or `vision: true` step whose semantic capture timed out before the step did); see the [security model](/security). An unknown tool or an undeclared field goes back to the model as the call's result, never this code | configuration | 2 |
|
|
108
109
|
| `SESSION_MISMATCH` | A stored session does not match the current run, target, engine, platform, or app identity | configuration | 2 |
|
|
109
110
|
| `SESSION_EXPIRED` | A stored session is past its expiry at restore time (configuration, exit 2), or a setup test saved state whose expiry is not after its creation (test, exit 1) | configuration or test | 2 or 1 |
|
|
110
111
|
| `SESSION_INVALID` | A stored session failed authentication. Tampered or corrupt. | configuration | 2 |
|
|
111
112
|
| `SESSION_UNAVAILABLE` | A test requires a session no selected setup test produced | configuration | 2 |
|
|
112
113
|
| `SESSION_CONTRACT` | A setup test saved a session twice, saved an undeclared name, or finished without saving a declared one | test | 1 |
|
|
113
|
-
| `TEST_TIMEOUT` | The attempt exceeded the resolved test `timeout`. The body is not stopped: the harness operation it is in is cancelled and its fixtures die with the attempt, so it cannot reach the next test | test | 1 |
|
|
114
|
+
| `TEST_TIMEOUT` | The attempt exceeded the resolved test `timeout`. The body is not stopped: the harness operation it is in is cancelled and its fixtures die with the attempt, so it cannot reach the next test. A test that blocks the event loop gets its worker killed once `cleanupTimeout` (at least 5 s) passes after the timeout; see [Hooks](/reference/test#hooks) | test | 1 |
|
|
114
115
|
| `STEP_NOT_AWAITED` | The test body settled while a step it started was still running: a step call without `await`. The step is failed at the line of the call and cancelled; the attempt fails with this code when the body returned, or keeps the body's own error and records this one under `secondaryErrors` when the body threw. An `expect.poll` matcher left running when the body, an `afterEach` hook, or a fixture teardown returned fails that phase the same way at the line of the call, and is cancelled; one left by a `beforeAll` or `afterAll` hook fails the hook with `HOOK_FAILED` | test | 1 |
|
|
115
116
|
| `UNHANDLED_REJECTION` | A promise rejected with no test running. An unhandled rejection is charged to the test running when it surfaces, since Node cannot say which test created the promise; between tests it is this run error, naming the last test that finished in the worker as the likely source. Before any test has finished in the worker it ends the worker instead: the rejection is the run error and the test on its way fails with `WORKER_CRASH`. The body that left it keeps running like one past `TEST_TIMEOUT` | test | 1 |
|
|
116
117
|
| `HOOK_FAILED` | `beforeAll` or `afterAll` threw. Skips the suite's tests. | test | 1 |
|
|
117
118
|
| `LAUNCH_TIMEOUT` | Engine init or attempt start exceeded `config.launchTimeout` | infrastructure | 3 |
|
|
118
119
|
| `CLEANUP_TIMEOUT` | Engine attempt end exceeded `config.cleanupTimeout`, or a route or dialog handler the engine ran for the test was still running when the attempt settled | infrastructure | 3 |
|
|
119
|
-
| `TRACE_WITHHELD` | A trace from
|
|
120
|
+
| `TRACE_WITHHELD` | A trace from a session that knows a secret value could not be rewritten, so it was deleted, or the engine returned an archive path outside the attempt's artifact directory, so nothing was registered. Recorded in the attempt's `secondaryErrors`; never fails the attempt. | infrastructure | 3 |
|
|
120
121
|
| `BROWSER_INSTALL_FAILED` | Browser provisioning failed | infrastructure | 3 |
|
|
121
122
|
| `WORKER_INIT_FAILED` | A worker process never became ready | infrastructure | 3 |
|
|
122
|
-
| `WORKER_EXIT` | A worker exited unexpectedly while running a file: the test in flight fails with `WORKER_CRASH` and the file's other tests are skipped. An unhandled rejection is charged to the test running when it surfaces, or recorded as `UNHANDLED_REJECTION` between tests; before any test has finished there it ends the worker, and the rejection itself is the run error, not this one | infrastructure | 3 |
|
|
123
|
+
| `WORKER_EXIT` | A worker exited unexpectedly while running a file: the test in flight fails with `WORKER_CRASH` and the file's other tests are skipped. Attempts that finished before the exit stay in the report, a retry's earlier attempts and a serial group's finished members included, and the crash is recorded as the attempt after them, or on the last attempt when it came after it (an `afterAll`). An unhandled rejection is charged to the test running when it surfaces, or recorded as `UNHANDLED_REJECTION` between tests; before any test has finished there it ends the worker, and the rejection itself is the run error, not this one | infrastructure | 3 |
|
|
123
124
|
| `WORKER_CRASH` | A worker exited during a specific test | infrastructure | 3 |
|
|
124
125
|
| `WORKER_ENTRY_MISSING` | The worker entry module could not be resolved. Packaging fault. | infrastructure | 3 |
|
|
126
|
+
| `NODE_UNSUPPORTED` | The runtime cannot run e2e's TypeScript loader: a Node.js outside `^22.22.3 \|\| >=24.8.0`, Bun (no `module.registerHooks`), or Deno. The CLI checks this before it starts and exits 2 with the same sentence; the loader checks it again where it registers its hooks, for a host that runs e2e's runner without the CLI | infrastructure | 3 |
|
|
127
|
+
| `TYPESCRIPT_COMPILER_UNAVAILABLE` | oxc, which compiles e2e's TypeScript, could not be loaded; the message gives the cause. Most often its native bindings for this platform (`@oxc-transform/binding-<platform>-<arch>`, `@oxc-parser/binding-<platform>-<arch>`) are not installed. npm leaves it out with `--omit=optional`, or when `package-lock.json` was written on another platform (npm/cli#4828): install again without `--omit=optional`, or delete `package-lock.json` and `node_modules` and run `npm install` | infrastructure | 3 |
|
|
125
128
|
| `ENGINE_FAILURE` | Any engine fault with no more specific mapping | infrastructure | 3 |
|
|
126
129
|
| `WORKER_PROTOCOL` | A worker reported a unit other than the one it was running, or started an attempt while another was still running | infrastructure | 3 |
|
|
127
130
|
| `SESSION_KEY_INVALID` | A worker received a session key that is not 32 bytes. Packaging or IPC fault. | infrastructure | 3 |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: expect
|
|
3
3
|
description: "Locator assertions, fixture assertions, and plain value matchers."
|
|
4
|
+
icon: "circle-check"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Use `expect(locator)` to wait for an app condition and `expect(value)` to
|
|
@@ -370,7 +371,7 @@ The unit-runner matchers (`toThrow`, `toBeInstanceOf`, `toBeNaN`,
|
|
|
370
371
|
`toStrictEqual`, `resolves`, `rejects`, `expect.extend`) are not part of
|
|
371
372
|
`expect`. An end-to-end test checks what the app shows and what its API
|
|
372
373
|
returns; the value matchers cover that, see
|
|
373
|
-
[
|
|
374
|
+
[API](/api-testing). Code-level
|
|
374
375
|
checks belong in the unit runner.
|
|
375
376
|
|
|
376
377
|
Pass a second argument to describe the check in its failure message:
|
package/docs/reference/mcp.mdx
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: MCP server (e2e mcp)
|
|
3
3
|
description: Let a coding agent inspect and interact with your app over MCP.
|
|
4
|
+
icon: "plug"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
<CodeGroup>
|
|
7
8
|
```bash npm
|
|
8
|
-
npx e2e mcp [--config <path>] [--target <name>] [--
|
|
9
|
+
npx e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
|
|
9
10
|
```
|
|
10
11
|
|
|
11
12
|
```bash pnpm
|
|
12
|
-
pnpm exec e2e mcp [--config <path>] [--target <name>] [--
|
|
13
|
+
pnpm exec e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
```bash bun
|
|
16
|
-
bunx e2e mcp [--config <path>] [--target <name>] [--
|
|
17
|
+
bunx e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
|
|
17
18
|
```
|
|
18
19
|
</CodeGroup>
|
|
19
20
|
|
|
@@ -64,21 +65,32 @@ Start the server in the project directory. Config discovery works like
|
|
|
64
65
|
| --- | --- |
|
|
65
66
|
| `--config <path>` | The config every session loads unless `open_session` names one. Configs are reloaded on every `open_session`, with every file they import by path. |
|
|
66
67
|
| `--target <name>` | The target every session opens on. Otherwise `open_session` names one, or the only target is used; several targets and no name is `TARGET_REQUIRED`. |
|
|
67
|
-
| `--
|
|
68
|
+
| `--headed` | Show the browser or simulator, when the engine supports it, in every session whose `open_session` does not set `headed`. Sessions are headless by default, as in `e2e run`. |
|
|
68
69
|
| `--max-sessions <n>` | How many sessions may be open at once, 1 through 16; default 4. Each session is its own browser or device. |
|
|
69
70
|
|
|
70
71
|
Operator output goes to stderr, prefixed `e2e mcp:`. Stdout carries only the
|
|
71
|
-
protocol
|
|
72
|
+
protocol. What your code writes through `console`, `process.stdout`, or
|
|
73
|
+
`process.stderr` (the config's top-level code, project tools) goes to stderr
|
|
74
|
+
with secret values redacted. Output printed while a config loads appears once
|
|
75
|
+
it has loaded; if the load fails, that output is withheld, since the config's
|
|
76
|
+
secrets are unknown. An error nobody caught is logged redacted and shuts the
|
|
77
|
+
server down. A child process that inherits the server's stdio writes past
|
|
78
|
+
this.
|
|
72
79
|
|
|
73
80
|
## Tools
|
|
74
81
|
|
|
75
82
|
| Tool | Arguments | Result |
|
|
76
83
|
| --- | --- | --- |
|
|
77
|
-
| `open_session` | `target` (optional), `config` (optional path) | Loads the config, starts the target's app command, boots the engine, starts one attempt, opens the app URL when the engine can navigate. Returns the session id, the catalog, and the first observation. |
|
|
84
|
+
| `open_session` | `target` (optional), `config` (optional path), `headed` (optional boolean) | Loads the config, starts the target's app command, boots the engine, starts one attempt, opens the app URL when the engine can navigate. Returns the session id, the catalog, and the first observation. `headed: true` shows this session's browser or simulator, `false` hides it; without it the session follows `--headed`. |
|
|
78
85
|
| `tools` | `tool` (optional), `session` (required while several are open) | The catalog, one line per tool. With `tool`, its full description and argument schema. |
|
|
79
86
|
| `call` | `tool`, `args` (optional), `session` (required while several are open) | Runs one catalog tool. Arguments are validated against the tool's closed schema first: a wrong type or an argument the tool does not declare is `INVALID_ARGUMENT`, and nothing runs. An action that fails is an error result that still carries the screen it re-observed, and leads with the tool, its target, and the code: `navigate file:///etc/passwd failed: POLICY_DENIED: forbidden URL scheme: file:`. |
|
|
80
87
|
| `close_session` | `session` (required while several are open) | Saves a recording still running, ends the attempt, disposes the engine, stops the app processes the session started once no other session uses them. |
|
|
81
88
|
|
|
89
|
+
Project tools honor their AI SDK `toModelOutput` renderer, including when
|
|
90
|
+
`execute` returns a string. Rendered `error-text` and `error-json` outputs
|
|
91
|
+
are MCP error results (`isError: true`) so the coding agent can distinguish
|
|
92
|
+
a failed tool call from a successful one.
|
|
93
|
+
|
|
82
94
|
## Several sessions
|
|
83
95
|
|
|
84
96
|
Several sessions can be open at once, up to `--max-sessions`, each on its
|
|
@@ -183,8 +195,9 @@ Sessions enforce what tests enforce. Node ids are valid only against the
|
|
|
183
195
|
newest observation. A secret fills only through `type_secret`; once one has
|
|
184
196
|
been filled, `screenshot` and the point tools (`tap_at`, `hover_at`,
|
|
185
197
|
`type_at`, `press_at`, `select_at`) stay in the catalog but answer
|
|
186
|
-
`PIXEL_TAINTED` for the rest of the session. Every text a
|
|
187
|
-
an action's result, or
|
|
198
|
+
`PIXEL_TAINTED` for the rest of the session. Every text a tool returns, a node `locate` found,
|
|
199
|
+
an action's result, an `open_session` or `close_session` failure, or a cleanup line, has the
|
|
200
|
+
config's secret values redacted once the config has loaded, as do the server's own log lines. Nothing a
|
|
188
201
|
session does is recorded as a test result or into the replay cache; its
|
|
189
202
|
artifacts land under `<output>/artifacts/<target>/sessions/`, its videos under
|
|
190
203
|
`<output>/videos/`. A later `e2e run` clears `<output>/artifacts`; the
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: screen and Locator
|
|
3
3
|
description: Queries, refinement, actions, and reads.
|
|
4
|
+
icon: "display"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
A `Locator` stores a query and resolves it when an action, read, or assertion
|
|
@@ -178,14 +179,15 @@ not the finger: `'down'` reveals what is below. `momentum` is its fling
|
|
|
178
179
|
strength, `'none'` by default: `'none'` a plain drag of half the surface,
|
|
179
180
|
`'slow'` a short fling, `'fast'` a long one. How far each carries is the
|
|
180
181
|
engine's.
|
|
181
|
-
`{ from, to }` is a swipe along a path between two viewport points:
|
|
182
|
-
swipe on a device, a pointer drag on a document platform, which is
|
|
183
|
-
reveals a row action, dismisses a sheet, or drags a card between
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
`
|
|
182
|
+
`{ from, to, duration? }` is a swipe along a path between two viewport points:
|
|
183
|
+
a touch swipe on a device, a pointer drag on a document platform, which is
|
|
184
|
+
what reveals a row action, dismisses a sheet, or drags a card between
|
|
185
|
+
columns. `duration` sets how long the gesture takes in milliseconds; omitted
|
|
186
|
+
leaves the duration to the engine's default. Uses `config.actionTimeout`.
|
|
187
|
+
Throws `ACTION_FAILED` when the gesture is rejected, `INVALID_ARGUMENT` for
|
|
188
|
+
a mix of the two shapes, an out-of-range duration, or a coordinate off the
|
|
189
|
+
plane, and `UNSUPPORTED_CAPABILITY` when the engine does not list `swipe` in
|
|
190
|
+
its actions (a direction) or `swipeTo` in its pointer actions (a path).
|
|
189
191
|
|
|
190
192
|
```ts
|
|
191
193
|
await screen.swipe({ direction: 'up', momentum: 'fast' });
|
|
@@ -194,6 +196,7 @@ const row = await screen.getByText('Design review').boundingBox();
|
|
|
194
196
|
await screen.swipe({
|
|
195
197
|
from: { x: row.x + row.width - 10, y: row.y + row.height / 2 },
|
|
196
198
|
to: { x: row.x + 10, y: row.y + row.height / 2 },
|
|
199
|
+
duration: 300,
|
|
197
200
|
});
|
|
198
201
|
```
|
|
199
202
|
|
|
@@ -585,6 +588,7 @@ export interface SwipeOptions {
|
|
|
585
588
|
momentum?: Momentum;
|
|
586
589
|
from?: never;
|
|
587
590
|
to?: never;
|
|
591
|
+
duration?: never;
|
|
588
592
|
}
|
|
589
593
|
|
|
590
594
|
/** A swipe along a path between two viewport points: a touch swipe on a device, a pointer drag on a document platform. */
|
|
@@ -593,6 +597,8 @@ export interface SwipePathOptions {
|
|
|
593
597
|
from: Point;
|
|
594
598
|
/** Where it lifts. */
|
|
595
599
|
to: Point;
|
|
600
|
+
/** Gesture duration in milliseconds; omitted = the engine's default. */
|
|
601
|
+
duration?: number;
|
|
596
602
|
direction?: never;
|
|
597
603
|
momentum?: never;
|
|
598
604
|
}
|
|
@@ -663,7 +669,11 @@ and `<details>` are `group`, `<input type="number">` is `spinbutton`,
|
|
|
663
669
|
`<select size="3">` is a `listbox` like a `multiple` one, an `<input list>`
|
|
664
670
|
naming a `<datalist>` is a `combobox`, a `<td>` of a `role="grid"` table is a
|
|
665
671
|
`gridcell`,
|
|
666
|
-
`<thead>` and `<tbody>` are `rowgroup`, `<th scope="row">` is `rowheader
|
|
672
|
+
`<thead>` and `<tbody>` are `rowgroup`, `<th scope="row">` is `rowheader`.
|
|
673
|
+
A scope-less `<th>` beside a data cell with text or children is also a
|
|
674
|
+
`rowheader`; header-only rows keep `columnheader`. A lone scope-less header in a one-row
|
|
675
|
+
table has no header role, while `scope="col"` and `scope="colgroup"` explicitly
|
|
676
|
+
keep `columnheader`.
|
|
667
677
|
`<figure>` is `figure`, `<article>` is `article`, `<header>` and `<footer>` are
|
|
668
678
|
`banner` and `contentinfo` only outside sectioning content or an element with
|
|
669
679
|
a sectioning role (`article`, `complementary`, `main`, `navigation`, `region`),
|
package/docs/reference/test.mdx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: test
|
|
3
3
|
description: Tests, setup tests, groups, hooks, options, and fixtures.
|
|
4
|
+
icon: "flask-vial"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Declare tests, groups, and hooks synchronously while the test file is
|
|
@@ -192,6 +193,19 @@ for every scope whose `beforeAll` started.
|
|
|
192
193
|
its own `cleanupTimeout` budget with working fixtures, so teardown can still
|
|
193
194
|
drive the app after the body timed out.
|
|
194
195
|
|
|
196
|
+
A test that blocks its worker's event loop (`while (true) {}` in a fixture,
|
|
197
|
+
`beforeEach`, the body, or `afterEach`) stops every timer in that worker, so
|
|
198
|
+
the worker cannot time it out. A `beforeAll` or `afterAll` that blocks still
|
|
199
|
+
hangs the run. When the worker has not responded `cleanupTimeout` (at least 5 s) after
|
|
200
|
+
the test timeout, the runner kills it. That attempt is reported `timed-out`
|
|
201
|
+
with `TEST_TIMEOUT` and no artifacts, after the attempts before it, and is
|
|
202
|
+
not retried. The rest of its file runs on a new worker. In a serial group,
|
|
203
|
+
the members after it are skipped. A worker with a debugger attached when the
|
|
204
|
+
test starts (an editor's auto-attach, or `NODE_OPTIONS=--inspect=0`) is
|
|
205
|
+
never killed this way, so a breakpoint can stay paused. A programmatic run with an inline
|
|
206
|
+
config or in-memory tests runs in the runner's own process and has no worker
|
|
207
|
+
to kill: a blocked event loop hangs it.
|
|
208
|
+
|
|
195
209
|
A throw inside `beforeAll` or `afterAll` is `HOOK_FAILED` and skips the
|
|
196
210
|
suite's tests with cause `hook-failed`. Inside a serial group it skips every
|
|
197
211
|
remaining member and fails the group without a retry.
|
|
@@ -302,7 +316,13 @@ group's attempt. A retry starts fresh. Inside it, `retries`, `trace`,
|
|
|
302
316
|
`video`, `session`, `platforms`, `requires`, `skip`, `only`, and a nested
|
|
303
317
|
`serial` on a member are collection errors. Set them on the group. The group
|
|
304
318
|
records one trace and one video per group attempt, from the `trace` and
|
|
305
|
-
`video` on the serial `describe` or a group around it.
|
|
319
|
+
`video` on the serial `describe` or a group around it. A group attempt that
|
|
320
|
+
cannot start, because its file fails to load or its session fails to launch,
|
|
321
|
+
fails the first member with that error and skips the rest with cause
|
|
322
|
+
`serial-predecessor-failed`. When the run is interrupted during the launch,
|
|
323
|
+
every member skips instead. On a retry, the members keep the verdicts of
|
|
324
|
+
the attempt before it. The exit code follows the error's category, as for any
|
|
325
|
+
other test.
|
|
306
326
|
|
|
307
327
|
## Fixtures
|
|
308
328
|
|
package/docs/reference/web.mdx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Browser
|
|
3
3
|
description: The browser engine, its options, and the browser fixture it contributes.
|
|
4
|
+
icon: "globe"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
`@e2e-dev/web` drives Chromium, Firefox, and WebKit through Playwright.
|
|
@@ -162,7 +163,7 @@ That protection is text only: unlike a fill, it withholds no screenshot or
|
|
|
162
163
|
model pixels, so a page that renders the password on screen is not masked in
|
|
163
164
|
them ([Security](/security#secrets)).
|
|
164
165
|
|
|
165
|
-
`headers` and `basicAuth` are how a test reaches a [protected preview](/
|
|
166
|
+
`headers` and `basicAuth` are how a test reaches a [protected preview](/web#protected-previews).
|
|
166
167
|
|
|
167
168
|
`screencast` shapes the recording, not which attempts record: that is the
|
|
168
169
|
config's [`video`](/reference/config#video) mode. `size` takes whole pixels,
|
|
@@ -291,7 +292,7 @@ export interface Browser extends Expectable<BrowserExpectation> {
|
|
|
291
292
|
locator(selector: string): Locator;
|
|
292
293
|
/** Creates a screen query scope inside one iframe. */
|
|
293
294
|
frameLocator(selector: string): FrameScreen;
|
|
294
|
-
/** Evaluates trusted test code in the page. */
|
|
295
|
+
/** Evaluates trusted test code in the page: a function, or a string expression whose value is returned, called first if it is a function. */
|
|
295
296
|
evaluate<T extends JsonValue>(fn: string | (() => T | Promise<T>)): Promise<T>;
|
|
296
297
|
/** Evaluates trusted test code with one required JSON-safe argument. */
|
|
297
298
|
evaluate<T extends JsonValue, Arg extends JsonValue>(
|
|
@@ -428,6 +429,10 @@ close over anything outside its own body; helpers declared inside the body
|
|
|
428
429
|
work. Pass outside values through `arg`. Both `arg` and the return value
|
|
429
430
|
must be `JsonValue`, otherwise the call fails with `INVALID_ARGUMENT`.
|
|
430
431
|
|
|
432
|
+
A string is evaluated as a single expression, not as statements. If it
|
|
433
|
+
evaluates to a function, that function is called with `arg`; otherwise its
|
|
434
|
+
value is the result. A promise, from either, is awaited.
|
|
435
|
+
|
|
431
436
|
An exception in the evaluated code is `EVALUATE_FAILED` with its message. A
|
|
432
437
|
Playwright timeout is `ACTION_FAILED`; a closed page or lost context is
|
|
433
438
|
`ENGINE_FAILURE`.
|
|
@@ -438,6 +443,7 @@ const flag = await browser.evaluate(
|
|
|
438
443
|
(name: string) => window.localStorage.getItem(name),
|
|
439
444
|
'featureFlags',
|
|
440
445
|
);
|
|
446
|
+
const title = await browser.evaluate<string>('document.title');
|
|
441
447
|
```
|
|
442
448
|
|
|
443
449
|
### Init scripts
|