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.
Files changed (256) hide show
  1. package/README.md +10 -0
  2. package/dist/.build.json +1 -1
  3. package/dist/agent/act.js +7 -4
  4. package/dist/agent/action-dispatcher.js +4 -5
  5. package/dist/agent/actions.d.ts +32 -23
  6. package/dist/agent/actions.js +38 -24
  7. package/dist/agent/ai-sdk.js +5 -4
  8. package/dist/agent/error.js +3 -3
  9. package/dist/agent/invocation.js +3 -4
  10. package/dist/agent/model/sdk.js +2 -0
  11. package/dist/agent/model/tool-choice.d.ts +9 -0
  12. package/dist/agent/model/tool-choice.js +14 -0
  13. package/dist/agent/observation-feed.d.ts +5 -6
  14. package/dist/agent/observation-feed.js +8 -11
  15. package/dist/agent/observation.d.ts +40 -11
  16. package/dist/agent/observation.js +109 -59
  17. package/dist/agent/phases.d.ts +7 -1
  18. package/dist/agent/phases.js +12 -3
  19. package/dist/agent/point-tap.d.ts +8 -8
  20. package/dist/agent/point-tap.js +1 -1
  21. package/dist/agent/prompts.js +3 -0
  22. package/dist/agent/replay.d.ts +19 -8
  23. package/dist/agent/replay.js +29 -29
  24. package/dist/agent/scroll-target.d.ts +2 -3
  25. package/dist/agent/step-cache.d.ts +43 -15
  26. package/dist/agent/step-cache.js +156 -67
  27. package/dist/agent/tool-loop.js +94 -5
  28. package/dist/cache/anchors.d.ts +72 -23
  29. package/dist/cache/anchors.js +233 -60
  30. package/dist/cache/context.d.ts +25 -14
  31. package/dist/cache/context.js +50 -8
  32. package/dist/cache/decide.d.ts +2 -2
  33. package/dist/cache/decide.js +4 -6
  34. package/dist/cache/identity.d.ts +18 -4
  35. package/dist/cache/identity.js +9 -4
  36. package/dist/cache/recorder.d.ts +15 -1
  37. package/dist/cache/recorder.js +36 -23
  38. package/dist/cache/rekeyed.d.ts +46 -0
  39. package/dist/cache/rekeyed.js +89 -0
  40. package/dist/cache/relocate.d.ts +14 -14
  41. package/dist/cache/relocate.js +33 -25
  42. package/dist/cache/route.d.ts +32 -33
  43. package/dist/cache/route.js +94 -50
  44. package/dist/cache/store.d.ts +6 -0
  45. package/dist/cache/store.js +21 -1
  46. package/dist/cache/trace.d.ts +53 -14
  47. package/dist/cache/trace.js +76 -30
  48. package/dist/cli/bin.js +2 -2
  49. package/dist/cli/index.js +43 -32
  50. package/dist/cli/init/engines.d.ts +1 -3
  51. package/dist/cli/init/engines.js +2 -2
  52. package/dist/cli/init/gateways.d.ts +1 -1
  53. package/dist/cli/init/gateways.js +11 -1
  54. package/dist/cli/init/mcp-config.d.ts +2 -1
  55. package/dist/cli/init/mcp-config.js +8 -3
  56. package/dist/cli/init/sibling-versions.json +1 -1
  57. package/dist/cli/init/versions.d.ts +0 -8
  58. package/dist/cli/init/versions.js +1 -10
  59. package/dist/cli/mcp.d.ts +3 -2
  60. package/dist/cli/mcp.js +52 -16
  61. package/dist/cli/run-output.d.ts +17 -0
  62. package/dist/cli/run-output.js +45 -0
  63. package/dist/collect/collect.js +8 -4
  64. package/dist/collect/registry.js +27 -16
  65. package/dist/config/compiled-files.d.ts +36 -0
  66. package/dist/config/compiled-files.js +50 -0
  67. package/dist/config/diagnose.js +21 -19
  68. package/dist/config/esm-hooks.d.ts +39 -44
  69. package/dist/config/esm-hooks.js +238 -63
  70. package/dist/config/load.d.ts +1 -7
  71. package/dist/config/load.js +23 -66
  72. package/dist/config/register.d.ts +8 -0
  73. package/dist/config/register.js +9 -0
  74. package/dist/config/resolve.d.ts +1 -0
  75. package/dist/config/resolve.js +33 -1
  76. package/dist/config/specifiers.d.ts +19 -0
  77. package/dist/config/specifiers.js +33 -0
  78. package/dist/config/tsconfig.d.ts +56 -0
  79. package/dist/config/tsconfig.js +115 -0
  80. package/dist/config/typescript.d.ts +19 -0
  81. package/dist/config/typescript.js +253 -0
  82. package/dist/engine/contract.d.ts +1 -0
  83. package/dist/engine/index.d.ts +4 -2
  84. package/dist/engine/session.js +2 -2
  85. package/dist/expect/async.js +4 -2
  86. package/dist/expect/poll-scope.d.ts +52 -0
  87. package/dist/expect/poll-scope.js +113 -0
  88. package/dist/expect/poll.js +33 -6
  89. package/dist/expect/values.js +11 -4
  90. package/dist/internal/abandoned.d.ts +11 -0
  91. package/dist/internal/abandoned.js +23 -0
  92. package/dist/internal/brands.d.ts +2 -0
  93. package/dist/internal/brands.js +2 -0
  94. package/dist/internal/client-identity.d.ts +9 -0
  95. package/dist/internal/client-identity.js +14 -0
  96. package/dist/internal/errors.d.ts +10 -3
  97. package/dist/internal/errors.js +23 -8
  98. package/dist/internal/node-version.d.ts +16 -7
  99. package/dist/internal/node-version.js +50 -15
  100. package/dist/internal/package-manager.js +1 -1
  101. package/dist/internal/realm-slot.d.ts +5 -4
  102. package/dist/internal/redact.d.ts +9 -4
  103. package/dist/internal/redact.js +120 -23
  104. package/dist/internal/time.d.ts +26 -4
  105. package/dist/internal/time.js +58 -14
  106. package/dist/internal/urls.d.ts +9 -5
  107. package/dist/internal/urls.js +19 -10
  108. package/dist/locator/engine.d.ts +12 -2
  109. package/dist/locator/engine.js +55 -14
  110. package/dist/locator/screen.js +59 -9
  111. package/dist/mcp/catalog.d.ts +3 -2
  112. package/dist/mcp/catalog.js +6 -6
  113. package/dist/mcp/output.d.ts +59 -0
  114. package/dist/mcp/output.js +129 -0
  115. package/dist/mcp/server.d.ts +4 -0
  116. package/dist/mcp/server.js +9 -12
  117. package/dist/mcp/session.d.ts +8 -1
  118. package/dist/mcp/session.js +56 -15
  119. package/dist/mcp/tools.js +7 -5
  120. package/dist/oauth/chatgpt.js +3 -23
  121. package/dist/oauth/cli.js +1 -0
  122. package/dist/oauth/copilot.d.ts +5 -4
  123. package/dist/oauth/copilot.js +80 -7
  124. package/dist/oauth/grok.js +2 -1
  125. package/dist/oauth/models.js +2 -1
  126. package/dist/oauth/opencode-console.d.ts +10 -0
  127. package/dist/oauth/opencode-console.js +133 -0
  128. package/dist/oauth/providers/github-copilot.d.ts +12 -0
  129. package/dist/oauth/providers/github-copilot.js +77 -23
  130. package/dist/oauth/providers/opencode-console.d.ts +31 -0
  131. package/dist/oauth/providers/opencode-console.js +148 -0
  132. package/dist/oauth/providers.d.ts +2 -6
  133. package/dist/oauth/providers.js +3 -7
  134. package/dist/oauth/responses.d.ts +9 -0
  135. package/dist/oauth/responses.js +23 -0
  136. package/dist/report/build.d.ts +26 -0
  137. package/dist/report/build.js +16 -8
  138. package/dist/report/failure-text.d.ts +6 -1
  139. package/dist/report/failure-text.js +14 -4
  140. package/dist/report/format.d.ts +10 -5
  141. package/dist/report/format.js +12 -5
  142. package/dist/report/junit.js +11 -12
  143. package/dist/report/list.d.ts +12 -0
  144. package/dist/report/list.js +61 -24
  145. package/dist/report/markdown.d.ts +1 -1
  146. package/dist/report/markdown.js +37 -14
  147. package/dist/report/repeats.d.ts +6 -1
  148. package/dist/report/repeats.js +12 -3
  149. package/dist/run/artifacts.d.ts +22 -4
  150. package/dist/run/artifacts.js +71 -3
  151. package/dist/run/execute.d.ts +34 -5
  152. package/dist/run/execute.js +93 -44
  153. package/dist/run/extended-fixtures.js +6 -2
  154. package/dist/run/failure-evidence.js +10 -4
  155. package/dist/run/fixtures.js +6 -8
  156. package/dist/run/in-process.d.ts +2 -0
  157. package/dist/run/in-process.js +52 -10
  158. package/dist/run/last-run.d.ts +42 -8
  159. package/dist/run/last-run.js +121 -14
  160. package/dist/run/process-output.d.ts +73 -0
  161. package/dist/run/process-output.js +130 -0
  162. package/dist/run/provision.d.ts +2 -0
  163. package/dist/run/provision.js +1 -0
  164. package/dist/run/realm.d.ts +4 -1
  165. package/dist/run/realm.js +20 -6
  166. package/dist/run/records.d.ts +20 -4
  167. package/dist/run/records.js +26 -0
  168. package/dist/run/retry.d.ts +10 -1
  169. package/dist/run/retry.js +30 -12
  170. package/dist/run/runner.d.ts +9 -1
  171. package/dist/run/runner.js +101 -39
  172. package/dist/run/scheduler.js +255 -19
  173. package/dist/run/secrecy.d.ts +22 -19
  174. package/dist/run/secrecy.js +22 -18
  175. package/dist/run/serial.d.ts +75 -2
  176. package/dist/run/serial.js +243 -59
  177. package/dist/run/standalone.js +1 -1
  178. package/dist/run/steps.d.ts +0 -6
  179. package/dist/run/steps.js +3 -20
  180. package/dist/run/trace-redaction.d.ts +5 -6
  181. package/dist/run/trace-redaction.js +5 -6
  182. package/dist/run/worker/entry.js +40 -18
  183. package/dist/run/worker/handle.js +10 -5
  184. package/dist/run/worker/protocol.d.ts +85 -3
  185. package/dist/run/worker/session.d.ts +2 -0
  186. package/dist/run/worker/session.js +29 -0
  187. package/dist/telemetry/environment.d.ts +2 -1
  188. package/dist/telemetry/environment.js +12 -2
  189. package/dist/telemetry/events.js +1 -0
  190. package/dist/telemetry/project.d.ts +5 -6
  191. package/dist/telemetry/project.js +6 -11
  192. package/dist/telemetry/store.d.ts +5 -5
  193. package/dist/telemetry/store.js +5 -7
  194. package/dist/telemetry/telemetry.d.ts +2 -3
  195. package/dist/telemetry/telemetry.js +2 -4
  196. package/dist/types.d.ts +15 -6
  197. package/docs/agent-steps.mdx +1 -0
  198. package/docs/agents.mdx +7 -3
  199. package/docs/api-testing.mdx +2 -1
  200. package/docs/authentication.mdx +6 -3
  201. package/docs/browser.mdx +49 -12
  202. package/docs/bug-bash.mdx +2 -1
  203. package/docs/cache.mdx +66 -19
  204. package/docs/ci.mdx +14 -9
  205. package/docs/coding-agents.mdx +2 -1
  206. package/docs/core-concepts.mdx +208 -0
  207. package/docs/debugging.mdx +25 -12
  208. package/docs/decision-models.mdx +142 -0
  209. package/docs/example-projects.mdx +32 -0
  210. package/docs/executors.mdx +7 -2
  211. package/docs/explore.mdx +1 -0
  212. package/docs/github.mdx +14 -5
  213. package/docs/index.mdx +9 -5
  214. package/docs/integrations/eas.mdx +1 -0
  215. package/docs/integrations/index.mdx +1 -0
  216. package/docs/integrations/kernel.mdx +3 -2
  217. package/docs/migrate/cypress.mdx +26 -3
  218. package/docs/migrate/detox.mdx +24 -1
  219. package/docs/migrate/maestro.mdx +28 -4
  220. package/docs/migrate/playwright.mdx +44 -18
  221. package/docs/migrate/selenium.mdx +27 -3
  222. package/docs/mobile-ci.mdx +6 -5
  223. package/docs/mobile.mdx +96 -8
  224. package/docs/models.mdx +60 -9
  225. package/docs/quickstart.mdx +28 -16
  226. package/docs/reference/agent.mdx +13 -3
  227. package/docs/reference/app.mdx +2 -1
  228. package/docs/reference/cli.mdx +41 -18
  229. package/docs/reference/config.mdx +67 -13
  230. package/docs/reference/engine.mdx +25 -13
  231. package/docs/reference/environment.mdx +10 -2
  232. package/docs/reference/errors.mdx +13 -10
  233. package/docs/reference/expect.mdx +36 -13
  234. package/docs/reference/mcp.mdx +21 -8
  235. package/docs/reference/mobile.mdx +24 -13
  236. package/docs/reference/reporters.mdx +9 -2
  237. package/docs/reference/screen.mdx +49 -16
  238. package/docs/reference/test.mdx +32 -5
  239. package/docs/reference/web.mdx +133 -23
  240. package/docs/security.mdx +58 -42
  241. package/docs/subscriptions.mdx +92 -12
  242. package/docs/telemetry.mdx +9 -6
  243. package/docs/tools.mdx +1 -0
  244. package/docs/{starting-your-app.mdx → web.mdx} +157 -11
  245. package/docs/writing-an-engine.mdx +2 -1
  246. package/package.json +25 -8
  247. package/schema/report-v1.schema.json +14 -1
  248. package/skills/e2e/SKILL.md +5 -3
  249. package/skills/e2e/references/agent.md +33 -11
  250. package/skills/e2e/references/debugging.md +4 -4
  251. package/skills/e2e/references/mcp.md +8 -3
  252. package/skills/e2e/references/running.md +15 -9
  253. package/skills/e2e/references/setup.md +45 -16
  254. package/skills/e2e/references/writing-tests.md +35 -18
  255. package/docs/viewports.mdx +0 -146
  256. package/docs/writing-tests.mdx +0 -256
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  title: agent
3
3
  description: Run agent actions, check conditions, and extract structured data.
