e2e 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/.build.json +1 -1
- package/dist/agent/ai-sdk.js +5 -4
- package/dist/agent/error.js +3 -3
- package/dist/agent/invocation.js +3 -4
- package/dist/agent/model/tool-choice.d.ts +9 -0
- package/dist/agent/model/tool-choice.js +14 -0
- package/dist/agent/observation-feed.js +3 -4
- package/dist/agent/phases.d.ts +7 -1
- package/dist/agent/phases.js +12 -3
- package/dist/agent/prompts.js +3 -0
- package/dist/agent/replay.js +18 -15
- package/dist/agent/step-cache.d.ts +2 -1
- package/dist/agent/step-cache.js +9 -5
- package/dist/agent/tool-loop.js +92 -5
- package/dist/cache/trace.js +6 -2
- package/dist/cli/bin.js +2 -2
- package/dist/cli/index.js +35 -24
- package/dist/cli/init/mcp-config.d.ts +2 -1
- package/dist/cli/init/mcp-config.js +8 -3
- package/dist/cli/init/sibling-versions.json +1 -1
- package/dist/cli/mcp.d.ts +3 -2
- package/dist/cli/mcp.js +52 -16
- package/dist/cli/run-output.d.ts +17 -0
- package/dist/cli/run-output.js +45 -0
- package/dist/collect/collect.js +8 -4
- package/dist/collect/registry.js +27 -16
- package/dist/config/compiled-files.d.ts +36 -0
- package/dist/config/compiled-files.js +50 -0
- package/dist/config/diagnose.js +21 -19
- package/dist/config/esm-hooks.d.ts +39 -44
- package/dist/config/esm-hooks.js +238 -63
- package/dist/config/load.d.ts +1 -7
- package/dist/config/load.js +23 -66
- package/dist/config/register.d.ts +8 -0
- package/dist/config/register.js +9 -0
- package/dist/config/specifiers.d.ts +19 -0
- package/dist/config/specifiers.js +33 -0
- package/dist/config/tsconfig.d.ts +56 -0
- package/dist/config/tsconfig.js +115 -0
- package/dist/config/typescript.d.ts +19 -0
- package/dist/config/typescript.js +253 -0
- package/dist/engine/contract.d.ts +1 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/session.js +2 -2
- package/dist/expect/async.js +2 -1
- package/dist/expect/poll.js +2 -2
- package/dist/internal/errors.d.ts +10 -3
- package/dist/internal/errors.js +23 -8
- package/dist/internal/node-version.d.ts +16 -7
- package/dist/internal/node-version.js +50 -15
- package/dist/internal/package-manager.js +1 -1
- package/dist/internal/realm-slot.d.ts +5 -4
- package/dist/internal/redact.d.ts +2 -0
- package/dist/internal/redact.js +4 -0
- package/dist/internal/time.d.ts +21 -5
- package/dist/internal/time.js +35 -3
- package/dist/locator/engine.js +47 -11
- package/dist/locator/screen.js +31 -5
- package/dist/mcp/output.d.ts +59 -0
- package/dist/mcp/output.js +129 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +9 -12
- package/dist/mcp/session.d.ts +8 -1
- package/dist/mcp/session.js +55 -14
- package/dist/mcp/tools.js +7 -5
- package/dist/report/list.d.ts +11 -0
- package/dist/report/list.js +23 -6
- package/dist/run/artifacts.d.ts +4 -4
- package/dist/run/artifacts.js +3 -2
- package/dist/run/execute.d.ts +25 -5
- package/dist/run/execute.js +44 -20
- package/dist/run/failure-evidence.js +6 -2
- package/dist/run/in-process.js +51 -10
- package/dist/run/process-output.d.ts +73 -0
- package/dist/run/process-output.js +130 -0
- package/dist/run/provision.d.ts +2 -0
- package/dist/run/provision.js +1 -0
- package/dist/run/records.d.ts +4 -4
- package/dist/run/retry.d.ts +7 -0
- package/dist/run/retry.js +27 -11
- package/dist/run/runner.d.ts +8 -0
- package/dist/run/runner.js +32 -9
- package/dist/run/scheduler.js +255 -19
- package/dist/run/secrecy.d.ts +22 -19
- package/dist/run/secrecy.js +22 -18
- package/dist/run/serial.d.ts +73 -2
- package/dist/run/serial.js +239 -58
- package/dist/run/standalone.js +1 -1
- package/dist/run/trace-redaction.d.ts +5 -6
- package/dist/run/trace-redaction.js +5 -6
- package/dist/run/worker/entry.js +35 -14
- package/dist/run/worker/handle.js +10 -5
- package/dist/run/worker/protocol.d.ts +83 -3
- package/dist/run/worker/session.js +28 -0
- package/dist/telemetry/environment.d.ts +2 -1
- package/dist/telemetry/environment.js +12 -2
- package/dist/telemetry/project.d.ts +5 -6
- package/dist/telemetry/project.js +6 -11
- package/dist/telemetry/store.d.ts +5 -5
- package/dist/telemetry/store.js +5 -7
- package/dist/telemetry/telemetry.d.ts +2 -3
- package/dist/telemetry/telemetry.js +2 -4
- package/dist/types.d.ts +6 -3
- package/docs/agent-steps.mdx +1 -0
- package/docs/agents.mdx +1 -0
- package/docs/api-testing.mdx +2 -1
- package/docs/authentication.mdx +3 -2
- package/docs/browser.mdx +7 -6
- package/docs/bug-bash.mdx +2 -1
- package/docs/cache.mdx +2 -1
- package/docs/ci.mdx +3 -2
- package/docs/coding-agents.mdx +2 -1
- package/docs/core-concepts.mdx +208 -0
- package/docs/debugging.mdx +19 -9
- package/docs/decision-models.mdx +142 -0
- package/docs/example-projects.mdx +32 -0
- package/docs/executors.mdx +3 -0
- package/docs/explore.mdx +1 -0
- package/docs/github.mdx +2 -1
- package/docs/index.mdx +9 -5
- package/docs/integrations/eas.mdx +1 -0
- package/docs/integrations/index.mdx +1 -0
- package/docs/integrations/kernel.mdx +2 -1
- package/docs/migrate/cypress.mdx +26 -3
- package/docs/migrate/detox.mdx +24 -1
- package/docs/migrate/maestro.mdx +28 -4
- package/docs/migrate/playwright.mdx +27 -4
- package/docs/migrate/selenium.mdx +27 -3
- package/docs/mobile-ci.mdx +6 -5
- package/docs/mobile.mdx +79 -8
- package/docs/models.mdx +7 -2
- package/docs/quickstart.mdx +26 -15
- package/docs/reference/agent.mdx +13 -3
- package/docs/reference/app.mdx +1 -0
- package/docs/reference/cli.mdx +11 -5
- package/docs/reference/config.mdx +42 -6
- package/docs/reference/engine.mdx +22 -10
- package/docs/reference/environment.mdx +2 -1
- package/docs/reference/errors.mdx +11 -8
- package/docs/reference/expect.mdx +2 -1
- package/docs/reference/mcp.mdx +21 -8
- package/docs/reference/mobile.mdx +1 -0
- package/docs/reference/reporters.mdx +1 -0
- package/docs/reference/screen.mdx +19 -9
- package/docs/reference/test.mdx +21 -1
- package/docs/reference/web.mdx +8 -2
- package/docs/security.mdx +33 -27
- package/docs/subscriptions.mdx +1 -0
- package/docs/telemetry.mdx +8 -5
- package/docs/tools.mdx +1 -0
- package/docs/{starting-your-app.mdx → web.mdx} +157 -11
- package/docs/writing-an-engine.mdx +2 -1
- package/package.json +11 -7
- package/skills/e2e/SKILL.md +3 -1
- package/skills/e2e/references/agent.md +11 -0
- package/skills/e2e/references/debugging.md +1 -1
- package/skills/e2e/references/mcp.md +8 -3
- package/skills/e2e/references/running.md +2 -1
- package/skills/e2e/references/setup.md +9 -1
- package/skills/e2e/references/writing-tests.md +1 -1
- package/docs/assertions.mdx +0 -94
- package/docs/goals.mdx +0 -176
- package/docs/locators.mdx +0 -64
- package/docs/physical-devices.mdx +0 -76
- package/docs/viewports.mdx +0 -146
package/docs/assertions.mdx
DELETED
|
@@ -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).
|
package/docs/viewports.mdx
DELETED
|
@@ -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>
|