e2e 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +10 -0
  2. package/dist/.build.json +1 -1
  3. package/dist/agent/ai-sdk.js +5 -4
  4. package/dist/agent/error.js +3 -3
  5. package/dist/agent/invocation.js +3 -4
  6. package/dist/agent/model/tool-choice.d.ts +9 -0
  7. package/dist/agent/model/tool-choice.js +14 -0
  8. package/dist/agent/observation-feed.js +3 -4
  9. package/dist/agent/phases.d.ts +7 -1
  10. package/dist/agent/phases.js +12 -3
  11. package/dist/agent/prompts.js +3 -0
  12. package/dist/agent/replay.js +18 -15
  13. package/dist/agent/step-cache.d.ts +2 -1
  14. package/dist/agent/step-cache.js +9 -5
  15. package/dist/agent/tool-loop.js +92 -5
  16. package/dist/cache/trace.js +6 -2
  17. package/dist/cli/bin.js +2 -2
  18. package/dist/cli/index.js +35 -24
  19. package/dist/cli/init/mcp-config.d.ts +2 -1
  20. package/dist/cli/init/mcp-config.js +8 -3
  21. package/dist/cli/init/sibling-versions.json +1 -1
  22. package/dist/cli/mcp.d.ts +3 -2
  23. package/dist/cli/mcp.js +52 -16
  24. package/dist/cli/run-output.d.ts +17 -0
  25. package/dist/cli/run-output.js +45 -0
  26. package/dist/collect/collect.js +8 -4
  27. package/dist/collect/registry.js +27 -16
  28. package/dist/config/compiled-files.d.ts +36 -0
  29. package/dist/config/compiled-files.js +50 -0
  30. package/dist/config/diagnose.js +21 -19
  31. package/dist/config/esm-hooks.d.ts +39 -44
  32. package/dist/config/esm-hooks.js +238 -63
  33. package/dist/config/load.d.ts +1 -7
  34. package/dist/config/load.js +23 -66
  35. package/dist/config/register.d.ts +8 -0
  36. package/dist/config/register.js +9 -0
  37. package/dist/config/specifiers.d.ts +19 -0
  38. package/dist/config/specifiers.js +33 -0
  39. package/dist/config/tsconfig.d.ts +56 -0
  40. package/dist/config/tsconfig.js +115 -0
  41. package/dist/config/typescript.d.ts +19 -0
  42. package/dist/config/typescript.js +253 -0
  43. package/dist/engine/contract.d.ts +1 -0
  44. package/dist/engine/index.d.ts +2 -0
  45. package/dist/engine/session.js +2 -2
  46. package/dist/expect/async.js +2 -1
  47. package/dist/expect/poll.js +2 -2
  48. package/dist/internal/errors.d.ts +10 -3
  49. package/dist/internal/errors.js +23 -8
  50. package/dist/internal/node-version.d.ts +16 -7
  51. package/dist/internal/node-version.js +50 -15
  52. package/dist/internal/package-manager.js +1 -1
  53. package/dist/internal/realm-slot.d.ts +5 -4
  54. package/dist/internal/redact.d.ts +2 -0
  55. package/dist/internal/redact.js +4 -0
  56. package/dist/internal/time.d.ts +21 -5
  57. package/dist/internal/time.js +35 -3
  58. package/dist/locator/engine.js +47 -11
  59. package/dist/locator/screen.js +31 -5
  60. package/dist/mcp/output.d.ts +59 -0
  61. package/dist/mcp/output.js +129 -0
  62. package/dist/mcp/server.d.ts +4 -0
  63. package/dist/mcp/server.js +9 -12
  64. package/dist/mcp/session.d.ts +8 -1
  65. package/dist/mcp/session.js +55 -14
  66. package/dist/mcp/tools.js +7 -5
  67. package/dist/report/list.d.ts +11 -0
  68. package/dist/report/list.js +23 -6
  69. package/dist/run/artifacts.d.ts +4 -4
  70. package/dist/run/artifacts.js +3 -2
  71. package/dist/run/execute.d.ts +25 -5
  72. package/dist/run/execute.js +44 -20
  73. package/dist/run/failure-evidence.js +6 -2
  74. package/dist/run/in-process.js +51 -10
  75. package/dist/run/process-output.d.ts +73 -0
  76. package/dist/run/process-output.js +130 -0
  77. package/dist/run/provision.d.ts +2 -0
  78. package/dist/run/provision.js +1 -0
  79. package/dist/run/records.d.ts +4 -4
  80. package/dist/run/retry.d.ts +7 -0
  81. package/dist/run/retry.js +27 -11
  82. package/dist/run/runner.d.ts +8 -0
  83. package/dist/run/runner.js +32 -9
  84. package/dist/run/scheduler.js +255 -19
  85. package/dist/run/secrecy.d.ts +22 -19
  86. package/dist/run/secrecy.js +22 -18
  87. package/dist/run/serial.d.ts +73 -2
  88. package/dist/run/serial.js +239 -58
  89. package/dist/run/standalone.js +1 -1
  90. package/dist/run/trace-redaction.d.ts +5 -6
  91. package/dist/run/trace-redaction.js +5 -6
  92. package/dist/run/worker/entry.js +35 -14
  93. package/dist/run/worker/handle.js +10 -5
  94. package/dist/run/worker/protocol.d.ts +83 -3
  95. package/dist/run/worker/session.js +28 -0
  96. package/dist/telemetry/environment.d.ts +2 -1
  97. package/dist/telemetry/environment.js +12 -2
  98. package/dist/telemetry/project.d.ts +5 -6
  99. package/dist/telemetry/project.js +6 -11
  100. package/dist/telemetry/store.d.ts +5 -5
  101. package/dist/telemetry/store.js +5 -7
  102. package/dist/telemetry/telemetry.d.ts +2 -3
  103. package/dist/telemetry/telemetry.js +2 -4
  104. package/dist/types.d.ts +6 -3
  105. package/docs/agent-steps.mdx +1 -0
  106. package/docs/agents.mdx +1 -0
  107. package/docs/api-testing.mdx +2 -1
  108. package/docs/authentication.mdx +3 -2
  109. package/docs/browser.mdx +7 -6
  110. package/docs/bug-bash.mdx +2 -1
  111. package/docs/cache.mdx +2 -1
  112. package/docs/ci.mdx +3 -2
  113. package/docs/coding-agents.mdx +2 -1
  114. package/docs/core-concepts.mdx +208 -0
  115. package/docs/debugging.mdx +19 -9
  116. package/docs/decision-models.mdx +142 -0
  117. package/docs/example-projects.mdx +32 -0
  118. package/docs/executors.mdx +3 -0
  119. package/docs/explore.mdx +1 -0
  120. package/docs/github.mdx +2 -1
  121. package/docs/index.mdx +9 -5
  122. package/docs/integrations/eas.mdx +1 -0
  123. package/docs/integrations/index.mdx +1 -0
  124. package/docs/integrations/kernel.mdx +2 -1
  125. package/docs/migrate/cypress.mdx +26 -3
  126. package/docs/migrate/detox.mdx +24 -1
  127. package/docs/migrate/maestro.mdx +28 -4
  128. package/docs/migrate/playwright.mdx +27 -4
  129. package/docs/migrate/selenium.mdx +27 -3
  130. package/docs/mobile-ci.mdx +6 -5
  131. package/docs/mobile.mdx +79 -8
  132. package/docs/models.mdx +7 -2
  133. package/docs/quickstart.mdx +26 -15
  134. package/docs/reference/agent.mdx +13 -3
  135. package/docs/reference/app.mdx +1 -0
  136. package/docs/reference/cli.mdx +11 -5
  137. package/docs/reference/config.mdx +42 -6
  138. package/docs/reference/engine.mdx +22 -10
  139. package/docs/reference/environment.mdx +2 -1
  140. package/docs/reference/errors.mdx +11 -8
  141. package/docs/reference/expect.mdx +2 -1
  142. package/docs/reference/mcp.mdx +21 -8
  143. package/docs/reference/mobile.mdx +1 -0
  144. package/docs/reference/reporters.mdx +1 -0
  145. package/docs/reference/screen.mdx +19 -9
  146. package/docs/reference/test.mdx +21 -1
  147. package/docs/reference/web.mdx +8 -2
  148. package/docs/security.mdx +33 -27
  149. package/docs/subscriptions.mdx +1 -0
  150. package/docs/telemetry.mdx +8 -5
  151. package/docs/tools.mdx +1 -0
  152. package/docs/{starting-your-app.mdx → web.mdx} +157 -11
  153. package/docs/writing-an-engine.mdx +2 -1
  154. package/package.json +11 -7
  155. package/skills/e2e/SKILL.md +3 -1
  156. package/skills/e2e/references/agent.md +11 -0
  157. package/skills/e2e/references/debugging.md +1 -1
  158. package/skills/e2e/references/mcp.md +8 -3
  159. package/skills/e2e/references/running.md +2 -1
  160. package/skills/e2e/references/setup.md +9 -1
  161. package/skills/e2e/references/writing-tests.md +1 -1
  162. package/docs/assertions.mdx +0 -94
  163. package/docs/goals.mdx +0 -176
  164. package/docs/locators.mdx +0 -64
  165. package/docs/physical-devices.mdx +0 -76
  166. package/docs/viewports.mdx +0 -146