4
+ icon: "robot"
4
5
  ---
5
6
 
6
7
  Use `act` to interact with the app, `assert` to check the current screen,
@@ -191,7 +192,9 @@ Every `act` observation with unavailable semantics includes that image and
191
192
  warning automatically. Earlier node ids are invalid, and the next successful
192
193
  semantic capture presents the whole tree again. The screenshot shows the
193
194
  current viewport; an unavailable tree does not prove that a control or
194
- message is absent.
195
+ message is absent. After a secret fill there is no fallback: `act`, and a
196
+ judgment with `vision: true`, fails with `POLICY_DENIED` when semantic
197
+ capture times out before the step does.
195
198
 
196
199
  If semantic capture fails, trace replay stops and the step cannot record a
197
200
  new trace entry, even if the tree later recovers.
@@ -238,7 +241,12 @@ assert(assertion: string, options?: AssertOptions): Promise<void>;
238
241
 
239
242
  Checks one fresh observation without polling. The judge sees only the
240
243
  assertion and current observation, never prior steps or `act` summaries.
241
- It answers `holds`, `fails`, or `inconclusive`.
244
+ It answers `holds`, `fails`, or `inconclusive`. When what the assertion is
245
+ about appears more than once, such as a total in a summary and on a pay
246
+ button, every instance must agree: one that contradicts it fails. Name
247
+ the place (`the order summary total is $42.00`) to judge only that one. A
248
+ claim about some item, such as `a todo is marked done`, still holds when one
249
+ item matches.
242
250
 
