e2e 0.16.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/act.js +7 -4
- package/dist/agent/action-dispatcher.js +4 -5
- package/dist/agent/actions.d.ts +32 -23
- package/dist/agent/actions.js +38 -24
- 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/sdk.js +2 -0
- package/dist/agent/model/tool-choice.d.ts +9 -0
- package/dist/agent/model/tool-choice.js +14 -0
- package/dist/agent/observation-feed.d.ts +5 -6
- package/dist/agent/observation-feed.js +8 -11
- package/dist/agent/observation.d.ts +40 -11
- package/dist/agent/observation.js +109 -59
- package/dist/agent/phases.d.ts +7 -1
- package/dist/agent/phases.js +12 -3
- package/dist/agent/point-tap.d.ts +8 -8
- package/dist/agent/point-tap.js +1 -1
- package/dist/agent/prompts.js +3 -0
- package/dist/agent/replay.d.ts +19 -8
- package/dist/agent/replay.js +29 -29
- package/dist/agent/scroll-target.d.ts +2 -3
- package/dist/agent/step-cache.d.ts +43 -15
- package/dist/agent/step-cache.js +156 -67
- package/dist/agent/tool-loop.js +94 -5
- package/dist/cache/anchors.d.ts +72 -23
- package/dist/cache/anchors.js +233 -60
- package/dist/cache/context.d.ts +25 -14
- package/dist/cache/context.js +50 -8
- package/dist/cache/decide.d.ts +2 -2
- package/dist/cache/decide.js +4 -6
- package/dist/cache/identity.d.ts +18 -4
- package/dist/cache/identity.js +9 -4
- package/dist/cache/recorder.d.ts +15 -1
- package/dist/cache/recorder.js +36 -23
- package/dist/cache/rekeyed.d.ts +46 -0
- package/dist/cache/rekeyed.js +89 -0
- package/dist/cache/relocate.d.ts +14 -14
- package/dist/cache/relocate.js +33 -25
- package/dist/cache/route.d.ts +32 -33
- package/dist/cache/route.js +94 -50
- package/dist/cache/store.d.ts +6 -0
- package/dist/cache/store.js +21 -1
- package/dist/cache/trace.d.ts +53 -14
- package/dist/cache/trace.js +76 -30
- package/dist/cli/bin.js +2 -2
- package/dist/cli/index.js +43 -32
- package/dist/cli/init/engines.d.ts +1 -3
- package/dist/cli/init/engines.js +2 -2
- package/dist/cli/init/gateways.d.ts +1 -1
- package/dist/cli/init/gateways.js +11 -1
- 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/init/versions.d.ts +0 -8
- package/dist/cli/init/versions.js +1 -10
- 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/resolve.d.ts +1 -0
- package/dist/config/resolve.js +33 -1
- 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 +4 -2
- package/dist/engine/session.js +2 -2
- package/dist/expect/async.js +4 -2
- package/dist/expect/poll-scope.d.ts +52 -0
- package/dist/expect/poll-scope.js +113 -0
- package/dist/expect/poll.js +33 -6
- package/dist/expect/values.js +11 -4
- package/dist/internal/abandoned.d.ts +11 -0
- package/dist/internal/abandoned.js +23 -0
- package/dist/internal/brands.d.ts +2 -0
- package/dist/internal/brands.js +2 -0
- package/dist/internal/client-identity.d.ts +9 -0
- package/dist/internal/client-identity.js +14 -0
- 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 +9 -4
- package/dist/internal/redact.js +120 -23
- package/dist/internal/time.d.ts +26 -4
- package/dist/internal/time.js +58 -14
- package/dist/internal/urls.d.ts +9 -5
- package/dist/internal/urls.js +19 -10
- package/dist/locator/engine.d.ts +12 -2
- package/dist/locator/engine.js +55 -14
- package/dist/locator/screen.js +59 -9
- package/dist/mcp/catalog.d.ts +3 -2
- package/dist/mcp/catalog.js +6 -6
- 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 +56 -15
- package/dist/mcp/tools.js +7 -5
- package/dist/oauth/chatgpt.js +3 -23
- package/dist/oauth/cli.js +1 -0
- package/dist/oauth/copilot.d.ts +5 -4
- package/dist/oauth/copilot.js +80 -7
- package/dist/oauth/grok.js +2 -1
- package/dist/oauth/models.js +2 -1
- package/dist/oauth/opencode-console.d.ts +10 -0
- package/dist/oauth/opencode-console.js +133 -0
- package/dist/oauth/providers/github-copilot.d.ts +12 -0
- package/dist/oauth/providers/github-copilot.js +77 -23
- package/dist/oauth/providers/opencode-console.d.ts +31 -0
- package/dist/oauth/providers/opencode-console.js +148 -0
- package/dist/oauth/providers.d.ts +2 -6
- package/dist/oauth/providers.js +3 -7
- package/dist/oauth/responses.d.ts +9 -0
- package/dist/oauth/responses.js +23 -0
- package/dist/report/build.d.ts +26 -0
- package/dist/report/build.js +16 -8
- package/dist/report/failure-text.d.ts +6 -1
- package/dist/report/failure-text.js +14 -4
- package/dist/report/format.d.ts +10 -5
- package/dist/report/format.js +12 -5
- package/dist/report/junit.js +11 -12
- package/dist/report/list.d.ts +12 -0
- package/dist/report/list.js +61 -24
- package/dist/report/markdown.d.ts +1 -1
- package/dist/report/markdown.js +37 -14
- package/dist/report/repeats.d.ts +6 -1
- package/dist/report/repeats.js +12 -3
- package/dist/run/artifacts.d.ts +22 -4
- package/dist/run/artifacts.js +71 -3
- package/dist/run/execute.d.ts +34 -5
- package/dist/run/execute.js +93 -44
- package/dist/run/extended-fixtures.js +6 -2
- package/dist/run/failure-evidence.js +10 -4
- package/dist/run/fixtures.js +6 -8
- package/dist/run/in-process.d.ts +2 -0
- package/dist/run/in-process.js +52 -10
- package/dist/run/last-run.d.ts +42 -8
- package/dist/run/last-run.js +121 -14
- 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/realm.d.ts +4 -1
- package/dist/run/realm.js +20 -6
- package/dist/run/records.d.ts +20 -4
- package/dist/run/records.js +26 -0
- package/dist/run/retry.d.ts +10 -1
- package/dist/run/retry.js +30 -12
- package/dist/run/runner.d.ts +9 -1
- package/dist/run/runner.js +101 -39
- 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 +75 -2
- package/dist/run/serial.js +243 -59
- package/dist/run/standalone.js +1 -1
- package/dist/run/steps.d.ts +0 -6
- package/dist/run/steps.js +3 -20
- package/dist/run/trace-redaction.d.ts +5 -6
- package/dist/run/trace-redaction.js +5 -6
- package/dist/run/worker/entry.js +40 -18
- package/dist/run/worker/handle.js +10 -5
- package/dist/run/worker/protocol.d.ts +85 -3
- package/dist/run/worker/session.d.ts +2 -0
- package/dist/run/worker/session.js +29 -0
- package/dist/telemetry/environment.d.ts +2 -1
- package/dist/telemetry/environment.js +12 -2
- package/dist/telemetry/events.js +1 -0
- 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 +15 -6
- package/docs/agent-steps.mdx +1 -0
- package/docs/agents.mdx +7 -3
- package/docs/api-testing.mdx +2 -1
- package/docs/authentication.mdx +6 -3
- package/docs/browser.mdx +49 -12
- package/docs/bug-bash.mdx +2 -1
- package/docs/cache.mdx +66 -19
- package/docs/ci.mdx +14 -9
- package/docs/coding-agents.mdx +2 -1
- package/docs/core-concepts.mdx +208 -0
- package/docs/debugging.mdx +25 -12
- package/docs/decision-models.mdx +142 -0
- package/docs/example-projects.mdx +32 -0
- package/docs/executors.mdx +7 -2
- package/docs/explore.mdx +1 -0
- package/docs/github.mdx +14 -5
- 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 +3 -2
- 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 +44 -18
- package/docs/migrate/selenium.mdx +27 -3
- package/docs/mobile-ci.mdx +6 -5
- package/docs/mobile.mdx +96 -8
- package/docs/models.mdx +60 -9
- package/docs/quickstart.mdx +28 -16
- package/docs/reference/agent.mdx +13 -3
- package/docs/reference/app.mdx +2 -1
- package/docs/reference/cli.mdx +41 -18
- package/docs/reference/config.mdx +67 -13
- package/docs/reference/engine.mdx +25 -13
- package/docs/reference/environment.mdx +10 -2
- package/docs/reference/errors.mdx +13 -10
- package/docs/reference/expect.mdx +36 -13
- package/docs/reference/mcp.mdx +21 -8
- package/docs/reference/mobile.mdx +24 -13
- package/docs/reference/reporters.mdx +9 -2
- package/docs/reference/screen.mdx +49 -16
- package/docs/reference/test.mdx +32 -5
- package/docs/reference/web.mdx +133 -23
- package/docs/security.mdx +58 -42
- package/docs/subscriptions.mdx +92 -12
- package/docs/telemetry.mdx +9 -6
- 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 +25 -8
- package/schema/report-v1.schema.json +14 -1
- package/skills/e2e/SKILL.md +5 -3
- package/skills/e2e/references/agent.md +33 -11
- package/skills/e2e/references/debugging.md +4 -4
- package/skills/e2e/references/mcp.md +8 -3
- package/skills/e2e/references/running.md +15 -9
- package/skills/e2e/references/setup.md +45 -16
- package/skills/e2e/references/writing-tests.md +35 -18
- package/docs/viewports.mdx +0 -146
- package/docs/writing-tests.mdx +0 -256
package/docs/telemetry.mdx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Telemetry
|
|
3
3
|
description: What the CLI sends, what it never sends, and how to turn it off.
|
|
4
|
+
icon: "chart-line"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
The `e2e` CLI sends anonymous usage data so we can see which commands,
|
|
@@ -72,7 +73,7 @@ Who ran it:
|
|
|
72
73
|
| Property | Value |
|
|
73
74
|
| --- | --- |
|
|
74
75
|
| `distinct_id` | A random id stored in the preferences file. CI uses `ci:<vendor>` instead, and a platform that runs e2e for its users uses `fleet:<name>` when it sets `E2E_TELEMETRY_FLEET` |
|
|
75
|
-
| `project_id` | A hash derived from the repository's root commit, shared across full clones.
|
|
76
|
+
| `project_id` | A hash derived from the repository's root commit, shared across full clones. `null` outside git and in a shallow clone |
|
|
76
77
|
| `session_id` | A random id for this one invocation |
|
|
77
78
|
| `first_run`, `days_since_first_run` | Whether this command created the preferences file, and whole days since it was created. `null` in CI and in a fleet, where no machine persists, and for a preferences file from before the time was recorded |
|
|
78
79
|
|
|
@@ -119,7 +120,7 @@ Versions:
|
|
|
119
120
|
| `command` | `run` or `explore` |
|
|
120
121
|
| `status`, `exit_code`, `duration_ms` | The run's outcome |
|
|
121
122
|
| `flags` | The names of the flags given to the run command, never their values |
|
|
122
|
-
| `tests_discovered`, `tests_selected`, `tests_executed`, `tests_passed`, `tests_failed`, `tests_flaky`, `tests_skipped` | The summary counts |
|
|
123
|
+
| `tests_discovered`, `tests_selected`, `tests_executed`, `tests_passed`, `tests_failed`, `tests_interrupted`, `tests_flaky`, `tests_skipped` | The summary counts |
|
|
123
124
|
| `attempts_total`, `tests_retried` | How many attempts the run made across tests and serial groups, and how many of them needed more than one |
|
|
124
125
|
| `targets`, `platforms`, `engines` | How many targets, on which platforms, and with which engines and versions, as your config declares them: `web`, `ios`, `web@0.6.1`, or your own engine's name and version. A name that is not a plain token of letters, digits, dots, dashes, and underscores is reported as `other` |
|
|
125
126
|
| `steps_total`, `steps_agent`, `steps_locator`, `steps_assertion`, `steps_screen`, `steps_app`, `steps_session`, `steps_resource` | Step counts by kind |
|
|
@@ -181,16 +182,18 @@ listed above.
|
|
|
181
182
|
## In CI
|
|
182
183
|
|
|
183
184
|
CI runs are attributed to the CI vendor rather than to a machine, no
|
|
184
|
-
preferences file is written on the runner, and no notice is printed.
|
|
185
|
-
|
|
185
|
+
preferences file is written on the runner, and no notice is printed. A coding
|
|
186
|
+
agent that sets `CI` in its shell, with no CI vendor around it, counts as the
|
|
187
|
+
machine it runs on. Set `E2E_TELEMETRY_DISABLED: '1'` in the job's `env` to
|
|
188
|
+
send nothing from CI.
|
|
186
189
|
|
|
187
190
|
## In a fleet
|
|
188
191
|
|
|
189
192
|
A platform that runs e2e on its users' behalf starts each run on a fresh
|
|
190
193
|
machine, and every fresh machine would otherwise count as a new user. Set
|
|
191
194
|
`E2E_TELEMETRY_FLEET=<name>` in those machines and they are attributed to
|
|
192
|
-
`fleet:<name>` instead, like CI: no preferences file
|
|
193
|
-
|
|
195
|
+
`fleet:<name>` instead, like CI: no preferences file and no notice. Or set
|
|
196
|
+
`E2E_TELEMETRY_DISABLED=1` and send nothing.
|
|
194
197
|
|
|
195
198
|
## Where it goes
|
|
196
199
|
|
package/docs/tools.mdx
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description: Point the
|
|
2
|
+
title: Web
|
|
3
|
+
description: Point the web engine at your app or let the runner start it, and run a suite at several viewport sizes.
|
|
4
|
+
icon: "globe"
|
|
4
5
|
---
|
|
5
6
|
|
|
7
|
+
`@e2e-dev/web` runs tests in a browser against your app's URL. This page
|
|
8
|
+
covers getting the app running for a run and testing it at different
|
|
9
|
+
sizes. For routes, cookies, dialogs, downloads, and the rest of the
|
|
10
|
+
`browser` fixture, see [Working with the browser](/browser).
|
|
11
|
+
|
|
12
|
+
## Starting your app
|
|
13
|
+
|
|
6
14
|
Set your app's URL in the target's `app`. If you also provide a start
|
|
7
15
|
command, the runner starts the app, waits until it is ready, and stops it
|
|
8
16
|
when the run ends.
|
|
9
17
|
|
|
10
|
-
|
|
18
|
+
### Point at a running app
|
|
11
19
|
|
|
12
20
|
Import `web` from `@e2e-dev/web` and put this in the exported
|
|
13
21
|
config object:
|
|
@@ -25,7 +33,7 @@ export default {
|
|
|
25
33
|
you can change the address for a run without editing the file. URLs without
|
|
26
34
|
a scheme use HTTPS, or HTTP for loopback hosts.
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
### Let the runner start it
|
|
29
37
|
|
|
30
38
|
Add `app.command` and the runner spawns the dev server, waits until `app.url`
|
|
31
39
|
answers, and stops it when the run ends, fails, or is interrupted:
|
|
@@ -79,7 +87,7 @@ temp-directory variables, and `env`, so pass what the app needs explicitly.
|
|
|
79
87
|
See the
|
|
80
88
|
[command reference](/reference/config#command) for all options.
|
|
81
89
|
|
|
82
|
-
|
|
90
|
+
#### Let the runner pick the port
|
|
83
91
|
|
|
84
92
|
Use port `0` to run multiple checkouts without a port conflict. The runner
|
|
85
93
|
chooses a free port and replaces `{port}` in the command's `args` and `env`.
|
|
@@ -98,7 +106,7 @@ valid when the port changes. Use `127.0.0.1` or `[::1]`, matching the address
|
|
|
98
106
|
the command binds; `localhost:0` is rejected. See
|
|
99
107
|
[free ports](/reference/config#free-ports) for details.
|
|
100
108
|
|
|
101
|
-
|
|
109
|
+
#### Next.js dev targets
|
|
102
110
|
|
|
103
111
|
Next.js 16 blocks cross-origin requests to its dev resources. A target that
|
|
104
112
|
opens `127.0.0.1` or `[::1]` while `next dev` identifies as `localhost`
|
|
@@ -122,7 +130,7 @@ Use `'[::1]'` in the list when `app.url` opens `http://[::1]:0`.
|
|
|
122
130
|
A target on `localhost` needs no entry, because `next dev` identifies as
|
|
123
131
|
`localhost`. A production `next start` target is unaffected either way.
|
|
124
132
|
|
|
125
|
-
|
|
133
|
+
### Dependencies
|
|
126
134
|
|
|
127
135
|
<Note>
|
|
128
136
|
This version starts only `app.command` (one process for every target that
|
|
@@ -132,7 +140,7 @@ have `app.command` start a script that brings them up and serves the app. A
|
|
|
132
140
|
services API returns in a later release.
|
|
133
141
|
</Note>
|
|
134
142
|
|
|
135
|
-
|
|
143
|
+
### Several targets
|
|
136
144
|
|
|
137
145
|
Two browsers on one app are two targets that each name it:
|
|
138
146
|
|
|
@@ -155,9 +163,9 @@ test runs on every target unless it is scoped with `platforms` or
|
|
|
155
163
|
`requires`; `--target <name>` selects at run time. A device target sits
|
|
156
164
|
beside a browser target in the same list; see the
|
|
157
165
|
[Mobile engine reference](/reference/mobile). One target per screen size is
|
|
158
|
-
how a suite runs at several [viewports](
|
|
166
|
+
how a suite runs at several [viewports](#viewports).
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
### Protected previews
|
|
161
169
|
|
|
162
170
|
Use `headers` to access protected previews or skip a tunnel's interstitial
|
|
163
171
|
page. For example, a Vercel preview can use a protection bypass header:
|
|
@@ -221,11 +229,149 @@ workers. Playwright traces include request headers, so a protected-preview
|
|
|
221
229
|
trace can contain your bypass secret. Restrict access to those traces.
|
|
222
230
|
</Note>
|
|
223
231
|
|
|
232
|
+
## Viewports
|
|
233
|
+
|
|
234
|
+
Set a viewport on the target to run a suite at that size. Use
|
|
235
|
+
`browser.setViewport()` to resize during a test. The agent uses the current size
|
|
236
|
+
and cannot resize it.
|
|
237
|
+
|
|
238
|
+
### One size per target
|
|
239
|
+
|
|
240
|
+
`viewport` sets the initial size of each attempt, in pixels. The default is
|
|
241
|
+
1280 by 720. Add one target for each size you want to test:
|
|
242
|
+
|
|
243
|
+
```ts title="e2e.config.ts"
|
|
244
|
+
import type { E2EConfig } from 'e2e';
|
|
245
|
+
import { web } from '@e2e-dev/web';
|
|
246
|
+
|
|
247
|
+
const app = { url: 'http://localhost:3000' };
|
|
248
|
+
|
|
249
|
+
export default {
|
|
250
|
+
targets: [
|
|
251
|
+
{ name: 'desktop', engine: web(), app },
|
|
252
|
+
{ name: 'tablet', engine: web({ viewport: { width: 820, height: 1180 } }), app },
|
|
253
|
+
{ name: 'phone', engine: web({ browser: 'webkit', viewport: { width: 390, height: 844 } }), app },
|
|
254
|
+
],
|
|
255
|
+
} satisfies E2EConfig;
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Each test runs once per target, with a separate result labeled `desktop`,
|
|
259
|
+
`tablet`, or `phone`. Run one size with `npx e2e run --target phone`, or list
|
|
260
|
+
the selected test-target pairs with `npx e2e list`.
|
|
261
|
+
|
|
262
|
+
To have the runner start one dev server for every size, give every target
|
|
263
|
+
the same `app.command`: targets declaring one command share one process; see
|
|
264
|
+
[several targets](#several-targets). Every retry starts at
|
|
265
|
+
the target's configured size.
|
|
266
|
+
|
|
267
|
+
### Fill the window
|
|
268
|
+
|
|
269
|
+
`viewport: null` sets no size, so the page fills the browser window. The
|
|
270
|
+
agent, screenshots, and videos use that size. Use it in a headed run, or
|
|
271
|
+
with a hosted browser whose live view would show a fixed 1280 by 720 page
|
|
272
|
+
in one corner:
|
|
273
|
+
|
|
274
|
+
```ts title="e2e.config.ts"
|
|
275
|
+
{ name: 'window', engine: web({ viewport: null }), app }
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The page takes the window's size, minus the toolbar in a headed browser. A
|
|
279
|
+
local headed launch opens a small window; a hosted browser uses the window
|
|
280
|
+
size you requested from the service. `browser.setViewport` still works.
|
|
281
|
+
|
|
282
|
+
### Resize inside a test
|
|
283
|
+
|
|
284
|
+
`browser.setViewport` resizes the page for the rest of the attempt, through
|
|
285
|
+
`app.restart()` and `app.clearState()`:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { test } from '@e2e-dev/web';
|
|
289
|
+
import { expect } from 'e2e';
|
|
290
|
+
|
|
291
|
+
test('the menu collapses on a narrow screen', async ({ app, screen, browser }) => {
|
|
292
|
+
await app.open('/');
|
|
293
|
+
await expect(screen.getByRole('navigation')).toBeVisible();
|
|
294
|
+
|
|
295
|
+
await browser.setViewport({ width: 390, height: 844 });
|
|
296
|
+
await expect(screen.getByRole('button', 'Menu')).toBeVisible();
|
|
297
|
+
await expect(screen.getByRole('navigation')).toBeHidden();
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Use this when one test needs to check a layout before and after resizing.
|
|
302
|
+
For a whole suite, configure separate targets instead.
|
|
303
|
+
|
|
304
|
+
Call it before `app.open` to load the first page at that size, so the app
|
|
305
|
+
never renders its desktop layout first:
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
await browser.setViewport({ width: 390, height: 844 });
|
|
309
|
+
await app.open('/');
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The resize appears as a step in the report. To check the exact width, read
|
|
313
|
+
it from the page:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
const width = await browser.evaluate(() => window.innerWidth);
|
|
317
|
+
expect(width).toBe(390);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### What the agent sees
|
|
321
|
+
|
|
322
|
+
The agent receives the current viewport size with each observation.
|
|
323
|
+
Screenshots cover that viewport. Off-screen elements can still appear in
|
|
324
|
+
the tree, and tapping them scrolls them into view. The agent can scroll to
|
|
325
|
+
reveal lazy-loaded content.
|
|
326
|
+
|
|
327
|
+
Resize before calling `agent.act`:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
test('the mobile menu reaches pricing', async ({ app, agent, browser }) => {
|
|
331
|
+
await browser.setViewport({ width: 390, height: 844 });
|
|
332
|
+
await app.open('/');
|
|
333
|
+
await agent.act('open the navigation menu and go to Pricing');
|
|
334
|
+
await expect(browser).toHaveURL('/pricing');
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Scoping a test to one size
|
|
339
|
+
|
|
340
|
+
`platforms` and `requires` cannot select a viewport; all Playwright targets
|
|
341
|
+
use platform `web`. To skip a test at some sizes, check the width in the
|
|
342
|
+
test:
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
test('the sidebar stays open', async ({ app, screen, browser }) => {
|
|
346
|
+
await app.open('/');
|
|
347
|
+
test.skip((await browser.evaluate(() => window.innerWidth)) < 768, 'desktop layout only');
|
|
348
|
+
await expect(screen.getByRole('complementary')).toBeVisible();
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
To keep a whole group of size-specific tests apart, give them their own
|
|
353
|
+
config and run it with `--config`.
|
|
354
|
+
|
|
355
|
+
### Limits
|
|
356
|
+
|
|
357
|
+
A narrow viewport tests responsive layout. It still uses a desktop browser
|
|
358
|
+
with a scale factor of 1, desktop pointer behavior, and its usual user agent.
|
|
359
|
+
It does not emulate touch, color scheme, or reduced-motion preferences.
|
|
360
|
+
Use the [device engine](/mobile) for tests on a simulator or emulator.
|
|
361
|
+
|
|
362
|
+
A video keeps the size the page had when it started recording; set
|
|
363
|
+
`web({ screencast: { size } })` for one fixed size. A browser provider that
|
|
364
|
+
records, such as [Kernel](/integrations/kernel#recordings), records the whole
|
|
365
|
+
screen instead.
|
|
366
|
+
|
|
224
367
|
<CardGroup cols={2}>
|
|
368
|
+
<Card title="Working with the browser" icon="globe" href="/browser">
|
|
369
|
+
Routes, cookies, dialogs, downloads, and the rest of the `browser` fixture.
|
|
370
|
+
</Card>
|
|
225
371
|
<Card title="Config reference" icon="sliders" href="/reference/config#the-app-under-test">
|
|
226
372
|
`app`, `command`, and every app key.
|
|
227
373
|
</Card>
|
|
228
374
|
<Card title="Web engine reference" icon="masks-theater" href="/reference/web">
|
|
229
|
-
Browser, viewport, `connect`, `headers`, `basicAuth
|
|
375
|
+
Browser, viewport, `connect`, `headers`, `basicAuth`, and every `browser` method.
|
|
230
376
|
</Card>
|
|
231
377
|
</CardGroup>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Writing an engine
|
|
3
3
|
description: Implement the e2e engine contract for another UI platform.
|
|
4
|
+
icon: "gear"
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
An engine connects `screen`, `app`, and agent calls to a UI platform through
|
|
@@ -40,7 +41,7 @@ the engine's capabilities when the config loads.
|
|
|
40
41
|
This skeleton assumes a `createClient` adapter for your platform. Its nodes
|
|
41
42
|
must follow the semantic tree contract described below.
|
|
42
43
|
|
|
43
|
-
```ts title="src/index.ts"
|
|
44
|
+
```ts title="src/index.ts" expandable
|
|
44
45
|
import { ConfigurationError, defineEngine, resolveExpression, type EngineHandle } from 'e2e/engine';
|
|
45
46
|
|
|
46
47
|
export function hyperdrive(options: { device: string }): EngineHandle {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "e2e",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "SDK, runner, and CLI for agentic end-to-end testing",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"e2e",
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
"./oauth/grok": {
|
|
63
63
|
"types": "./dist/oauth/grok.d.ts",
|
|
64
64
|
"default": "./dist/oauth/grok.js"
|
|
65
|
+
},
|
|
66
|
+
"./oauth/opencode-console": {
|
|
67
|
+
"types": "./dist/oauth/opencode-console.d.ts",
|
|
68
|
+
"default": "./dist/oauth/opencode-console.js"
|
|
65
69
|
}
|
|
66
70
|
},
|
|
67
71
|
"bin": {
|
|
@@ -75,23 +79,35 @@
|
|
|
75
79
|
"NOTICE"
|
|
76
80
|
],
|
|
77
81
|
"dependencies": {
|
|
78
|
-
"@ai-sdk/provider": "4.0.
|
|
82
|
+
"@ai-sdk/provider": "4.0.21",
|
|
79
83
|
"@clack/prompts": "1.8.1",
|
|
80
|
-
"@
|
|
81
|
-
"@
|
|
84
|
+
"@jridgewell/remapping": "2.3.5",
|
|
85
|
+
"@modelcontextprotocol/server": "2.3.0",
|
|
86
|
+
"@oxc-project/runtime": "0.152.0",
|
|
87
|
+
"@vitest/expect": "5.0.3",
|
|
82
88
|
"commander": "15.0.0",
|
|
89
|
+
"get-tsconfig": "4.14.3",
|
|
90
|
+
"oxc-parser": "0.152.0",
|
|
91
|
+
"oxc-transform": "0.152.0",
|
|
83
92
|
"picocolors": "1.1.1",
|
|
84
93
|
"pngjs": "7.0.0",
|
|
85
|
-
"tsx": "4.23.14",
|
|
86
94
|
"zod": "4.6.1"
|
|
87
95
|
},
|
|
88
96
|
"peerDependencies": {
|
|
97
|
+
"@ai-sdk/anthropic": "^4.0.0",
|
|
98
|
+
"@ai-sdk/google": "^4.0.0",
|
|
89
99
|
"@ai-sdk/openai": "^4.0.0",
|
|
90
100
|
"@ai-sdk/openai-compatible": "^3.0.0",
|
|
91
101
|
"@ai-sdk/xai": "^5.0.0",
|
|
92
102
|
"ai": "^7.0.0"
|
|
93
103
|
},
|
|
94
104
|
"peerDependenciesMeta": {
|
|
105
|
+
"@ai-sdk/anthropic": {
|
|
106
|
+
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"@ai-sdk/google": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
95
111
|
"@ai-sdk/openai": {
|
|
96
112
|
"optional": true
|
|
97
113
|
},
|
|
@@ -106,10 +122,12 @@
|
|
|
106
122
|
}
|
|
107
123
|
},
|
|
108
124
|
"devDependencies": {
|
|
125
|
+
"@ai-sdk/anthropic": "4.0.58",
|
|
126
|
+
"@ai-sdk/google": "4.0.77",
|
|
109
127
|
"@ai-sdk/openai": "4.0.71",
|
|
110
128
|
"@ai-sdk/openai-compatible": "3.0.53",
|
|
111
129
|
"@ai-sdk/xai": "5.0.4",
|
|
112
|
-
"@e2e-dev/web": "0.
|
|
130
|
+
"@e2e-dev/web": "0.13.0",
|
|
113
131
|
"@modelcontextprotocol/client": "2.2.0",
|
|
114
132
|
"@openrouter/ai-sdk-provider": "3.1.0",
|
|
115
133
|
"@types/node": "26.6.2",
|
|
@@ -117,12 +135,11 @@
|
|
|
117
135
|
"ai": "7.0.107",
|
|
118
136
|
"ajv": "8.20.0",
|
|
119
137
|
"ajv-formats": "3.0.1",
|
|
120
|
-
"playwright": "1.63.0",
|
|
121
138
|
"typescript": "7.0.2",
|
|
122
139
|
"vitest": "5.0.1"
|
|
123
140
|
},
|
|
124
141
|
"engines": {
|
|
125
|
-
"node": "
|
|
142
|
+
"node": "^22.22.3 || >=24.8.0"
|
|
126
143
|
},
|
|
127
144
|
"scripts": {
|
|
128
145
|
"build": "node scripts/prepare-build.ts && tsc --project tsconfig.build.json && node scripts/write-sibling-versions.ts && node ../../scripts/stamp-dist.ts",
|
|
@@ -570,13 +570,14 @@
|
|
|
570
570
|
"summary": {
|
|
571
571
|
"type": "object",
|
|
572
572
|
"additionalProperties": false,
|
|
573
|
-
"required": ["discovered", "selected", "executed", "passed", "failed", "flaky", "skipped"],
|
|
573
|
+
"required": ["discovered", "selected", "executed", "passed", "failed", "interrupted", "flaky", "skipped"],
|
|
574
574
|
"properties": {
|
|
575
575
|
"discovered": { "type": "integer", "minimum": 0 },
|
|
576
576
|
"selected": { "type": "integer", "minimum": 0 },
|
|
577
577
|
"executed": { "type": "integer", "minimum": 0 },
|
|
578
578
|
"passed": { "type": "integer", "minimum": 0 },
|
|
579
579
|
"failed": { "type": "integer", "minimum": 0 },
|
|
580
|
+
"interrupted": { "type": "integer", "minimum": 0 },
|
|
580
581
|
"flaky": { "type": "integer", "minimum": 0 },
|
|
581
582
|
"skipped": { "type": "integer", "minimum": 0 }
|
|
582
583
|
}
|
|
@@ -670,10 +671,22 @@
|
|
|
670
671
|
"summary": { "$ref": "#/$defs/summary" },
|
|
671
672
|
"limits": { "$ref": "#/$defs/limits" },
|
|
672
673
|
"usage": { "$ref": "#/$defs/usage" },
|
|
674
|
+
"carried": { "$ref": "#/$defs/carried" },
|
|
673
675
|
"explore": { "$ref": "#/$defs/explore" },
|
|
674
676
|
"extensions": { "$ref": "#/$defs/extensions" }
|
|
675
677
|
}
|
|
676
678
|
},
|
|
679
|
+
"carried": {
|
|
680
|
+
"description": "What earlier runs left unresolved that a --last-failed rerun did not run again: their results as they reported them, the serial groups those results' attempts live on, and the suite hook failures whose scope still holds one of them. The next --last-failed selects these tests too. Not part of the run's status, exit code, or summary.",
|
|
681
|
+
"type": "object",
|
|
682
|
+
"additionalProperties": false,
|
|
683
|
+
"required": ["results", "serialGroups", "errors"],
|
|
684
|
+
"properties": {
|
|
685
|
+
"results": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/result" } },
|
|
686
|
+
"serialGroups": { "type": "array", "items": { "$ref": "#/$defs/serialGroup" } },
|
|
687
|
+
"errors": { "type": "array", "items": { "$ref": "#/$defs/error" } }
|
|
688
|
+
}
|
|
689
|
+
},
|
|
677
690
|
"exploreFinding": {
|
|
678
691
|
"type": "object",
|
|
679
692
|
"additionalProperties": false,
|
package/skills/e2e/SKILL.md
CHANGED
|
@@ -10,8 +10,8 @@ one goal; `agent.assert`, `agent.waitFor`, and `agent.extract` judge the
|
|
|
10
10
|
screen. `screen`, `app`, `browser`, and `expect` make exact interactions and
|
|
11
11
|
checks. The replay cache reruns verified actions and checks their recorded end
|
|
12
12
|
state without a model call; agent judgments still run live. UI targets use
|
|
13
|
-
`@e2e-dev/web` for browsers or `@e2e-dev/mobile` for iOS simulators
|
|
14
|
-
Android emulators. A test that takes only `app` can check an API with `fetch`
|
|
13
|
+
`@e2e-dev/web` for browsers or `@e2e-dev/mobile` for iOS simulators,
|
|
14
|
+
Android emulators, and connected phones. A test that takes only `app` can check an API with `fetch`
|
|
15
15
|
and `expect` (topic `writing-tests`). Model sign-in commands are in
|
|
16
16
|
[setup](references/setup.md#subscriptions-and-api-keys).
|
|
17
17
|
|
|
@@ -61,7 +61,9 @@ one; the installed CLI prints the same text with `npx e2e guide <topic>`
|
|
|
61
61
|
(`e2e guide` alone prints this page). For anything the topics do not cover,
|
|
62
62
|
the full documentation ships in the `docs/` directory of the installed `e2e`
|
|
63
63
|
package (`node_modules/e2e/docs` in a single-package project); a link such as
|
|
64
|
-
`/reference/cli` is `docs/reference/cli.mdx`.
|
|
64
|
+
`/reference/cli` is `docs/reference/cli.mdx`. Complete projects for Vite,
|
|
65
|
+
Next.js, Expo, and SwiftUI, each with its config, scripts, and a passing
|
|
66
|
+
suite, are in https://github.com/tester-army/e2e/tree/main/examples.
|
|
65
67
|
|
|
66
68
|
| Topic | File | Read it when |
|
|
67
69
|
| --- | --- | --- |
|
|
@@ -6,8 +6,13 @@ model-call budget, no shared transcript. No agent step, no model calls.
|
|
|
6
6
|
|
|
7
7
|
## Configure a model
|
|
8
8
|
|
|
9
|
-
Put an AI SDK model under `agents.default`.
|
|
10
|
-
|
|
9
|
+
Put an AI SDK model under `agents.default`. You can use all AI SDK providers
|
|
10
|
+
that support tool calls and the language model specification v2 or later
|
|
11
|
+
(https://ai-sdk.dev/providers). Examples: `@ai-sdk/openai`,
|
|
12
|
+
`@ai-sdk/anthropic`, `@ai-sdk/google`, `@ai-sdk/amazon-bedrock`,
|
|
13
|
+
`@openrouter/ai-sdk-provider`, `ollama-ai-provider-v2`. For a server with
|
|
14
|
+
`/v1/chat/completions`, use `@ai-sdk/openai-compatible`. Vercel AI Gateway
|
|
15
|
+
reads `AI_GATEWAY_API_KEY` or, without it, a Vercel OIDC token:
|
|
11
16
|
|
|
12
17
|
```ts
|
|
13
18
|
import type { E2EConfig } from 'e2e';
|
|
@@ -24,6 +29,14 @@ Subscription logins and local models:
|
|
|
24
29
|
[setup](setup.md#subscriptions-and-api-keys). Keep `ai@^7` installed with
|
|
25
30
|
any provider.
|
|
26
31
|
|
|
32
|
+
Decision models pick actions from choices the executor builds: install
|
|
33
|
+
`@e2e-dev/decision` and put `decisionExecutor({ model:
|
|
34
|
+
typeSafeAi.decisionModel('jev-latest'), textModel: openrouter('inception/mercury-2.5') })`
|
|
35
|
+
under `executor`. Tests stay plain language with no params; the text model
|
|
36
|
+
writes field values. See the shipped `docs/decision-models.mdx` or
|
|
37
|
+
[the online guide](https://e2e.tester.army/docs/decision-models) for setup,
|
|
38
|
+
gates, and limits.
|
|
39
|
+
|
|
27
40
|
- Pass a model instance, not a string (`INVALID_CONFIG`).
|
|
28
41
|
- An agents entry is one plain object of `model`, `judge`, `system`,
|
|
29
42
|
`context`, `tools`, `maxSteps`, `maxModelCalls`, `judgmentTimeout`,
|
|
@@ -32,7 +45,8 @@ any provider.
|
|
|
32
45
|
- `model` drives `agent.act`. Judgments use `judge` when set, else `model`.
|
|
33
46
|
- A missing model for the built-in agent raises one run-level
|
|
34
47
|
`MODEL_UNAVAILABLE` when the first test acquires `agent`, exit 2; auth
|
|
35
|
-
failures surface on the first model call as `MODEL_PROVIDER_FAILED
|
|
48
|
+
failures surface on the first model call as `MODEL_PROVIDER_FAILED`, or as
|
|
49
|
+
`CONFIG_LOAD_FAILED` for a provider that reads its key when the config loads.
|
|
36
50
|
- `context` is what the app calls things, sent to every model call, judges
|
|
37
51
|
included; `system` is how the acting agent works, read only by the act
|
|
38
52
|
loop. Both live on the agents entry; `agentContext` on a test or group
|
|
@@ -50,8 +64,8 @@ Tests use `agents.default` unless selected otherwise:
|
|
|
50
64
|
- `{ agent: 'name' }` on an `agent.*` call overrides the test's choice.
|
|
51
65
|
|
|
52
66
|
For signed-in personas, pair an agent with a `session` in a describe block
|
|
53
|
-
repeated per persona; the cache
|
|
54
|
-
|
|
67
|
+
repeated per persona; the cache keys every entry by the agent and its
|
|
68
|
+
context, so each persona records and replays its own steps.
|
|
55
69
|
|
|
56
70
|
## act: one goal
|
|
57
71
|
|
|
@@ -156,6 +170,9 @@ expect(data.titles).toContain('Buy milk');
|
|
|
156
170
|
- Judgments see the assertion and the current screen only, never prior
|
|
157
171
|
steps or the act loop's summaries; malformed output gets one repair
|
|
158
172
|
round, then `MODEL_OUTPUT_INVALID`.
|
|
173
|
+
- A value shown in more than one place (a total in the summary and on the
|
|
174
|
+
pay button) must agree everywhere, or the judgment fails. Name the one
|
|
175
|
+
you mean (`'the order summary total is $42.00'`) when only it matters.
|
|
159
176
|
- `waitFor` observes every `interval` (default 3 s), judges only when the
|
|
160
177
|
screen changed, and is `STEP_TIMEOUT` after `timeout` (default 30 s).
|
|
161
178
|
- `extract` takes any Standard Schema validator (zod works); the model sees
|
|
@@ -236,14 +253,19 @@ matches. Misses and hand-offs use the model; `agent.assert`,
|
|
|
236
253
|
`agent.assert`, `agent.waitFor`), so an unchecked `act` never replays; a
|
|
237
254
|
plain-value `expect`, `expect.poll`, `agent.extract`, another `act`, or
|
|
238
255
|
the attempt passing confirms nothing.
|
|
239
|
-
- A replay needs the app on the recorded
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
256
|
+
- A replay needs the app on the recorded route (origin, path, and query,
|
|
257
|
+
ids and tokens aside; unless the recording opens with a navigation),
|
|
258
|
+
re-finds each control by role, name, test id, placeholder, input purpose,
|
|
259
|
+
and its named row (an unnamed control with twins and no named container
|
|
260
|
+
hands off), and passes alone only when the recorded end route is back,
|
|
261
|
+
every control that appeared (with its checked or selected state) is
|
|
262
|
+
there, every one that went away is gone, at least one of those changed
|
|
263
|
+
during the replay, and no new alert showed; otherwise the agent takes
|
|
264
|
+
over mid-step. Entries are keyed per agent and per `agentContext`. `step.cache.reason` says why: `no-entry`,
|
|
244
265
|
`wrong-context`, `target-not-found`, `target-ambiguous`, `end-mismatch`,
|
|
245
266
|
and so on.
|
|
246
|
-
- A step recording no actions
|
|
267
|
+
- A step recording no actions, or changing nothing on screen or in the
|
|
268
|
+
route, creates no entry; one whose `unique()` value
|
|
247
269
|
equals, is spelled inside, or is the encoded form of another param's value
|
|
248
270
|
is not recorded either (`step.cache.notRecorded`: `param-collision`).
|
|
249
271
|
- `e2e init` gitignores `.e2e/cache/`; remove that line to commit entries
|
|
@@ -28,7 +28,7 @@ jq '.run.results[] | select(.selected and .status != "passed") | .attempts[-1]
|
|
|
28
28
|
(recorded as `skipped`).
|
|
29
29
|
4. Artifacts, under `.e2e/artifacts/`: `failure/screen.txt`
|
|
30
30
|
and the engine's screenshot per failed attempt; a Playwright trace per
|
|
31
|
-
traced attempt (`npx playwright show-trace <file
|
|
31
|
+
traced attempt (`npx playwright-core@1.63.0 show-trace <file>`; `pnpm dlx` under pnpm); downloads; with
|
|
32
32
|
`--video` the recording (`video/video.webm` in a local browser, each
|
|
33
33
|
later page `video/video-part<n>.webm` with its own `startedAt`;
|
|
34
34
|
`video/video.mp4` on a device; a provider's file or link); with
|
|
@@ -46,7 +46,7 @@ jq '.run.results[] | select(.selected and .status != "passed") | .attempts[-1]
|
|
|
46
46
|
| `COLLECTION_ERROR` | `async` describe body, `test.setup` inside `describe`, an option forbidden in a serial group, registration outside collection | Restructure per `writing-tests` |
|
|
47
47
|
| `HOOK_FAILED` | `beforeAll` or `afterAll` threw; its scope's tests skip | Fix the hook; the report carries its error |
|
|
48
48
|
| `UNSUPPORTED_ARTIFACT` | A test's or target's `trace` or `video` on an engine that cannot record | Drop it there, or set it at the config root or CLI (such targets skip with a notice) |
|
|
49
|
-
| `BROWSER_INSTALL_FAILED`, `LAUNCH_TIMEOUT` | Browser download failed; engine init or attempt start exceeded `launchTimeout` | Run the quoted `npx
|
|
49
|
+
| `BROWSER_INSTALL_FAILED`, `LAUNCH_TIMEOUT` | Browser download failed; engine init or attempt start exceeded `launchTimeout` | Run the quoted `npx @e2e-dev/web install <names>` or `pnpm exec e2e-web install <names>` (`--with-deps` on bare Linux); raise the root `launchTimeout` (60 s default) |
|
|
50
50
|
| `APP_UNREACHABLE` | `app.command` never answered `readyUrl` within `startupTimeout`; on a device, a message naming the iOS automation runner: the runner failed, not the app | Read the quoted log lines; check the port, `app.url`, `app.command.env`, `app.command.startupTimeout`. Runner: rerun, else `npx agent-device daemon stop` and reboot the simulator |
|
|
51
51
|
| `APP_ALREADY_RUNNING` | Something already serves `url` when `command` should start | Stop it, or `reuseExisting: true` locally |
|
|
52
52
|
| `APP_URL_REQUIRED`, `APP_NOT_OPEN` | A navigation on a target without `app.url`; a `screen` call before `app.open()` | Add `app.url` to the target; open the app first |
|
|
@@ -58,14 +58,14 @@ jq '.run.results[] | select(.selected and .status != "passed") | .attempts[-1]
|
|
|
58
58
|
| `MODEL_OUTPUT_INVALID` | An `extract` or `assert` answer failed the schema after one repair round | Simplify the schema or question; pick a stronger model |
|
|
59
59
|
| `ACTION_FAILED` | Element not actionable (covered, disabled, detached), or an operation timed out | `expect` the condition first; close overlays; check `actionTimeout` |
|
|
60
60
|
| `TEST_TIMEOUT` | The attempt exceeded `timeout` (120 s) | Split the test, or raise `timeout` |
|
|
61
|
-
| `STEP_NOT_AWAITED` | The body returned while a step still ran: a call without `await` | `await` the call the code frame names (every `app`, `agent`, `screen`, `expect` call) |
|
|
61
|
+
| `STEP_NOT_AWAITED` | The body returned while a step or `expect.poll` still ran, or an `afterEach` hook or fixture teardown returned while an `expect.poll` still ran: a call without `await` | `await` the call the code frame names (every `app`, `agent`, `screen`, `expect`, `expect.poll` call) |
|
|
62
62
|
| `MODEL_UNAVAILABLE` | `agents.<name>.model` holds no AI SDK instance: reported once under `run.errors` at the first `agent` fixture; the run stops. A model-less custom executor fails only `waitFor` and `extract` this way | Construct one, e.g. `gateway('openai/gpt-6-luna-fast')` from `ai`, and export its key (`AI_GATEWAY_API_KEY`) |
|
|
63
63
|
| `MODEL_PROVIDER_FAILED` | Network, 5xx, rate limit, no credits, or no response in 120 s, after the transport retries | Check the credential (the key; for keyless `gateway()` the Vercel CLI login and `.vercel/project.json`) and quota; retry (exit 3) |
|
|
64
64
|
| `REPLAY_STALE` | `--strict-cache` or `cache.strict`, and a committed recording no longer replays; `step.cache.reason` says why | Rerun read-write with the knob the message names off; commit the changed entry under the directory it names (a custom `cache.store` is written by that run) |
|
|
65
65
|
| `AUTOMATION_UNSUPPORTED` | The step needs an interaction the engine's toolset lacks (drag on a device, say); blocked, exit 1 | Do that step with `screen` actions, or run the test on a target that supports it |
|
|
66
66
|
| `STEP_TIMEOUT`, `STEP_BUDGET_EXHAUSTED` | Goal too big or ambiguous, or a slow provider | Split the goal, use on-screen wording, add vocabulary via `agents.<name>.context` or `agentContext`; raise `maxSteps` or `maxModelCalls` (budget), the config `timeout` (`act`), `judgmentTimeout` (judgments); `--debug` |
|
|
67
67
|
| `CONTEXT_OVERFLOW` | Screen plus step history did not fit the model's context window (`act` after one shrink-and-retry; a judgment on the first overflow) | Lower `agents.<name>.maxObservationBytes`, split the step, or pick a larger-window model |
|
|
68
|
-
| `POLICY_DENIED` | A `file:`, `
|
|
68
|
+
| `POLICY_DENIED` | A URL whose scheme is not `http:` or `https:` (`file:`, `view-source:`, `data:`); a password `Secret` into a non-password sink; reading a secure field, `toHaveValue`, `toHaveText`, `toContainText`, `toHaveAttribute` included; `app.screenshot()` after a secret fill, or an `act` or `vision: true` step whose tree read timed out after one | http(s) or `about:blank` only; passwords into password inputs only; assert the outcome, not the value; screenshot before filling secrets |
|
|
69
69
|
| `UNSUPPORTED_CAPABILITY` | A fixture the engine lacks (`browser` on a device), `schema` or `vision` on `act`, an action the surface lacks | Declare `requires: ['browser']`; drop the option or action |
|
|
70
70
|
| `SESSION_UNAVAILABLE`, `SESSION_CONTRACT` | `session: 'x'` with no setup saving `x`; a setup that skipped a declared name. `SESSION_MISMATCH`, `SESSION_EXPIRED`, `SESSION_INVALID`: the stored session is another run's or app's, expired, or corrupt | Add or fix the `test.setup`; rerun it |
|
|
71
71
|
| `ONLY_IN_CI` | `test.only` reached CI | Remove it |
|
|
@@ -24,8 +24,9 @@ No model is needed: without the optional `ai` package, sessions open and the
|
|
|
24
24
|
deterministic tools work.
|
|
25
25
|
|
|
26
26
|
Flags: `--config <path>` names the default config file, `--target <name>`
|
|
27
|
-
fixes the target every session opens on, `--
|
|
28
|
-
simulator
|
|
27
|
+
fixes the target every session opens on, `--headed` shows the browser or
|
|
28
|
+
simulator when the engine supports it in sessions that do not set `headed`
|
|
29
|
+
(sessions are headless by default, as in `e2e run`), `--max-sessions <n>`
|
|
29
30
|
sets how many sessions may be open at once (default 4, 1 through 16).
|
|
30
31
|
|
|
31
32
|
## Tools
|
|
@@ -34,7 +35,7 @@ Four tools; everything a session can do is a catalog behind `call`.
|
|
|
34
35
|
|
|
35
36
|
| Tool | Does |
|
|
36
37
|
| --- | --- |
|
|
37
|
-
| `open_session` | Loads the config (`config` names another file; default the nearest `e2e.config.ts`), starts the declared app command if any, boots the engine, opens the app URL, and returns the session id, the catalog, and the first observation. `target` is required when the config declares several. Each session has its own browser or device. |
|
|
38
|
+
| `open_session` | Loads the config (`config` names another file; default the nearest `e2e.config.ts`), starts the declared app command if any, boots the engine, opens the app URL, and returns the session id, the catalog, and the first observation. `target` is required when the config declares several. `headed: true` shows the browser or simulator when the user wants to watch; without it the session follows the server's `--headed`, headless by default. Each session has its own browser or device. |
|
|
38
39
|
| `tools` | The catalog: one line per tool with its argument names (`?` marks optional), the first sentence of its description, and `[read-only]` where it changes nothing. `tools {tool}` shows the full description and the JSON Schema of its arguments. |
|
|
39
40
|
| `call` | Runs one catalog tool: `call {tool: "tap", args: {target: "n42"}}`. Arguments are checked against the tool's schema first; a wrong one fails with `INVALID_ARGUMENT` naming the field. |
|
|
40
41
|
| `close_session` | Saves a recording still running, ends the attempt, disposes the engine, stops the app processes the session started once no other session uses them. |
|
|
@@ -57,6 +58,10 @@ The catalog, per session:
|
|
|
57
58
|
| `stop_recording` | Stops it and returns the absolute path of each video file, under `<output>/videos/<session>/` (`.e2e` by default), or the URL of a provider's own recording. |
|
|
58
59
|
| Project tools | Every `defineTool` in the agent's `tools` that applies to the target's platform, under its own name; an engine pack such as `mobileTools` adds `open_app`, `swipe`, `alert`. |
|
|
59
60
|
|
|
61
|
+
Project tools use their `toModelOutput` renderer for strings as well as
|
|
62
|
+
structured results. Its `error-text` and `error-json` outputs set the MCP
|
|
63
|
+
result's `isError: true`; treat them as failed calls.
|
|
64
|
+
|
|
60
65
|
Once a secret is filled in the session, `screenshot` and the point tools
|
|
61
66
|
(`tap_at`, `hover_at`, `type_at`, `press_at`, `select_at`) answer with a
|
|
62
67
|
`PIXEL_TAINTED` line for the rest of it; act on listed nodes by id (topic
|