@e2edev/e2e 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/agent/act.js +4 -1
  2. package/dist/agent/act.js.map +1 -1
  3. package/dist/agent/action-dispatcher.d.ts +3 -3
  4. package/dist/agent/action-dispatcher.js +3 -3
  5. package/dist/agent/error.d.ts.map +1 -1
  6. package/dist/agent/error.js +1 -0
  7. package/dist/agent/error.js.map +1 -1
  8. package/dist/agent/executor.d.ts +3 -3
  9. package/dist/agent/phases.js +1 -1
  10. package/dist/agent/phases.js.map +1 -1
  11. package/dist/agent/primitives.d.ts.map +1 -1
  12. package/dist/agent/primitives.js +3 -2
  13. package/dist/agent/primitives.js.map +1 -1
  14. package/dist/agent/secrets.d.ts.map +1 -1
  15. package/dist/agent/secrets.js +13 -9
  16. package/dist/agent/secrets.js.map +1 -1
  17. package/dist/agent/step-accounting.d.ts +12 -1
  18. package/dist/agent/step-accounting.d.ts.map +1 -1
  19. package/dist/agent/step-accounting.js +18 -3
  20. package/dist/agent/step-accounting.js.map +1 -1
  21. package/dist/cli/index.d.ts.map +1 -1
  22. package/dist/cli/index.js +5 -8
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/cli/init/engine-versions.json +1 -1
  25. package/dist/cli/init/engines.d.ts +19 -9
  26. package/dist/cli/init/engines.d.ts.map +1 -1
  27. package/dist/cli/init/engines.js +24 -11
  28. package/dist/cli/init/engines.js.map +1 -1
  29. package/dist/cli/init/scaffold.d.ts.map +1 -1
  30. package/dist/cli/init/scaffold.js +1 -3
  31. package/dist/cli/init/scaffold.js.map +1 -1
  32. package/dist/cli/init.d.ts.map +1 -1
  33. package/dist/cli/init.js +1 -0
  34. package/dist/cli/init.js.map +1 -1
  35. package/dist/config/diagnose.d.ts.map +1 -1
  36. package/dist/config/diagnose.js +2 -0
  37. package/dist/config/diagnose.js.map +1 -1
  38. package/dist/config/resolve.d.ts +11 -2
  39. package/dist/config/resolve.d.ts.map +1 -1
  40. package/dist/config/resolve.js +43 -18
  41. package/dist/config/resolve.js.map +1 -1
  42. package/dist/config/secrets.d.ts +89 -0
  43. package/dist/config/secrets.d.ts.map +1 -0
  44. package/dist/config/secrets.js +67 -0
  45. package/dist/config/secrets.js.map +1 -0
  46. package/dist/engine/contract.d.ts +8 -0
  47. package/dist/engine/contract.d.ts.map +1 -1
  48. package/dist/engine/contract.js.map +1 -1
  49. package/dist/explore/body.js +1 -1
  50. package/dist/explore/body.js.map +1 -1
  51. package/dist/index.d.ts +3 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +4 -1
  54. package/dist/index.js.map +1 -1
  55. package/dist/locator/engine.d.ts.map +1 -1
  56. package/dist/locator/engine.js +1 -0
  57. package/dist/locator/engine.js.map +1 -1
  58. package/dist/mcp/session.d.ts +1 -1
  59. package/dist/mcp/session.d.ts.map +1 -1
  60. package/dist/mcp/session.js +8 -4
  61. package/dist/mcp/session.js.map +1 -1
  62. package/dist/report/builtin.d.ts +14 -0
  63. package/dist/report/builtin.d.ts.map +1 -0
  64. package/dist/report/builtin.js +22 -0
  65. package/dist/report/builtin.js.map +1 -0
  66. package/dist/report/explore-text.d.ts +17 -0
  67. package/dist/report/explore-text.d.ts.map +1 -0
  68. package/dist/report/explore-text.js +37 -0
  69. package/dist/report/explore-text.js.map +1 -0
  70. package/dist/report/format.d.ts +2 -0
  71. package/dist/report/format.d.ts.map +1 -1
  72. package/dist/report/format.js +1 -1
  73. package/dist/report/format.js.map +1 -1
  74. package/dist/report/junit.d.ts +2 -4
  75. package/dist/report/junit.d.ts.map +1 -1
  76. package/dist/report/junit.js +7 -40
  77. package/dist/report/junit.js.map +1 -1
  78. package/dist/report/list-explore.d.ts +1 -1
  79. package/dist/report/list-explore.d.ts.map +1 -1
  80. package/dist/report/list-explore.js +5 -25
  81. package/dist/report/list-explore.js.map +1 -1
  82. package/dist/report/markdown.d.ts +39 -0
  83. package/dist/report/markdown.d.ts.map +1 -0
  84. package/dist/report/markdown.js +426 -0
  85. package/dist/report/markdown.js.map +1 -0
  86. package/dist/report/outcome.d.ts +29 -0
  87. package/dist/report/outcome.d.ts.map +1 -0
  88. package/dist/report/outcome.js +36 -0
  89. package/dist/report/outcome.js.map +1 -0
  90. package/dist/report/write.d.ts +10 -1
  91. package/dist/report/write.d.ts.map +1 -1
  92. package/dist/report/write.js +22 -1
  93. package/dist/report/write.js.map +1 -1
  94. package/dist/run/execute.js +1 -1
  95. package/dist/run/execute.js.map +1 -1
  96. package/dist/run/fixtures.d.ts.map +1 -1
  97. package/dist/run/fixtures.js +22 -7
  98. package/dist/run/fixtures.js.map +1 -1
  99. package/dist/run/runner.d.ts.map +1 -1
  100. package/dist/run/runner.js +5 -7
  101. package/dist/run/runner.js.map +1 -1
  102. package/dist/run/secrecy.d.ts +3 -3
  103. package/dist/run/secrecy.d.ts.map +1 -1
  104. package/dist/run/secrecy.js +4 -4
  105. package/dist/run/secrecy.js.map +1 -1
  106. package/dist/run/standalone.js +6 -6
  107. package/dist/run/standalone.js.map +1 -1
  108. package/dist/run/worker/entry.js +2 -2
  109. package/dist/run/worker/entry.js.map +1 -1
  110. package/dist/secrets.d.ts +21 -0
  111. package/dist/secrets.d.ts.map +1 -0
  112. package/dist/secrets.js +66 -0
  113. package/dist/secrets.js.map +1 -0
  114. package/dist/types.d.ts +21 -37
  115. package/dist/types.d.ts.map +1 -1
  116. package/package.json +2 -2
  117. package/skills/e2e/SKILL.md +39 -21
  118. package/skills/e2e/references/agent.md +55 -14
  119. package/skills/e2e/references/debugging.md +2 -1
  120. package/skills/e2e/references/mcp.md +1 -1
  121. package/skills/e2e/references/running.md +11 -4
  122. package/skills/e2e/references/setup.md +17 -4
  123. package/skills/e2e/references/writing-tests.md +20 -11
  124. package/dist/credentials.d.ts +0 -13
  125. package/dist/credentials.d.ts.map +0 -1
  126. package/dist/credentials.js +0 -51
  127. package/dist/credentials.js.map +0 -1