243
251
  Throws `ASSERTION_FAILED` on a false judgment and `ASSERTION_INCONCLUSIVE`
244
252
  when the screen did not show enough to decide. Both carry the explanation
@@ -271,7 +279,9 @@ Observes and judges at once. After a false or inconclusive judgment it
271
279
  waits out `interval`, then observes every 500 ms (or every `interval`, when
272
280
  shorter) without a model call, and judges again only once the screen
273
281
  differs from the one last judged, pixels included under `vision`. Succeeds
274
- on the first judgment that holds.
282
+ on the first judgment that holds. Judgments follow the `agent.assert`
283
+ rules, so a value shown in more than one place holds only once every
284
+ instance agrees.
275
285
 
276
286
  Throws `STEP_TIMEOUT` on deadline expiry and `STEP_BUDGET_EXHAUSTED` on
277
287
  budget exhaustion, each carrying the last judgment (with the same vision
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  title: app
3
3
  description: The portable app lifecycle fixture, available on every platform.
4
+ icon: "cube"
4
5
  ---
5
6
 
6
7
  `app` controls the app's lifecycle and captures screenshots. Each method
@@ -43,7 +44,7 @@ timeout. They do not accept per-call timeout options.
43
44
 
44
45
  | Method | Timeout | Throws |
45
46
  | --- | --- | --- |
46
- | `open(path?)` | `config.timeout`, 120000 ms | `APP_URL_REQUIRED` for a path when the target declares no `app.url`, `POLICY_DENIED` for a `file:`, `data:`, or `javascript:` URL, `APP_UNREACHABLE`, `ACTION_FAILED`, `UNSUPPORTED_CAPABILITY` without `session.open` on a target with a URL or without `session.restart` on one without (a device launches its pinned app) |
47
+ | `open(path?)` | `config.timeout`, 120000 ms | `APP_URL_REQUIRED` for a path when the target declares no `app.url`, `POLICY_DENIED` for a URL whose scheme is not `http:` or `https:` (`about:blank` aside), `APP_UNREACHABLE`, `ACTION_FAILED`, `UNSUPPORTED_CAPABILITY` without `session.open` on a target with a URL or without `session.restart` on one without (a device launches its pinned app) |
47
48
  | `restart()` | `config.timeout`, 120000 ms | `ACTION_FAILED`, `ENGINE_FAILURE`, `UNSUPPORTED_CAPABILITY` without `session.restart` |
48
49
  | `clearState()` | `config.timeout`, 120000 ms | `ACTION_FAILED`, `ENGINE_FAILURE`, `UNSUPPORTED_CAPABILITY` without `session.reset` |
49
50
  | `back()` | `config.actionTimeout`, 30000 ms | `APP_NOT_OPEN`, `ACTION_FAILED`, `UNSUPPORTED_CAPABILITY` without `session.back` |
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  title: CLI
3
3
  description: Commands, flags, output formats, and exit codes.
4
+ icon: "terminal"
4
5
  ---
5
6
 
6
7
  <CodeGroup>
@@ -56,7 +57,7 @@ bunx e2e --version
56
57
  ```
57
58
  </CodeGroup>
58
59
 
59
- The CLI needs Node.js 22.12 or newer.
60
+ The CLI needs Node.js 24.8 or newer, or 22.22.3 or newer on Node.js 22. Any package manager installs it (npm, pnpm, Yarn with or without Plug'n'Play, Bun); the CLI itself runs on Node.js, not on the Bun or Deno runtimes (`bunx e2e` runs it on Node.js). Under Yarn Plug'n'Play on Node.js 24.8 through 24.11.0, a `.cts` file fails to load (Node.js rejects the source Yarn's loader hands on); 24.11.1 and later load it.
60
61
 
61
62
  ## Help and version
62
63
 
@@ -96,9 +97,9 @@ For a new project, the wizard asks you to choose:
96
97
  **Mobile (iOS/Android)** adds `@e2e-dev/mobile`, defaults to iOS on
97
98
  macOS and Android elsewhere, and opens Settings. **None** adds nothing.
98
99
  - A model provider or subscription for agent steps. Choose Vercel AI Gateway,
99
- OpenRouter, an OpenAI-compatible endpoint, ChatGPT, GitHub Copilot, or
100
- SuperGrok. Choose None for tests that only use locators. The wizard adds
101
- the required packages and model config. Subscription users then sign in
100
+ OpenRouter, an OpenAI-compatible endpoint, ChatGPT, GitHub Copilot,
101
+ OpenCode Console, or SuperGrok. Choose None for tests that only use
102
+ locators. The wizard adds the required packages and model config. Subscription users then sign in
102
103
  with `e2e login`. See [Models](/models) and [Subscriptions](/subscriptions).
103
104
  - Where to install the agent skill (see [e2e guide](#e2e-guide)):
104
105
  `.agents/skills/` for Codex, Cursor, Copilot, Gemini CLI, OpenCode, and
@@ -140,7 +141,10 @@ bunx e2e init --yes
140
141
  ```
141
142
  </CodeGroup>
142
143
 
143
- Re-running `init` keeps an existing config and adds no packages for it. Once
144
+ Re-running `init` keeps an existing config and adds no packages for it. MCP
145
+ registration keeps extra settings on the e2e server, such as environment
146
+ variables and client timeouts, even when repairing its launch command. A
147
+ current registration is left byte-for-byte unchanged. Once
144
148
  a skill directory exists, later runs refresh copies that differ from the
145
149
  installed version and never add a directory the project did not choose. A
146
150
  `.claude/skills/e2e/` copy from an earlier version becomes the symlink when
@@ -164,6 +168,7 @@ npx e2e login openai # ChatGPT, browser sign-in
164
168
  npx e2e login openai --device # sign in with a device code
165
169
  npx e2e login github-copilot --from-gh # use your GitHub CLI login
166
170
  npx e2e login github-copilot --client-id <id> [--enterprise-url <host>]
171
+ npx e2e login opencode-console # OpenCode Console, device code
167
172
  npx e2e login spacexai # SuperGrok, device code
168
173
  npx e2e login # choose a provider
169
174
  ```
@@ -173,6 +178,7 @@ pnpm exec e2e login openai # ChatGPT, browser sign-in
173
178
  pnpm exec e2e login openai --device # sign in with a device code
174
179
  pnpm exec e2e login github-copilot --from-gh # use your GitHub CLI login
175
180
  pnpm exec e2e login github-copilot --client-id <id> [--enterprise-url <host>]
181
+ pnpm exec e2e login opencode-console # OpenCode Console, device code
176
182
  pnpm exec e2e login spacexai # SuperGrok, device code
177
183
  pnpm exec e2e login # choose a provider
178
184
  ```
@@ -182,6 +188,7 @@ bunx e2e login openai # ChatGPT, browser sign-in
182
188
  bunx e2e login openai --device # sign in with a device code
183
189
  bunx e2e login github-copilot --from-gh # use your GitHub CLI login
184
190
  bunx e2e login github-copilot --client-id <id> [--enterprise-url <host>]
191
+ bunx e2e login opencode-console # OpenCode Console, device code
185
192
  bunx e2e login spacexai # SuperGrok, device code
186
193
  bunx e2e login # choose a provider
187
194
  ```
@@ -194,7 +201,7 @@ configuration.
194
201
 
195
202
  | Argument or flag | Type | Default | Behavior |
196
203
  | --- | --- | --- | --- |
197
- | `[provider]` | `openai`, `github-copilot`, or `spacexai` | a picker | The subscription to sign in to |
204
+ | `[provider]` | `openai`, `github-copilot`, `opencode-console`, or `spacexai` | a picker | The subscription to sign in to |
198
205
  | `--device` | boolean | `false` | ChatGPT: show a code to enter on another device instead of opening a browser |
199
206
  | `--from-gh` | boolean | `false` | GitHub Copilot: reuse the token of the signed-in GitHub CLI |
200
207
  | `--client-id <id>` | string | none | GitHub Copilot: the client id of your GitHub OAuth App with the device flow enabled |
@@ -239,10 +246,11 @@ bunx e2e models openai # one provider
239
246
  </CodeGroup>
240
247
 
241
248
  Asks the vendor which models the stored login serves and prints one line per
242
- model: the id `chatgpt()`, `copilot()`, or `grok()` takes, the vendor's name
243
- for it, and its notes (reasoning levels, vision, preview, hidden, and for
244
- Copilot `not enabled` or `no chat completions` when `copilot()` cannot call
245
- it). Without a
249
+ model: the id `chatgpt()`, `copilot()`, `opencodeConsole()`, or `grok()`
250
+ takes, the vendor's name for it, and its notes (reasoning levels, vision,
251
+ preview, hidden, for Copilot `not enabled` or `no chat or responses` when
252
+ `copilot()` cannot call it, and for OpenCode Console the plan, Zen or Go,
253
+ that serves it). Without a
246
254
  stored login, or when the named provider has none, exits 1 with the sign-in
247
255
  command; when one of several logins fails to list, the others still print and
248
256
  the command exits 1. An unknown provider exits 2. See
@@ -277,15 +285,15 @@ See [Coding agents](/coding-agents).
277
285
 
278
286
  <CodeGroup>
279
287
  ```bash npm
280
- npx e2e mcp [--config <path>] [--target <name>] [--headless] [--max-sessions <n>]
288
+ npx e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
281
289
  ```
282
290
 
283
291
  ```bash pnpm
284
- pnpm exec e2e mcp [--config <path>] [--target <name>] [--headless] [--max-sessions <n>]
292
+ pnpm exec e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
285
293
  ```
286
294
 
287
295
  ```bash bun
288
- bunx e2e mcp [--config <path>] [--target <name>] [--headless] [--max-sessions <n>]
296
+ bunx e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
289
297
  ```
290
298
  </CodeGroup>
291
299
 
@@ -342,6 +350,8 @@ A positional starting with `-` that names no file is a usage error, exit 2.
342
350
  This is what `pnpm test:e2e -- --headed` produces: the `--` reaches e2e and
343
351
  `--headed` would otherwise be taken for a file. The message names the direct
344
352
  form, `pnpm exec e2e run --headed`.
353
+ For npm, the direct form is `npm exec -- e2e run --headed`; npm needs the
354
+ separator before the binary to pass its flags through.
345
355
 
346
356
  When nothing is left to run, `NO_TESTS` names the cause: no file matched the
347
357
  globs (with look-alike files such as `tests/login.test.ts`), a positional
@@ -359,7 +369,7 @@ or every test was filtered out.
359
369
  | `--exclude-tag <tags>` | comma-separated or repeated | none | Leaves out every test carrying any of the tags, whatever `--tag` or a positional selected. A serial group still runs whole when any member is selected; tag the serial `describe` to exclude the flow. |
360
370
  | `--grep <pattern>` | regular expression, repeatable | none | Only tests whose title matches one of the patterns. The text matched is the describe titles and the test title joined by one space (`checkout pays`); the file and the tags are not part of it. A bare pattern is case-sensitive; `'/pattern/i'` adds flags. A pattern that does not compile is a usage error, exit 2. |
361
371
  | `--grep-invert <pattern>` | regular expression, repeatable | none | Leaves out every test whose title matches one of the patterns. Same text and pattern forms as `--grep`; both together keep the tests `--grep` matches minus the ones `--grep-invert` matches. |
362
- | `--last-failed` | boolean | `false` | Only the tests the previous run did not pass, read from the `report.json` this run overwrites, `<output>/report.json` (`--output`, else the config's [`output`](/reference/config#output)): failed, timed out, interrupted, or skipped because a setup, a serial predecessor, a hook, or the worker failed, and every test it selected in the describe or file, on that target, of a `beforeAll` or `afterAll` that failed (for a setup, the tests that consume its session). A rerun that runs the hook again reports it again if it still fails; another filter that leaves its tests out leaves the failure out of the next report too. A test is named per target and agent. No report is `NO_LAST_RUN`; a previous run with nothing failed selects nothing, `NO_TESTS` unless `--pass-with-no-tests`. |
372
+ | `--last-failed` | boolean | `false` | Only the tests the previous run did not pass, read from the `report.json` this run overwrites, `<output>/report.json` (`--output`, else the config's [`output`](/reference/config#output)): failed, timed out, interrupted, or skipped because a setup, a serial predecessor, a hook, or the worker failed or `--max-failures` stopped the run first, and every test it selected in the describe or file, on that target, of a `beforeAll` or `afterAll` that failed (for a setup, the tests that consume its session). A rerun that runs the hook again reports it again if it still fails, and a rerun that runs any test of its scope without it failing resolves it. A test another filter (`--grep`, `--tag`, `--target`, `--agent`, a positional) leaves out of the rerun stays owed: the rerun's report carries its last result, and the hook failure whose scope it is in, under `run.carried`, the next `--last-failed` selects it again, and the [`@e2e-dev/github`](/github) page stays red until it passes (⏹️ for a test the run before interrupted). The rerun's own exit code and summary count only what it ran. It keeps the artifacts the previous report names and writes its own under `artifacts/rerun-<n>/`, so no attempt overwrites an earlier one's files. A test is named per target and agent. No report is `NO_LAST_RUN`; a previous run with nothing failed selects nothing, `NO_TESTS` unless `--pass-with-no-tests`. |
363
373
  | `--shard <index/total>` | `2/3` | none | Runs one contiguous slice of the selected tests, cut once every other filter applied. Slices differ in size by at most one test, a serial group counts as one and is never split, and each shard brings only the setup tests its own tests need. Every shard runs the same command with a different index; the order is the report order, so the same suite shards the same way on every machine. A shard that gets no test is `NO_TESTS` unless `--pass-with-no-tests`. See [Sharding](/ci#sharding). |
364
374
  | `--headed` | boolean | `false` | Visible UI when the engine supports it. |
365
375
  | `--agent <names>` | comma-separated or repeated | `default` | Runs unpinned tests once per selected agent. For a test with `{ agent }`, selects the shared names when there is overlap; otherwise keeps the test's selection. An unknown name is `INVALID_CONFIG`. |
@@ -368,7 +378,7 @@ or every test was filtered out.
368
378
  | `--repeat-each <n>` | integer, at least 1 | `1` | Runs every selected test this many times. Each run is a result of its own, with `repeat` 0 through `n - 1` and its own `id`; the list reporter titles the later runs `(repeat #1)`, `(repeat #2)`, and their artifacts go under a `repeat-<n>` directory. Setup tests run once. With the replay cache on, the first run records and the later ones replay, so pair it with `--no-cache` to exercise the model each time. The `list` summary adds a `Repeats` row (`1 of 2 tests passed all 5 runs`) and one line per test that did not pass every run, naming each run that failed; the `markdown` page carries the same tally. |
369
379
  | `--max-failures <n>` | integer, at least 1 | none | Stops the run once this many tests have failed or timed out. Nothing more is dispatched, the tests running on other workers end as `interrupted`, and the tests not started, the rest of the file whose test reached the limit included, are skipped with cause `failure-limit` and the reason `run stopped after 3 failures (--max-failures 3)`. The exit code is the failures' own, 1, not 130. |
370
380
  | `--reporter <ids>` | comma-separated list | config | Replaces the built-in IDs in `config.reporters`: `list`, `json`, `junit`, `markdown`. `json` with `list` is `INVALID_CONFIG`. [Reporter objects](/reference/reporters) in the config keep running. |
371
- | `--output <dir>` | string | config, else `.e2e` | The results directory for this run, over the config's [`output`](/reference/config#output): the report, the reporter files, the AI trace, `artifacts/` (cleared once the run's tests start), and `sessions/`. Resolved from the project root; one the run cannot own is `INVALID_CONFIG`. The removed `--artifacts <dir>` fails with the `--output` it maps to: `--artifacts out/artifacts` is `--output out`, and a directory whose parent is the project root or outside it maps to `--output .e2e`. |
381
+ | `--output <dir>` | string | config, else `.e2e` | The results directory for this run, over the config's [`output`](/reference/config#output): the report, the reporter files, the AI trace, `artifacts/` (cleared once the run's tests start, down to what the previous report names on a `--last-failed` rerun), and `sessions/`. Resolved from the project root; one the run cannot own is `INVALID_CONFIG`. The removed `--artifacts <dir>` fails with the `--output` it maps to: `--artifacts out/artifacts` is `--output out`, and a directory whose parent is the project root or outside it maps to `--output .e2e`. |
372
382
  | `--no-cache` | boolean | `false` | Replay cache off for this run, whatever `config.cache` says. |