@@ -1,94 +0,0 @@
1
- ---
2
- title: Assertions
3
- description: Check the screen with agent.assert, wait for a condition, and extract data.
4
- ---
5
-
6
- An assertion checks what is on screen. The agent has three judgment calls:
7
- `assert` checks the screen now, `waitFor` waits until a condition holds, and
8
- `extract` reads structured data. When you know the exact value, use an
9
- `expect` [locator assertion](#locator-assertions) instead, with no model call.
10
-
11
- ```ts
12
- await agent.assert('the dashboard shows a trial badge');
13
- ```
14
-
15
- ## `agent.assert`
16
-
17
- `assert` checks a statement against the current screen. A false statement
18
- fails with `ASSERTION_FAILED`. If the screen does not provide enough evidence,
19
- it fails with `ASSERTION_INCONCLUSIVE`.
20
-
21
- The judging model sees the statement and the current screen. It does not see
22
- earlier steps or the acting agent's summaries.
23
-
24
- ## `agent.waitFor`
25
-
26
- `assert` checks the screen now. Use `waitFor` when the condition may become true
27
- later. It polls the screen and only calls the model when the observation changes.
28
-
29
- ```ts
30
- await agent.waitFor('the export finishes and a download link appears', {
31
- interval: 500,
32
- timeout: 120_000,
33
- });
34
- ```
35
-
36
- ## `agent.extract`
37
-
38
- `extract` reads structured data from the screen. Provide a
39
- [Standard Schema](https://standardschema.dev) validator, such as Zod, to define
40
- the result.
41
-
42
- ```ts
43
- import { expect } from 'e2e';
44
- import { z } from 'zod';
45
-
46
- const data = await agent.extract('every todo title and how many remain', {
47
- schema: z.object({ titles: z.array(z.string()), remaining: z.number().int() }),
48
- });
49
- expect(data.titles).toContain('Buy milk');
50
- ```
51
-
52
- <Tip>
53
- For model-generated descriptions, check the relevant meaning rather than the
54
- entire sentence. Use a [locator assertion](/locators) when the exact text matters.
55
- </Tip>
56
-
57
- ## Judging pixels
58
-
59
- By default, judgments read a redacted text snapshot of the screen. Add
60
- `vision: true` for visual checks, such as charts or layout. Use
61
- `vision: 'only'` to judge the screenshot alone.
62
-
63
- ```ts
64
- await agent.assert('the chart trends upward', { vision: true });
65
- await agent.assert('the search form is not covered by a banner', { vision: 'only' });
66
- ```
67
-
68
- Images add input tokens. Screenshots are withheld for the rest of an attempt
69
- after a secret is filled. The [agent reference](/reference/agent#vision)
70
- lists the modes and restrictions.
71
-
72
- ## Locator assertions
73
-
74
- `expect` on a locator checks an exact value and calls no model. It retries
75
- until the check passes or the assertion timeout runs out.
76
-
77
- ```ts
78
- await expect(screen.getByRole('status')).toHaveText('2 remaining');
79
- await expect(screen.getByRole('heading', 'Dashboard')).toBeVisible();
80
- ```
81
-
82
- [Locators](/locators) covers queries and actions. The
83
- [expect reference](/reference/expect#locator-matchers) lists every matcher.
84
-
85
- ## Next
86
-
87
- <CardGroup cols={2}>
88
- <Card title="Locators" icon="crosshairs" href="/locators">
89
- Exact actions and checks with `screen` and `expect`.
90
- </Card>
91
- <Card title="How agent steps work" icon="list-check" href="/agent-steps">
92
- Verdicts, exit codes, and what the model sees.
93
- </Card>
94
- </CardGroup>
package/docs/goals.mdx DELETED
@@ -1,176 +0,0 @@
1
- ---
2
- title: Goals
3
- description: Write test steps as goals with agent.act, and structure the tests around them.
4
- ---
5
-
6
- Use agent calls for goals and questions about the screen. Use
7
- [locators](/locators) when you know the exact action or value to check. A test
8
- can freely mix both.
9
-
10
- ```ts title="tests/todos.e2e.ts"
11
- import { test, expect } from 'e2e';
12
-
13
- test('todos survive a filter round-trip', async ({ app, agent, screen }) => {
14
- await app.open('/todos');
15
-
16
- await agent.act('add todos "Buy milk" and "Walk the dog", complete the first');
17
- await agent.assert('only one todo remains open');
18
-
19
- await screen.getByRole('tab', 'Done').tap();
20
- await expect(screen.getByTestId('todo')).toHaveCount(1);
21
- });
22
- ```
23
-
24
- ## Goals: `agent.act`
25
-
26
- Give `act` one goal. The agent chooses and performs the actions, then returns
27
- when the goal passes or throws when it fails. Await every agent and screen
28
- call, and every `expect.poll`; a test that returns while one is still running
29
- fails with `STEP_NOT_AWAITED`.
30
-
31
- ```ts
32
- await agent.act('complete checkout with the test card');
33
- await agent.act('invite {email} as an editor', { params: { email: 'ada@example.test' } });
34
- ```
35
-
36
- Pass values through `params` so the agent uses your test data. For passwords,
37
- use a `Secret`, this prevents the model from seeing your secrets. See [Signing in](/authentication).
38
-
39
- ```ts
40
- import { credentials } from 'e2e';
41
-
42
- const member = credentials.user('member');
43
- await agent.act('Sign in', {
44
- params: { username: member.username, password: member.password },
45
- });
46
- ```
47
-
48
- <Tip>
49
- **Write instructions as you would say them out loud**
50
-
51
- Name what is visible and prefer the exact wording on screen. One goal per
52
- call. The flow inside a goal is the model's job; the order of goals is yours.
53
- </Tip>
54
-
55
- For a value that changes on every run, wrap it in `unique()` so the
56
- [replay cache](/cache#what-must-match) can still replay the step:
57
-
58
- ```ts
59
- import { expect, unique } from 'e2e';
60
-
61
- const email = `ada+${Date.now()}@example.test`;
62
- await agent.act('sign up with email {email}', { params: { email: unique(email) } });
63
- await expect(screen.getByText(email)).toBeVisible();
64
- ```
65
-
66
- If a goal fails, check the report and make the instruction more specific.
67
- Use the labels on screen. Put vocabulary shared by several goals in the
68
- agent's `context`; see [Agents and personas](/agents).
69
-
70
- A step can pass, fail, or be blocked by something such as missing credentials.
71
- [How agent steps work](/agent-steps#how-a-step-ends) explains the verdicts and
72
- exit codes.
73
-
74
- `act` requests screenshots itself when it needs them, so unlike the
75
- [judgments](/assertions#judging-pixels) it has no `vision` option. On
76
- supported engines, it can tap and type at points in the image:
77
-
78
- ```ts
79
- await agent.act('enter the code 3141 on the drawn keypad and press OK');
80
- ```
81
-
82
- Images add input tokens. Screenshots are withheld for the rest of an attempt
83
- after a secret is filled. The [agent reference](/reference/agent#vision)
84
- lists the modes and restrictions.
85
-
86
- To check what a goal left on screen, follow it with an
87
- [assertion](/assertions) or a [locator check](/locators).
88
-
89
- ## Tests without a screen
90
-
91
- A test that takes only `app` calls no model and drives no screen. Use it to
92
- check your API with `fetch` in the same run as UI tests. See
93
- [Testing APIs](/api-testing).
94
-
95
- ## Structure
96
-
97
- ```ts
98
- import { beforeEach, describe, test } from 'e2e';
99
-
100
- describe('billing', { tags: ['billing'] }, () => {
101
- beforeEach(async ({ app }) => { await app.open('/billing'); });
102
-
103
- test('upgrades', { retries: 2, timeout: 60_000 }, async ({ agent }) => {
104
- await agent.act('upgrade to Pro');
105
- });
106
- });
107
-
108
- test.skip('not ready yet', async () => {});
109
- test.only('just this one while I work', async () => {}); // rejected in CI
110
-
111
- test('needs two organizations', async ({ app }) => {
112
- test.skip((await countOrganizations()) < 2, 'one organization: nothing to switch to');
113
- await app.open('/organizations');
114
- });
115
- ```
116
-
117
- `test.skip(condition, reason)` inside a body throws when the condition holds:
118
- the test stops at that line and is reported `skipped`, as in Playwright. Call
119
- it before the first step when it guards a precondition.
120
-
121
- `describe` and the hooks are also `test.describe`, `test.beforeEach`, and so
122
- on. A `beforeEach` imported from `e2e` sees the core fixtures; import
123
- `test`, `describe`, and the hooks from `@e2e-dev/web` (or `@e2e-dev/mobile`)
124
- to have `browser` (or `device`) typed from one import, and use
125
- `test.beforeEach` on a `test` you extended with your own fixtures.
126
-
127
- Common options include `timeout`, `retries`, and `tags`. Use `session` for
128
- state from a setup test, `agentContext` for extra instructions, and `agent`
129
- to choose a configured [agent](/agents). `trace` and `video` record one test
130
- without recording the suite; see [Watch it happen](/debugging#watch-it-happen).
131
- Group options apply to their tests. The [test reference](/reference/test)
132
- lists all options and defaults.
133
-
134
- Each test runs in its own attempt. Prepare the app data it needs through
135
- fixtures or hooks. When a flow must span several tests, mark the group
136
- `{ serial: true }`. Members share app state, run in order on one worker,
137
- and retry as a whole.
138
-
139
- Share setup and cleanup through `test.extend`. A fixture can create a test
140
- workspace before the test and delete it afterward, including when the test
141
- fails. See [Your own fixtures](/reference/test#your-own-fixtures).
142
-
143
- ## Run one test
144
-
145
- <CodeGroup>
146
- ```bash npm
147
- npx e2e run tests/signup.e2e.ts:12 # the test declared at line 12
148
- npx e2e run --grep checkout # tests whose title matches
149
- npx e2e run --target web # one target, by name
150
- ```
151
-
152
- ```bash pnpm
153
- pnpm exec e2e run tests/signup.e2e.ts:12 # the test declared at line 12
154
- pnpm exec e2e run --grep checkout # tests whose title matches
155
- pnpm exec e2e run --target web # one target, by name
156
- ```
157
-
158
- ```bash bun
159
- bunx e2e run tests/signup.e2e.ts:12 # the test declared at line 12
160
- bunx e2e run --grep checkout # tests whose title matches
161
- bunx e2e run --target web # one target, by name
162
- ```
163
- </CodeGroup>
164
-
165
- The [CLI reference](/reference/cli#e2e-run) lists every flag.
166
-
167
- ## Next
168
-
169
- <CardGroup cols={2}>
170
- <Card title="Assertions" icon="circle-check" href="/assertions">
171
- Check the screen with `assert`, `waitFor`, and `extract`.
172
- </Card>
173
- <Card title="Locators" icon="crosshairs" href="/locators">
174
- Exact actions and checks with `screen` and `expect`.
175
- </Card>
176
- </CardGroup>
package/docs/locators.mdx DELETED
@@ -1,64 +0,0 @@
1
- ---
2
- title: Locators
3
- description: Deterministic actions and checks with screen and expect.
4
- ---
5
-
6
- Use locators to interact with known controls and check exact values. Locator
7
- steps call no model.
8
-
9
- ```ts title="tests/todos.e2e.ts"
10
- import { test, expect } from 'e2e';
11
-
12
- test('adds a todo', async ({ app, screen }) => {
13
- await app.open('/todos');
14
- await screen.getByLabel('New todo').fill('Buy milk');
15
- await screen.getByRole('button', 'Add').tap();
16
- await expect(screen.getByTestId('todo')).toHaveCount(1);
17
- });
18
- ```
19
-
20
- An `expect` after an [`agent.act` goal](/goals) checks the exact result. It is
21
- also what lets the [replay cache](/cache) record the step and keep it.
22
-
23
- ## Queries, actions, and assertions
24
-
25
- ```ts
26
- // Semantic queries: role and name first, label second, test ID last.
27
- await screen.getByRole('button', 'Sign in').tap();
28
- await screen.getByLabel('Email').fill('ada@example.test');
29
- await screen.getByTestId('todo').first().tap();
30
-
31
- // Scope into a row, gesture like a user:
32
- const row = screen.getByRole('listitem').filter({ hasText: 'Design review' });
33
- await row.getByRole('button', 'Archive').tap();
34
- await screen.swipe({ direction: 'up', momentum: 'fast' });
35
-
36
- // Coordinates, for what the tree does not list:
37
- await screen.getByRole('image', 'Seat map').tap({ position: { x: 120, y: 48 } });
38
- await screen.swipe({ from: { x: 300, y: 400 }, to: { x: 40, y: 400 } });
39
-
40
- // Polling assertions:
41
- await expect(screen.getByRole('status')).toHaveText('2 remaining');
42
- await expect(screen.getByRole('heading', 'Dashboard')).toBeVisible();
43
- ```
44
-
45
- Actions and single-node reads or assertions fail when a query matches more
46
- than one node. Narrow it with `.filter()`, `.first()`, or `.nth()`.
47
- Locator assertions retry until they pass or time out. Direct reads such as
48
- `textContent()` do not retry a mismatched value.
49
-
50
- Use the `browser` fixture for browser features such as routes, cookies, dialogs,
51
- frames, and downloads; see [Working with the browser](/browser). The
52
- [screen reference](/reference/screen) lists every query and action, and the
53
- [expect reference](/reference/expect#locator-matchers) lists every matcher.
54
-
55
- ## Next
56
-
57
- <CardGroup cols={2}>
58
- <Card title="Signing in" icon="key" href="/authentication">
59
- Sessions and secrets.
60
- </Card>
61
- <Card title="Agents and personas" icon="users" href="/agents">
62
- Several agents in one suite, and running a flow as each of them.
63
- </Card>
64
- </CardGroup>
@@ -1,76 +0,0 @@
1
- ---
2
- title: Physical devices
3
- description: Run mobile tests on an iPhone or Android phone connected to your machine.
4
- ---
5
-
6
- agent-device drives a phone plugged into your machine the same way it
7
- drives a simulator, so a physical device needs no other engine or option.
8
- List what agent-device sees, then name the phone in `device` as it is
9
- listed:
10
-
11
- <CodeGroup>
12
- ```bash npm
13
- npx agent-device devices --platform ios
14
- ```
15
-
16
- ```bash pnpm
17
- pnpm exec agent-device devices --platform ios
18
- ```
19
-
20
- ```bash bun
21
- bunx agent-device devices --platform ios
22
- ```
23
- </CodeGroup>
24
-
25
- ```ts
26
- mobile({ platform: 'ios', device: 'QA iPhone' })
27
- ```
28
-
29
- Name a phone by its name, not its id. The engine reads a `device` string as
30
- an id only when it is a simulator UDID or an `emulator-<port>` serial, so a
31
- phone's UDID or adb serial is looked up as a name and not found. The first
32
- device with a matching name wins, so give each phone a unique name: two
33
- phones both called `iPhone` or `Pixel 8` would bind one phone to two
34
- workers. Without `device`, a connected phone counts as booted and joins the
35
- pool beside any booted simulators, so name it to be sure a run lands on it.
36
-
37
- Set the phone up once:
38
-
39
- - **iOS**: the phone is paired and trusted, unlocked, with Developer Mode
40
- on. agent-device builds and signs its XCTest runner onto the phone, so set
41
- `AGENT_DEVICE_IOS_TEAM_ID` to your team id and `AGENT_DEVICE_IOS_BUNDLE_ID`
42
- to a unique reverse-DNS id (`com.example.agentdevice.runner`); the
43
- signing profile must allow that id and `<id>.uitests`. Set them before the
44
- first agent-device command, or run `npx agent-device daemon stop` after:
45
- a running daemon keeps the environment it started with. The first run
46
- builds the runner and takes longer. `app.appPath` must be a signed device
47
- build: a simulator `.app` does not install on a phone.
48
- - **Android**: USB debugging is on and the phone is authorized, listed as
49
- `device` in `adb devices`. `app.appPath` is the same `.apk`.
50
-
51
- `npx agent-device doctor` checks the setup, and
52
- `npx agent-device help physical-device` covers iOS signing in detail.
53
-
54
- Some device controls need a simulator or emulator, and fail on a phone with
55
- agent-device's message:
56
-
57
- - On an iPhone, every device setting is simulator-only: `app.permissions`
58
- and `device.setPermission`, `app.clearState()`, `device.setNetwork`,
59
- `device.setAirplaneMode`, `device.setLocation`, `device.clearLocation`,
60
- `device.setAppearance`, the biometric methods, and `device.clearKeychain`.
61
- So are `device.clipboard`, `device.setClipboard`, and `device.fold`. For a
62
- fresh start, reinstall with `device.installApp(path, { reinstall: true })`.
63
- - An older iPhone that `xcrun devicectl list devices` does not list is
64
- driven without CoreDevice: taps, typing, and the screen work, but
65
- installs, `--video`, `device.openLink`, and `app.launchArguments` fail.
66
- Install the build from Xcode and pin `app.bundleId`.
67
- - On an Android phone, `device.setLocation` is emulator-only, and
68
- `device.setBiometrics('fingerprint', ...)` works only where the phone
69
- exposes `cmd fingerprint`.
70
- - `--video` on an iPhone is built from screenshots at about 15 frames a
71
- second, and drops frames.
72
- - A phone is not this machine, so `localhost` in the app is the phone
73
- itself. Point the app at your machine's network address, or on Android
74
- forward the port with `adb reverse tcp:<port> tcp:<port>`.
75
-
76
- To run on phones in a device farm, see [Hosted devices](/mobile#hosted-devices).
@@ -1,146 +0,0 @@
1
- ---
2
- title: Testing viewports
3
- description: Run a suite at several sizes, resize inside a test, and know what the agent sees at each.
4
- ---
5
-
6
- Set a viewport on the target to run a suite at that size. Use
7
- `browser.setViewport()` to resize during a test. The agent uses the current size
8
- and cannot resize it.
9
-
10
- ## One size per target
11
-
12
- `viewport` sets the initial size of each attempt, in pixels. The default is
13
- 1280 by 720. Add one target for each size you want to test:
14
-
15
- ```ts title="e2e.config.ts"
16
- import type { E2EConfig } from 'e2e';
17
- import { web } from '@e2e-dev/web';
18
-
19
- const app = { url: 'http://localhost:3000' };
20
-
21
- export default {
22
- targets: [
23
- { name: 'desktop', engine: web(), app },
24
- { name: 'tablet', engine: web({ viewport: { width: 820, height: 1180 } }), app },
25
- { name: 'phone', engine: web({ browser: 'webkit', viewport: { width: 390, height: 844 } }), app },
26
- ],
27
- } satisfies E2EConfig;
28
- ```
29
-
30
- Each test runs once per target, with a separate result labeled `desktop`,
31
- `tablet`, or `phone`. Run one size with `npx e2e run --target phone`, or list
32
- the selected test-target pairs with `npx e2e list`.
33
-
34
- To have the runner start one dev server for every size, give every target
35
- the same `app.command`: targets declaring one command share one process; see
36
- [several targets](/starting-your-app#several-targets). Every retry starts at
37
- the target's configured size.
38
-
39
- ## Fill the window
40
-
41
- `viewport: null` sets no size, so the page fills the browser window. The
42
- agent, screenshots, and videos use that size. Use it in a headed run, or
43
- with a hosted browser whose live view would show a fixed 1280 by 720 page
44
- in one corner:
45
-
46
- ```ts title="e2e.config.ts"
47
- { name: 'window', engine: web({ viewport: null }), app }
48
- ```
49
-
50
- The page takes the window's size, minus the toolbar in a headed browser. A
51
- local headed launch opens a small window; a hosted browser uses the window
52
- size you requested from the service. `browser.setViewport` still works.
53
-
54
- ## Resize inside a test
55
-
56
- `browser.setViewport` resizes the page for the rest of the attempt, through
57
- `app.restart()` and `app.clearState()`:
58
-
59
- ```ts
60
- import { test } from '@e2e-dev/web';
61
- import { expect } from 'e2e';
62
-
63
- test('the menu collapses on a narrow screen', async ({ app, screen, browser }) => {
64
- await app.open('/');
65
- await expect(screen.getByRole('navigation')).toBeVisible();
66
-
67
- await browser.setViewport({ width: 390, height: 844 });
68
- await expect(screen.getByRole('button', 'Menu')).toBeVisible();
69
- await expect(screen.getByRole('navigation')).toBeHidden();
70
- });
71
- ```
72
-
73
- Use this when one test needs to check a layout before and after resizing.
74
- For a whole suite, configure separate targets instead.
75
-
76
- Call it before `app.open` to load the first page at that size, so the app
77
- never renders its desktop layout first:
78
-
79
- ```ts
80
- await browser.setViewport({ width: 390, height: 844 });
81
- await app.open('/');
82
- ```
83
-
84
- The resize appears as a step in the report. To check the exact width, read
85
- it from the page:
86
-
87
- ```ts
88
- const width = await browser.evaluate(() => window.innerWidth);
89
- expect(width).toBe(390);
90
- ```
91
-
92
- ## What the agent sees
93
-
94
- The agent receives the current viewport size with each observation.
95
- Screenshots cover that viewport. Off-screen elements can still appear in
96
- the tree, and tapping them scrolls them into view. The agent can scroll to
97
- reveal lazy-loaded content.
98
-
99
- Resize before calling `agent.act`:
100
-
101
- ```ts
102
- test('the mobile menu reaches pricing', async ({ app, agent, browser }) => {
103
- await browser.setViewport({ width: 390, height: 844 });
104
- await app.open('/');
105
- await agent.act('open the navigation menu and go to Pricing');
106
- await expect(browser).toHaveURL('/pricing');
107
- });
108
- ```
109
-
110
- ## Scoping a test to one size
111
-
112
- `platforms` and `requires` cannot select a viewport; all Playwright targets
113
- use platform `web`. To skip a test at some sizes, check the width in the
114
- test:
115
-
116
- ```ts
117
- test('the sidebar stays open', async ({ app, screen, browser }) => {
118
- await app.open('/');
119
- test.skip((await browser.evaluate(() => window.innerWidth)) < 768, 'desktop layout only');
120
- await expect(screen.getByRole('complementary')).toBeVisible();
121
- });
122
- ```
123
-
124
- To keep a whole group of size-specific tests apart, give them their own
125
- config and run it with `--config`.
126
-
127
- ## Limits
128
-
129
- A narrow viewport tests responsive layout. It still uses a desktop browser
130
- with a scale factor of 1, desktop pointer behavior, and its usual user agent.
131
- It does not emulate touch, color scheme, or reduced-motion preferences.
132
- Use the [device engine](/mobile) for tests on a simulator or emulator.
133
-
134
- A video keeps the size the page had when it started recording; set
135
- `web({ screencast: { size } })` for one fixed size. A browser provider that
136
- records, such as [Kernel](/integrations/kernel#recordings), records the whole
137
- screen instead.
138
-
139
- <CardGroup cols={2}>
140
- <Card title="Working with the browser" icon="globe" href="/browser">
141
- Routes, cookies, dialogs, downloads, and the rest of the `browser` fixture.
142
- </Card>
143
- <Card title="Web engine reference" icon="masks-theater" href="/reference/web">
144
- Every engine option and `browser` method, with timeouts and errors.
145
- </Card>
146
- </CardGroup>