@@ -14,6 +14,10 @@
14
14
  (npm's `ERESOLVE`); upgrade `playwright` within the range.
15
15
  Missing browsers download when the engine first boots. In CI install them
16
16
  up front: `npx playwright install chromium --with-deps`.
17
+ - For mobile tests, `@e2edev/agent-device` plus `agent-device` (`0.21.x`),
18
+ likewise a peer dependency: a project that already drives devices with the
19
+ agent-device CLI keeps its version. The range pins the tested minor because
20
+ agent-device minors break; it moves with each engine release.
17
21
 
18
22
  ## Scaffold
19
23
 
@@ -73,7 +77,7 @@ export default {
73
77
  }),
74
78
  },
75
79
  ],
76
- // Only for agent.* steps. The model is an AI SDK instance; gateway() from 'ai' reads AI_GATEWAY_API_KEY.
80
+ // The model behind every agent.* step: an AI SDK instance; gateway() from 'ai' reads AI_GATEWAY_API_KEY.
77
81
  agents: {
78
82
  default: createAgent({
79
83
  model: gateway('openai/gpt-5.6-luna'),
@@ -95,10 +99,11 @@ export default {
95
99
  | `assertionTimeout` | `5000` | `expect` polling window. |
96
100
  | `retries` | `0`, `1` in CI | 0 to 10. |
97
101
  | `workers` | half the cores, `1` in CI | Test files run in parallel across workers, at most the `workers` the engine declares per target (a device target: one per device). |
98
- | `reporters` | `['list']` | `list`, `json`, `junit`, and reporter objects (`{ name, onEvent?, onRunFinished? }`) that receive the finished run. `json` excludes `list`; `--reporter` keeps the objects. |
102
+ | `reporters` | `['list']` | `list`, `json`, `junit`, `markdown`, and reporter objects (`{ name, onEvent?, onRunFinished? }`) that receive the finished run. `json` excludes `list`; `--reporter` keeps the objects. |
99
103
  | `cache` | `'read-write'`, `'read-only'` in CI | The trace cache for `agent.act`; `'off'` disables it. |
100
104
  | `agents` | `{ default: built-in }` | Agents by name. `default` is what tests run with; `e2e run --agent <name>` runs with another. Each entry is `createAgent(...)`, an options block `{ model, context, maxSteps, maxModelCalls, vision, providerOptions }`, or a custom `StepExecutor`. `model` is an AI SDK instance; without one anywhere, acquiring `agent` is `MODEL_UNAVAILABLE`. |
101
105
  | `credentials` | `{}` | Named `{ username, password, allowedOrigins? }` entries; `password` may be a function returning the value. |
106
+ | `secrets` | `{}` | Named values the model never sees (API keys, tokens): a string, a function returning the value, or `{ value, allowedOrigins? }`. A name cannot also be a credential. |
102
107
  | `screen.testIdAttribute` | `'data-testid'` | Attribute read by `getByTestId`. |
103
108
  | `artifacts` | `['screenshot', 'trace']` | Kinds to keep (`screenshot`, `trace`, and the opt-in `video`), or `{ kinds, store, video }`; `video: { retain: 'on-failure' }` keeps only the recordings of attempts that did not pass. |
104
109
  | `projectId` | the package name | Report and cache identity. |
@@ -206,6 +211,7 @@ or read the address from the environment:
206
211
  | --- | --- |
207
212
  | `AI_GATEWAY_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, ... | Read by the provider package the config constructs the model with (`gateway()` from `ai`, `openrouter()`, `openai()`), not by the runner. The runner reads no model variable; the model is always an AI SDK instance in the config. |
208
213
  | `E2E_USER_<NAME>_USERNAME`, `E2E_USER_<NAME>_PASSWORD` | Override `credentials.<name>`. `<NAME>` is the credential name uppercased, other characters as `_`. |
214
+ | `E2E_SECRET_<NAME>` | Overrides the value of `secrets.<name>`, same uppercasing rule. |
209
215
  | `CI` | Turns on CI defaults: `retries: 1`, `workers: 1`, `test.only` rejected, cache read-only, `reuseExisting` ignored. |
210
216
  | `E2E_TELEMETRY_DISABLED`, `DO_NOT_TRACK` | Turn anonymous usage telemetry off; `e2e telemetry disable` saves the same choice. `E2E_TELEMETRY_DEBUG=1` prints each event instead of sending it. |
211
217
 
@@ -240,6 +246,11 @@ export default {
240
246
  single `device` runs one worker whatever `workers` says; a list,
241
247
  `device: ['iPhone 17', 'iPhone 17 Pro']`, is an explicit pool. Devices boot
242
248
  in `prepare`, before the run's clock starts.
249
+ - A test's steps never wait for the screen to settle; `expect` verifies the
250
+ outcome. Only a control that appeared or moved with the previous action
251
+ waits out the `transition` budget (default 500 ms) before it is acted on.
252
+ Agent actions settle for `settle` ms (default 150) before the agent
253
+ observes again; `settle: false` skips that wait.
243
254
  - Cancelled device commands keep running; the next attempt waits for them.
244
255
  Raw screenshot files are removed when capture finishes, including after
245
256
  cancellation.
@@ -260,5 +271,7 @@ export default {
260
271
  - `npx e2e run tests/example.e2e.ts` passes against the app.
261
272
  - `package.json` has a script such as `"test:e2e": "e2e run"`.
262
273
  - `.gitignore` lists the `.e2e/` outputs (init adds them). Committing
263
- `.e2e/cache/` is opt-in: remove that line to share `agent.act` replays.
264
- - CI runs the deterministic suite on pull requests; see `running`.
274
+ `.e2e/cache/` is opt-in: remove that line so CI and teammates replay
275
+ `agent.act` steps instead of re-running the model.
276
+ - CI runs the whole suite on pull requests, agent steps included; see
277
+ `running`.
@@ -12,28 +12,31 @@ test.describe('todos', { tags: ['todos'] }, () => {
12
12
  await app.open('/todos');
13
13
  });
14
14
 
15
- test('adds and completes a todo', async ({ screen, web }) => {
16
- await screen.getByLabel('New todo').fill('Write the release notes');
17
- await screen.getByRole('button', { name: 'Add' }).tap();
18
-
15
+ test('adds and completes a todo', async ({ agent, screen, web }) => {
16
+ await agent.act('add a todo named {title}', { params: { title: 'Write the release notes' } });
19
17
  await expect(screen.getByRole('listitem')).toHaveCount(1);
20
18
  await expect(screen.getByRole('status', { name: 'Remaining' })).toHaveText('1 remaining');
21
19
 
22
- await screen.getByRole('checkbox', { name: 'Write the release notes' }).check();
20
+ await agent.act('mark the todo as done');
23
21
  await expect(screen.getByRole('status', { name: 'Remaining' })).toHaveText('0 remaining');
24
22
  await expect(web).toHaveURL('/todos');
25
23
  });
26
24
 
27
25
  test('ignores an empty submission', async ({ screen }) => {
26
+ // An exact interaction: the empty submit is the point of the test.
28
27
  await screen.getByRole('button', { name: 'Add' }).tap();
29
28
  await expect(screen.getByRole('listitem')).toHaveCount(0);
30
29
  });
31
30
  });
32
31
  ```
33
32
 
34
- Files match the config `tests` glob, default `tests/**/*.e2e.ts`. Every test
35
- starts from clean state: a fresh browser context and no page open, so a test
36
- calls `app.open()` first (here in `beforeEach`).
33
+ The agent does the flow; `expect` pins what must be true after each goal,
34
+ and that check is what lets the trace cache replay the step on later runs.
35
+ `screen` actions are for exact interactions and values, like the empty
36
+ submit above or a sign-in form. Files match the config `tests` glob, default
37
+ `tests/**/*.e2e.ts`. Every test starts from clean state: a fresh browser
38
+ context and no page open, so a test calls `app.open()` first (here in
39
+ `beforeEach`).
37
40
 
38
41
  ## Registration
39
42
 
@@ -237,7 +240,12 @@ credentials: {
237
240
 
238
241
  `E2E_USER_ADMIN_USERNAME` and `E2E_USER_ADMIN_PASSWORD` override either
239
242
  field per run. `credentials.user('admin').password` is a `Secret` with no
240
- plaintext accessor; only `fill()` and `agent.act` params accept it. Once a
243
+ plaintext accessor; only `fill()` and `agent.act` params accept it. Any other
244
+ sensitive value (an API key, a token) is a `secrets` entry,
245
+ `secrets: { 'stripe-key': process.env.STRIPE_KEY ?? '' }`, overridable with
246
+ `E2E_SECRET_STRIPE_KEY`; `secrets.get('stripe-key')` is the same kind of
247
+ handle and fills any editable input, with the value redacted by name
248
+ everywhere the runner writes. Once a
241
249
  secret is filled, model pixels and assertion screenshots are withheld for
242
250
  the rest of that session, including later tests sharing a serial session.
243
251
  `app.screenshot()` fails with `POLICY_DENIED` before capture. Sign in inside
@@ -282,8 +290,9 @@ expect(response.status).toBe(201);
282
290
  Otherwise tests are independent and may run on different workers.
283
291
  - Tag by area and by cost (`smoke`, `billing`, `agent`) and run subsets with
284
292
  `--tag`.
285
- - Mix agent steps in where the path varies and pin the outcome with
286
- `expect`; see the `agent` topic.
293
+ - Drive flows with `agent.act` and pin each outcome with `expect`. The
294
+ calls on this page are for exact values and exact checks; the `agent`
295
+ topic covers the steps that do the work.
287
296
 
288
297
  ## Mistakes to avoid
289
298
 
@@ -1,13 +0,0 @@
1
- /** Opaque credential handles. */
2
- import type { ResolvedCredential } from './config/resolve.ts';
3
- import type { Credentials } from './types.ts';
4
- /** Installed by the runner once config resolution completes. */
5
- export declare function setCredentialRegistry(map: ReadonlyMap<string, ResolvedCredential> | undefined): void;
6
- /**
7
- * Removes `map` from the slot if it is still the installed registry. A host
8
- * that closes one attempt while opening the next must not wipe what the new
9
- * attempt just installed.
10
- */
11
- export declare function releaseCredentialRegistry(map: ReadonlyMap<string, ResolvedCredential>): void;
12
- export declare const credentials: Credentials;
13
- //# sourceMappingURL=credentials.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAKjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAc,WAAW,EAAU,MAAM,YAAY,CAAC;AAKlE,gEAAgE;AAChE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,SAAS,GAAG,IAAI,CAGpG;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAE5F;AAcD,eAAO,MAAM,WAAW,EAAE,WAuBzB,CAAC"}
@@ -1,51 +0,0 @@
1
- /** Opaque credential handles. */
2
- import { credentialBrand, secretBrand } from './internal/brands.js';
3
- import { ConfigurationError } from './internal/errors.js';
4
- import { realmSlot } from './internal/realm-slot.js';
5
- /** Global slot so test modules in an isolated realm reach the runner's registry. */
6
- const credentialsSlot = realmSlot('e2e.credentials.v1');
7
- /** Installed by the runner once config resolution completes. */
8
- export function setCredentialRegistry(map) {
9
- if (map === undefined)
10
- credentialsSlot.delete(globalThis);
11
- else
12
- credentialsSlot.set(globalThis, map);
13
- }
14
- /**
15
- * Removes `map` from the slot if it is still the installed registry. A host
16
- * that closes one attempt while opening the next must not wipe what the new
17
- * attempt just installed.
18
- */
19
- export function releaseCredentialRegistry(map) {
20
- if (credentialsSlot.get(globalThis) === map)
21
- credentialsSlot.delete(globalThis);
22
- }
23
- function getRegistry() {
24
- return credentialsSlot.get(globalThis);
25
- }
26
- function makeSecret(name) {
27
- return Object.freeze({
28
- name,
29
- purpose: 'password',
30
- [secretBrand]: true,
31
- });
32
- }
33
- export const credentials = {
34
- user(name) {
35
- const registry = getRegistry();
36
- if (registry === undefined) {
37
- throw new ConfigurationError('AUTH_CREDENTIAL_UNAVAILABLE', 'credentials.user() is only available while the e2e runner is active');
38
- }
39
- const resolved = registry.get(name);
40
- if (resolved === undefined) {
41
- throw new ConfigurationError('AUTH_CREDENTIAL_UNAVAILABLE', `credential "${name}" is not configured; add it to config.credentials or E2E_USER_* variables`);
42
- }
43
- return Object.freeze({
44
- name,
45
- username: resolved.username,
46
- password: makeSecret(name),
47
- [credentialBrand]: true,
48
- });
49
- },
50
- };
51
- //# sourceMappingURL=credentials.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAIrD,oFAAoF;AACpF,MAAM,eAAe,GAAG,SAAS,CAA0C,oBAAoB,CAAC,CAAC;AAEjG,gEAAgE;AAChE,MAAM,UAAU,qBAAqB,CAAC,GAAwD;IAC5F,IAAI,GAAG,KAAK,SAAS;QAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;;QACrD,eAAe,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAA4C;IACpF,IAAI,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG;QAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,IAAI;QACJ,OAAO,EAAE,UAAmB;QAC5B,CAAC,WAAW,CAAC,EAAE,IAAa;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,IAAI,CAAC,IAAY;QACf,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,kBAAkB,CAC1B,6BAA6B,EAC7B,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,kBAAkB,CAC1B,6BAA6B,EAC7B,eAAe,IAAI,2EAA2E,CAC/F,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,IAAI;YACJ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC;YAC1B,CAAC,eAAe,CAAC,EAAE,IAAa;SACjC,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}