373
383
  | `--strict-cache` | boolean | `false` | Turns on `cache.strict` for this run: a step whose recording exists but no longer replays fails with `REPLAY_STALE` instead of handing off to the agent. See [Fail on a stale recording](/cache#fail-on-a-stale-recording). |
374
384
  | `--pass-with-no-tests` | boolean | `false` | Exit 0 instead of `NO_TESTS` when nothing is selected. |
@@ -479,8 +489,8 @@ bunx e2e run --reporter list,junit
479
489
  | --- | --- |
480
490
  | `list` | Terminal progress, failures with code frames, and a run summary. On a TTY, live progress includes the current agent step, model turns, tool calls, and reasoning excerpts. Untrusted fields are capped at 8 KiB and stripped of control characters. |
481
491
  | `json` | The report document to stdout and nothing else there, so `e2e run --reporter json \| jq` works. Same document as `<output>/report.json`, described by `schema/report-v1.schema.json` in the package. The shape changes only with the `schemaVersion` field (`report-1` today). Cannot be combined with `list`. |
482
- | `junit` | `junit.xml` beside the report. One `<testsuite>` per file, one `<testcase>` per test-target pair named `title > path [target]`. A test-class error is a `<failure>`, any other an `<error>`, a skip a `<skipped>` with its reason. Run-level errors form a `run` suite. |
483
- | `markdown` | `summary.md` beside the report with counts, cost, errors, and failure details. Failed and flaky tests also get a page under `failures/` there, with steps, model turns, and the screen at failure. Exploration runs show findings. [`@e2e-dev/github`](/github) uses this summary for pull request comments. |
492
+ | `junit` | `junit.xml` beside the report. One `<testsuite>` per file, one `<testcase>` per test-target pair named `title > path [target]`. A test-class error is a `<failure>`, any other an `<error>`, a skip a `<skipped>` with its reason. An interrupted test reached no verdict, so it is a `<skipped>` whose message starts `interrupted:`. Run-level errors form a `run` suite. |
493
+ | `markdown` | `summary.md` beside the report with counts, cost, errors, and failure details. Failed and flaky tests also get a page under `failures/` there, with steps, model turns, and the screen at failure. Interrupted tests are counted on their own and get no page. Exploration runs show findings. [`@e2e-dev/github`](/github) uses this summary for pull request comments. |
484
494
 
485
495
  Every run that gets to its tests writes `report.json` under the [output directory](/reference/config#output)
486
496
  (`.e2e` by default) whatever the reporters are, and the reporters' files
@@ -490,6 +500,16 @@ fails to start, an interrupt) writes none of them and leaves the previous
490
500
  run's files in place; its errors are on the terminal and in the exit code. A `junit.xml` or `summary.md` that could not be written is a
491
501
  line on stderr, never a run error.
492
502
 
503
+ `run.summary` in the report counts the results. `discovered` is every
504
+ test-target pair the runner collected and `selected` the ones this run
505
+ chose; the selection's left-out pairs are in `results` as `selected: false`
506
+ but in no other count. `passed`, `failed` (timed out included),
507
+ `interrupted`, `flaky`, and `skipped` count the selected results and add up
508
+ to `selected`. `interrupted` is a test the run stopped before its verdict
509
+ (a signal, `--max-failures`, or a run-level error such as
510
+ `MODEL_UNAVAILABLE`), never a failure. `executed` is the selected
511
+ results that ran at least one attempt.
512
+
493
513
  With `--ai-trace`, the run also writes `<output>/ai-trace.json`: one entry per
494
514
  model round trip with the exact prompt, tool definitions, response, usage,
495
515
  and provider metadata. Observations in it are already redacted. Inline image
@@ -510,7 +530,10 @@ Signals escalate.
510
530
  any test starts (during collection, an engine's `prepare`, or app
511
531
  startup) writes no report and leaves the previous one in place.
512
532
  2. A second signal forces. Workers dispose their engines at once and are
513
- killed when the cleanup budget is spent. The report is still written.
533
+ killed when the cleanup budget is spent. The report is still written,
534
+ and so are `junit.xml` and `summary.md` when the `junit` and
535
+ `markdown` reporters are on; a custom reporter's
536
+ `onRunFinished` is abandoned.
514
537
  3. A third signal exits on the spot, after killing the process groups of every
515
538
  app command the run started. A process attached through
516
539
  `reuseExisting` is left alone.
@@ -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 the TypeScript they import: a helper by path, or a
73
- workspace package that exports its `.ts` source by name. An installed package
74
- imported by name keeps its own format. A config or test written with `require`
75
- or `module.exports` fails to load.
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
 
@@ -96,6 +132,8 @@ export interface E2EConfig {
96
132
  cleanupTimeout?: number;
97
133
  /** Retries per test, 0 through 10; default 1 in CI, else 0. */
98
134
  retries?: number;
135
+ /** Fail the run when a test skips itself after a soft failure or an earlier failed attempt; default false. */
136
+ failOnSkippedFailure?: boolean;
99
137
  /** Parallel workers, 1 through 1024; default 1 in CI, else half the cores. An engine may cap it lower. */
100
138
  workers?: number;
101
139
  /** `{ store }` hands every artifact to a host store as it is produced. */
@@ -158,6 +196,17 @@ Every numeric value must be a safe integer. `projectId` falls back to
158
196
  `package.json`. `workers` never exceeds what a target's engine declares. A
159
197
  targeted agent action is bounded by the smaller of `actionTimeout` and 15 s.
160
198
 
199
+ ## failOnSkippedFailure
200
+
201
+ Set `failOnSkippedFailure: true` to fail the run with exit code 1 when a test
202
+ skips after a soft assertion failure or skips a retry after an earlier failed
203
+ attempt. The default is `false`.
204
+
205
+ The test keeps its `skipped` status and reason. Its failures remain in the
206
+ report, and the CLI shows them under `Skipped After Failure` with either
207
+ setting. Clean skips, successful retries, and engine cleanup diagnostics do
208
+ not trigger this policy. A skip still stops the body and does not retry.
209
+
161
210
  ## targets
162
211
 
163
212
  ```ts
@@ -267,8 +316,9 @@ it (no `app.command`) is `INVALID_CONFIG`. An `app.command` with neither
267
316
 
268
317
  ### The app's site
269
318
 
270
- A test or the agent may open any http(s) URL; `file:`, `data:`, and
271
- `javascript:` are `POLICY_DENIED`. The site of `url`, its registrable domain
319
+ A test or the agent may open any http(s) URL, and `about:blank`; every
320
+ other scheme (`file:`, `data:`, `javascript:`, `view-source:`, `blob:`) is
321
+ `POLICY_DENIED`. The site of `url`, its registrable domain
272
322
  (`app.example.com` and `auth.example.com` are one site), scopes two things:
273
323
  the browser engine's [`headers`](/reference/web#options) reach the
274
324
  site and no other host, and child frames off the site stay out of
@@ -369,7 +419,7 @@ run.
369
419
  | `report.json` | The canonical report; `--last-failed` reads the one here |
370
420
  | `junit.xml`, `summary.md`, `failures/` | The `junit` and `markdown` reporters' files |
371
421
  | `ai-trace.json` | `--ai-trace` |
372
- | `artifacts/` | Screenshots, traces, videos, downloads, and logs, one directory per attempt. Cleared once a run's tests start, so it holds the latest run's evidence and nothing its report no longer names |
422
+ | `artifacts/` | Screenshots, traces, videos, downloads, and logs, one directory per attempt. Cleared once a run's tests start, so it holds the latest run's evidence and nothing its report no longer names. A `--last-failed` rerun keeps the files the report it reruns names and writes its own attempts under `artifacts/rerun-<n>/` |
373
423
  | `sessions/` | The run's encrypted `test.setup` sessions, deleted when it ends |
374
424
  | `videos/<session>/` | [`e2e mcp`](/reference/mcp#recording) session recordings |
375
425
 
@@ -754,8 +804,9 @@ export interface ModelInstance {
754
804
  }
755
805
  ```
756
806
 
757
- Construct the model in your config with an AI SDK provider. For subscription
758
- sign-in, use the [`e2e/oauth/*` models](/subscriptions).
807
+ Construct the model in your config with an AI SDK provider. You can use all
808
+ providers that support tool calls, see [AI SDK providers](/models#ai-sdk-providers).
809
+ For subscription sign-in, use the [`e2e/oauth/*` models](/subscriptions).
759
810
 
760
811
  | Gateway | Constructor | Package | Reads |
761
812
  | --- | --- | --- | --- |
@@ -798,7 +849,9 @@ Credentials and secrets are separate namespaces: `secrets.get(name)` returns a
798
849
  reports know that handle as `<name>.password` (`<secret:admin.password>`), so
799
850
  `credentials.admin` and `secrets.admin` can both exist; a `secrets` entry
800
851
  named exactly `admin.password` beside an `admin` credential is
801
- `INVALID_CONFIG`.
852
+ `INVALID_CONFIG`. So are two credentials whose names map to one
853
+ `E2E_USER_<NAME>_*` override, like `svc-a` and `svc_a` (see
854
+ [Environment variables](/reference/environment)).
802
855
 
803
856
  ```ts
804
857
  /** Named test identity. The password remains an opaque Secret. */
@@ -824,7 +877,8 @@ export type SecretConfig = string | SecretProvider;
824
877
 
825
878
  An entry is a static value, or a provider called on every authorized fill (a
826
879
  vault lookup, a freshly computed TOTP). It is recorded in the config digest by
827
- name only, and an `E2E_SECRET_<NAME>` override wins over both. Every entry
880
+ name only, and an `E2E_SECRET_<NAME>` override wins over both. Two entries whose
881
+ names map to one variable, like `api-key` and `api_key`, are `INVALID_CONFIG`. Every entry
828
882
  must resolve to a string of at least 6 characters (code points) or a
829
883
  function; anything else is `INVALID_CONFIG`. The minimum exists for redaction: every occurrence
830
884
  of a value becomes `<secret:name>`, and a shorter value would take ordinary
@@ -877,7 +931,7 @@ See [Secrets that are not passwords](/authentication#secrets-that-are-not-passwo
877
931
  In `read-write` mode the replay cache records an `agent.act()` only after a
878
932
  later verification passes, such as a locator assertion or `agent.assert()`.
879
933
  `read-only` replays existing entries and writes none; `off` disables both.
880
- See [Caching agent steps](/cache).
934
+ See [Caching](/cache).
881
935
 
882
936
  ```ts
883
937
  /**
@@ -932,7 +986,7 @@ instead of a hand-off to the agent; see
932
986
  `e2e init` adds `.e2e/cache/` to `.gitignore`, so committing entries is
933
987
  opt-in. Remove that line to share replays with CI and teammates; CI stays
934
988
  `read-only` unless `cache: 'read-write'` is set. See
935
- [Caching agent steps](/cache#commit-the-replay-cache).
989
+ [Caching](/cache#commit-the-replay-cache).
936
990
 
937
991
  ## Run limits
938
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
- | { readonly kind: 'swipeTo'; readonly target: ViewportPoint };
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
@@ -960,8 +972,8 @@ Use these helpers to keep behavior consistent across engines:
960
972
  | --- | --- |
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
- | `urlMatches(current, expected, baseHref)` | `toHaveURL`-style comparison; 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 |
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` |
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
- After a secret fill, the runner redacts every text entry in each trace
1155
- archive and drops its screencast frames: every `screencast/` entry, each
1156
- `screencast-frame` record in a `.trace` entry, and any image a record names.
1157
- After `resolveSecret` returns, it redacts the trace the same way and keeps
1158
- the frames: a trace records the options the engine opened the attempt with,
1159
- and an engine-held secret is protected as text only.
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: after a secret fill or
1163
- once `resolveSecret` returns, the runner rewrites a text one (`.txt`,
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
 
@@ -1185,8 +1197,8 @@ export interface EngineFixtureContext {
1185
1197
  /**
1186
1198
  * Resolves a navigation target against the base URL. Throws
1187
1199
  * `APP_URL_REQUIRED` when the target declares no URL and `POLICY_DENIED`
1188
- * for a `file:`, `data:`, or `javascript:` scheme, so a fixture never
1189
- * re-implements the rule the harness owns.
1200
+ * for any scheme but `http:` and `https:` (`about:blank` aside), so a
1201
+ * fixture never re-implements the rule the harness owns.
1190
1202
  */
1191
1203
  resolveUrl(url: string): string;
1192
1204
  };
@@ -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,
@@ -21,6 +22,7 @@ See [Models](/models) and [Subscriptions](/subscriptions).
21
22
  | Variable | Reading | Effect |
22
23
  | --- | --- | --- |
23
24
  | `E2E_OAUTH_CREDENTIALS` | value | The stored subscription logins as JSON, the contents of `oauth.json`, for a machine nobody can sign in on. The `e2e/oauth/*` models and `e2e models` read it in place of `$XDG_CONFIG_HOME/e2e/oauth.json`; `e2e login` and `e2e logout` refuse to change it, and refreshed tokens are not written back |
25
+ | `OPENCODE_API_KEY` | value | An OpenCode Console service account key; `opencodeConsole()` uses it in place of the stored `opencode-console` login; `e2e models` still reads the stored login. OpenCode reads the same variable |
24
26
  | `XDG_CONFIG_HOME` | value | Directory of the stored logins, `$XDG_CONFIG_HOME/e2e/oauth.json`; default `~/.config/e2e/oauth.json` |
25
27
 
26
28
  ## Credentials and secrets
@@ -32,7 +34,13 @@ See [Models](/models) and [Subscriptions](/subscriptions).
32
34
  | `E2E_SECRET_<NAME>` | set | Replaces the value of `secrets.<name>` |
33
35
 
34
36
  `<NAME>` is the name uppercased with every character outside `A-Z0-9`
35
- replaced by `_`, so `power-user` becomes `E2E_USER_POWER_USER_USERNAME`. A
37
+ replaced by `_`, so `power-user` becomes `E2E_USER_POWER_USER_USERNAME`. Two
38
+ credentials, or two secrets, whose names map to the same `<NAME>` (`api-key`
39
+ and `api_key` both read `E2E_SECRET_API_KEY`; `svc-a` and `svc_a` both read
40
+ `E2E_USER_SVC_A_USERNAME` and `_PASSWORD`) are `INVALID_CONFIG` at config
41
+ load, whether or not a variable is set, so one rotation never replaces two
42
+ entries; the message names the entries and the variables. A credential and a
43
+ secret may share a name, since their prefixes differ. A
36
44
  variable for a name absent from the config is ignored. An empty value is
37
45
  `INVALID_CONFIG`, and so is a password or secret shorter than 6 characters
38
46
  (code points): redaction rewrites every occurrence of a value, and one that
@@ -50,7 +58,6 @@ short would take ordinary text with it.
50
58
  | --- | --- | --- |
51
59
  | `NO_COLOR` | set | Plain CLI output. Colors are otherwise on for a TTY or in CI and off when piped |
52
60
  | `FORCE_COLOR` | set | Colored CLI output even when piped |
53
- | `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 |
54
61
  | `TERM` | value | `dumb` draws the `e2e init` wordmark at rest, without animation, as in CI |
55
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` |
56
63
 
@@ -100,6 +107,7 @@ What is sent and how to read it is on the [Telemetry](/telemetry) page.
100
107
  | Variable | Reading | Effect |
101
108
  | --- | --- | --- |
102
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 |
103
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) |
104
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) |
105
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 |