diff --git a/.changeset/browser-install-verdict.md b/.changeset/browser-install-verdict.md new file mode 100644 index 000000000..7f29699fd --- /dev/null +++ b/.changeset/browser-install-verdict.md @@ -0,0 +1,5 @@ +--- +"@e2e-dev/web": patch +--- + +A headless run no longer downloads the full Chromium when the headless shell is installed, and the install no longer removes other browsers in the cache. Set `PLAYWRIGHT_SKIP_BROWSER_GC=0` to restore cleanup. diff --git a/.changeset/cache-empty-typed-value.md b/.changeset/cache-empty-typed-value.md new file mode 100644 index 000000000..a71198858 --- /dev/null +++ b/.changeset/cache-empty-typed-value.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +A recorded step that types an empty value, which clears a field, replays instead of failing with `REPLAY_STALE` (`invalid-entry`). The cache wrote the entry but its reader rejected the empty `type` or `typeText` value, so such a step could never replay from a recording. diff --git a/.changeset/engine-prepare-headed.md b/.changeset/engine-prepare-headed.md new file mode 100644 index 000000000..f8baab3a4 --- /dev/null +++ b/.changeset/engine-prepare-headed.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +`EnginePrepareInfo.headed` is now required, alongside the `headed` an engine already receives on `init`. A host that builds a `prepare` call now passes the run's mode, so an engine provisions for the browser that run launches. diff --git a/.changeset/error-default-redactor.md b/.changeset/error-default-redactor.md new file mode 100644 index 000000000..0a5672b5c --- /dev/null +++ b/.changeset/error-default-redactor.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +Run-level errors in reports (hook, collection, provisioning, and worker failures) are now redacted with the config's secrets. diff --git a/.changeset/evaluate-string-expression.md b/.changeset/evaluate-string-expression.md new file mode 100644 index 000000000..f1207b24a --- /dev/null +++ b/.changeset/evaluate-string-expression.md @@ -0,0 +1,5 @@ +--- +'@e2e-dev/web': patch +--- + +`browser.evaluate` evaluates a string as an expression: `browser.evaluate('document.title')` and `browser.evaluate('(() => document.title)()')` return the title instead of failing with `EVALUATE_FAILED: ... is not a function`. A string that evaluates to a function, such as `'() => document.title'`, is still called with the argument. diff --git a/.changeset/forced-conclusion-wrong-tool.md b/.changeset/forced-conclusion-wrong-tool.md new file mode 100644 index 000000000..81a774a7a --- /dev/null +++ b/.changeset/forced-conclusion-wrong-tool.md @@ -0,0 +1,5 @@ +--- +'e2e': patch +--- + +An agent step whose model answers a forced `complete_step` turn with another tool no longer fails as `MODEL_PROVIDER_FAILED`. The call goes back to the model as an error and the turn is asked again within the step's turn budget; a model that never concludes fails the step with `STEP_NO_CONCLUSION`. diff --git a/.changeset/foreign-engine-errors.md b/.changeset/foreign-engine-errors.md new file mode 100644 index 000000000..4bdb24c64 --- /dev/null +++ b/.changeset/foreign-engine-errors.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +An error an engine throws with a runner code, such as `INVALID_LOCATOR` for an unsupported selector, keeps its code and category when the engine is loaded from `e2e.config.ts`. It was reported as an infrastructure `ENGINE_FAILURE`. diff --git a/.changeset/in-process-stray-rejections.md b/.changeset/in-process-stray-rejections.md new file mode 100644 index 000000000..db9aca57f --- /dev/null +++ b/.changeset/in-process-stray-rejections.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +A run that executes in the host process, such as `e2e explore`, no longer crashes that process when a test leaves a promise rejection or an exception uncaught. It handles them as a worker process does: a rejection fails the test running when it surfaces, or is recorded as `UNHANDLED_REJECTION` between tests. An uncaught exception, or a rejection that surfaces with no test running before any test has finished, is a run error that fails the test on its way with `WORKER_CRASH` and skips the rest of its file. The handlers are removed when the run ends. diff --git a/.changeset/mcp-error-redaction.md b/.changeset/mcp-error-redaction.md new file mode 100644 index 000000000..53f8ed958 --- /dev/null +++ b/.changeset/mcp-error-redaction.md @@ -0,0 +1,5 @@ +--- +'e2e': patch +--- + +`e2e mcp` now redacts secret values from everything `open_session` and `close_session` return and from its log lines. Before, an engine or app error that carried a configured secret, in a failed open or in a `close_session` "Cleanup:" line, reached the client and stderr verbatim. diff --git a/.changeset/mcp-headless-default.md b/.changeset/mcp-headless-default.md new file mode 100644 index 000000000..fcb8d361e --- /dev/null +++ b/.changeset/mcp-headless-default.md @@ -0,0 +1,5 @@ +--- +"e2e": minor +--- + +`e2e mcp` sessions are now headless by default, the same as `e2e run` and `e2e explore`, and the agent decides per session: `open_session {headed: true}` shows the browser or simulator, `headed: false` hides it. `--headed` makes headed the default for sessions that do not say. Before, sessions were headed outside CI and only the server's `--headless` could hide them; `--headless` is still accepted, since it now asks for the default, but it is no longer documented. diff --git a/.changeset/mcp-user-output-redaction.md b/.changeset/mcp-user-output-redaction.md new file mode 100644 index 000000000..861fede6d --- /dev/null +++ b/.changeset/mcp-user-output-redaction.md @@ -0,0 +1,5 @@ +--- +'e2e': patch +--- + +`e2e mcp` now redacts secret values from what your code prints through `console`, `process.stdout`, or `process.stderr`, such as a `console.log` in a project tool or in the config's top-level code. Before, it reached stderr verbatim. Output printed while a config loads appears once the config has loaded; if the load fails, that output is withheld, since the config's secrets are unknown. An uncaught error or unhandled rejection is logged redacted and the server shuts down its sessions and exits with code 1, instead of crashing with the raw error. diff --git a/.changeset/npm-exec-cli-hint.md b/.changeset/npm-exec-cli-hint.md new file mode 100644 index 000000000..a98ba22e1 --- /dev/null +++ b/.changeset/npm-exec-cli-hint.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +Include npm's argument separator in CLI hints so flags such as `--headed` reach the installed `e2e` binary. diff --git a/.changeset/retry-crash-attempts.md b/.changeset/retry-crash-attempts.md new file mode 100644 index 000000000..8757ed80a --- /dev/null +++ b/.changeset/retry-crash-attempts.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +A worker that crashes during a retry no longer drops the attempts before it. Before, a test whose first attempt failed and whose retry crashed the worker showed a single attempt 0 with `WORKER_CRASH`, and the first attempt's error was lost. The report now keeps every finished attempt and adds the crash as the next one. A crash after the last attempt (in an `afterAll`) is recorded on that attempt instead of as a retry. For a serial group, the finished group attempts and the members that finished in the crashed attempt stay in the report, the member that was running fails with `WORKER_CRASH`, and a crash after a member finished fails the group attempt without blaming that member. A forced interrupt keeps the attempts that finished and the verdict they reach, `flaky` included. diff --git a/.changeset/run-user-output-redaction.md b/.changeset/run-user-output-redaction.md new file mode 100644 index 000000000..a156abcab --- /dev/null +++ b/.changeset/run-user-output-redaction.md @@ -0,0 +1,5 @@ +--- +'e2e': patch +--- + +`e2e run` redacts secret values from what the config's top-level code, a test file's top level, and reporters print, as it already did for tests, including what a timer or a reporter the run stopped waiting for prints after the run. Output printed while the config loads appears once it has loaded, and is withheld if the load fails. With the `list` reporter, output printed during the run prints under `stdout | runner` or `stderr | runner`, on stdout like a test's output, so the live view no longer paints over it. diff --git a/.changeset/strict-mode-reresolve.md b/.changeset/strict-mode-reresolve.md new file mode 100644 index 000000000..69907e501 --- /dev/null +++ b/.changeset/strict-mode-reresolve.md @@ -0,0 +1,5 @@ +--- +'@e2e-dev/web': patch +--- + +An action whose locator gains a second match just before it runs, such as a screen transition that briefly shows the old and new page together, no longer fails with `ENGINE_FAILURE` and a Playwright strict mode violation. The locator resolves again: two matches still fail with `LOCATOR_AMBIGUOUS`, and a duplicate that went away lets the action run. A drag whose drop target gains a second match after the press fails with `ACTION_FAILED` and is not repeated. diff --git a/.changeset/swipe-path-duration.md b/.changeset/swipe-path-duration.md new file mode 100644 index 000000000..df8c58dbe --- /dev/null +++ b/.changeset/swipe-path-duration.md @@ -0,0 +1,7 @@ +--- +"e2e": minor +"@e2e-dev/mobile": minor +"@e2e-dev/web": minor +--- + +`screen.swipe({ from, to, duration })` adds an optional `duration` for path swipes, controlling how long the gesture takes in milliseconds. diff --git a/.changeset/tainted-fallback-policy-denied.md b/.changeset/tainted-fallback-policy-denied.md new file mode 100644 index 000000000..45f2269e0 --- /dev/null +++ b/.changeset/tainted-fallback-policy-denied.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +After a secret fill, an agent step whose screen can only be read from a fallback screenshot (`act`, or a judgment with `vision: true`) now fails with `POLICY_DENIED` naming the secret fill, instead of the engine's timeout. diff --git a/.changeset/telemetry-identity.md b/.changeset/telemetry-identity.md new file mode 100644 index 000000000..b3a538709 --- /dev/null +++ b/.changeset/telemetry-identity.md @@ -0,0 +1,5 @@ +--- +"e2e": patch +--- + +Telemetry no longer sends a project id outside git or in a shallow clone, and a coding agent that sets `CI` outside a CI vendor counts as the machine it runs on. EAS Build, Bitrise, Codemagic, and Xcode Cloud are recognized as CI vendors. diff --git a/.changeset/web-hung-page-timeouts.md b/.changeset/web-hung-page-timeouts.md new file mode 100644 index 000000000..41bbc6154 --- /dev/null +++ b/.changeset/web-hung-page-timeouts.md @@ -0,0 +1,6 @@ +--- +'@e2e-dev/web': patch +'e2e': patch +--- + +A page whose script never yields no longer holds a web test until its test timeout: a tap, a locate, or a navigation on it now fails with `ACTION_FAILED` at its action or navigation timeout, also while a trace records. A tap or keystroke cut off that way may have reached the page and is never repeated by a replay. `expect(browser)` matchers on such a page end at their own timeout instead of the action timeout. diff --git a/.changeset/worker-hang-watchdog.md b/.changeset/worker-hang-watchdog.md new file mode 100644 index 000000000..07a7b271a --- /dev/null +++ b/.changeset/worker-hang-watchdog.md @@ -0,0 +1,5 @@ +--- +'e2e': patch +--- + +A test that blocks its worker's event loop (`while (true) {}`) no longer hangs the run until Ctrl-C. When the worker has not responded `cleanupTimeout` (at least 5 s) after the test timeout, the runner kills it, reports the test `timed-out` with `TEST_TIMEOUT`, and runs the rest of the file on a new worker. A worker with a debugger attached when the test starts (an editor's auto-attach, or `NODE_OPTIONS=--inspect=0`) is never killed this way. Runs with an inline config or in-memory tests run in the runner's own process and still hang. diff --git a/.fallowrc.json b/.fallowrc.json index 7df1d7782..92733e667 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -25,6 +25,7 @@ ], "ignorePatterns": [ "docs/**", + "examples/**", "**/next-env.d.ts", "**/.e2e/**", "packages/e2e/tests/tmp-projects/**" diff --git a/.github/filters.yml b/.github/filters.yml index dd3936055..de0304975 100644 --- a/.github/filters.yml +++ b/.github/filters.yml @@ -29,6 +29,7 @@ web: mobile: - *runner - 'packages/mobile/**' + - 'packages/github/**' - 'apps/mobile-benchmark/**' - '.github/workflows/mobile.yml' - '.github/actions/expo-app/**' diff --git a/.oxlintrc.json b/.oxlintrc.json index 5369d67fd..82795af8c 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -16,7 +16,9 @@ "packages/*/tests/fixtures/**", // Vendored conformance data, kept byte for byte. "packages/*/tests/conformance/*/**", - "packages/*/tests/tmp-projects/**" + "packages/*/tests/tmp-projects/**", + // Standalone user-facing projects with their own installs; nothing here builds them. + "examples/**" ], "rules": { // Sequential execution is the contract of a test runner, not a perf bug. diff --git a/AGENTS.md b/AGENTS.md index 493187f2b..19c961701 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,6 +66,14 @@ suites that consume the built packages the way a user would. `e2e/engine` only: the semantics every engine must reproduce (error taxonomy, text and URL matching, assertion polling, JSON-value rules) are exported there, and there is no `e2e/internal` subpath. +- `packages/mobile` — the published `@e2e-dev/mobile` package: the + iOS/Android engine on agent-device, built with the same public + `defineEngine`, contributing the `device` fixture. Like the web engine it + depends on `e2e` (peer), never the reverse; the engine implementation + imports from `e2e/engine` only, and a target names it as `engine: mobile()`. + The `@e2e-dev/mobile/tools` subpath holds the agent-side `open_app`, `swipe`, + and `alert` tools; focused-field typing uses the engine's keyboard grammar. + The main entry never loads the AI SDK. - `packages/kernel` - the published `@e2e-dev/kernel` package: Kernel hosted browsers for the web engine. An official integration with a hosted service is one package per service, named after it (`@e2e-dev/`), with the @@ -77,7 +85,19 @@ suites that consume the built packages the way a user would. hosted iOS simulators and Android emulators for the mobile engine (`DeviceProvider`). Expo publishes no SDK for the sessions API, so it calls Expo's GraphQL API with `fetch`, and `@e2e-dev/mobile` is its only peer. -- `packages/testmu` - the published `@e2e-dev/testmu` package: TestMu AI +- `packages/decision` — the published `@e2e-dev/decision` package: a + `StepExecutor` (`decisionExecutor()`) that drives `agent.act` and + `agent.assert` through an AI SDK *evaluation* model answering `choice` + questions with probability distributions, plus an optional small language + model that writes field values when the decision model picks `type`. + `minProbability` and `minConfidence` gate a chosen operation, target, + secret, or assertion verdict. +- `packages/github` — the published `@e2e-dev/github` package: the reporter + that posts the run as one pull request comment from GitHub Actions and + keeps it current on reruns, writing the same text to the job summary. It + reads the token, event, and repository when the run finishes, never at + config load, and renders the page with `renderMarkdownReport` from `e2e`. +- `packages/testmu` — the published `@e2e-dev/testmu` package: TestMu AI (formerly LambdaTest) hosted Android emulators, iOS simulators, and real devices for the mobile engine (`DeviceProvider`). agent-device's `testmu` cloud provider is the vendor client: the package allocates agent-device @@ -120,6 +140,14 @@ suites that consume the built packages the way a user would. "Committed recordings" under Gotchas. Scenario files are copies: keep diffs against the source minimal, and name no company a scenario was distilled from. +- `examples/` — standalone user-facing projects, one per technology + (`with-vite`, `with-next`, `with-expo`, `with-swiftui`), each the same + one-screen greeter demo with deterministic and agent tests. They install + the published packages from npm, sit outside the pnpm workspace, commit no + lockfile, and run in no CI; oxlint and fallow ignore them. A change runs + the example's suite by hand and updates the "Last checked" line in its + README. A SwiftUI example keeps its tests in an `e2e/` folder beside the + native project, as a user would. - `docs/` (the Mintlify docs site; pages are the `.mdx` files under `docs/`, navigation, theme, and redirects in `docs/docs.json`, extra CSS in `docs/style.css`; `docs/examples/` is typechecked and shown verbatim on @@ -218,7 +246,7 @@ thread handled, and labeled `Ready for Human Review`. "It compiles" and ## Testing quirks -- Vitest 4, `pool: 'forks'`, two projects. `integration` is capped at +- Vitest 5, `pool: 'forks'`, two projects. `integration` is capped at `maxWorkers: 3` and runs in a later group — do not raise it; CPU starvation produces timeouts indistinguishable from real failures. - Integration tests write throwaway projects into diff --git a/README.md b/README.md index ee0caf714..5ccbb722b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ Join the community on Discord

+

+ GitHub Trending Repository of the Day +

+ # e2e [e2e](https://tester.army/e2e?utm_source=e2e&utm_medium=github&utm_campaign=readme_intro) is an end-to-end testing framework for web and mobile apps. Describe a goal in natural language and an agent drives the app to reach it. Check the result with locators and assertions in the same test. @@ -40,6 +44,11 @@ npx e2e init config and an example test. The [quickstart](https://e2e.tester.army/docs/quickstart) covers the rest. +To see a finished setup in your stack, open +[`examples/`](https://github.com/tester-army/e2e/tree/main/examples): +Vite, Next.js, Expo, and SwiftUI, each a standalone project with a passing +suite. + ## Packages | Package | What it does | diff --git a/apps/mobile-benchmark/package.json b/apps/mobile-benchmark/package.json index ff74ce114..7043fa2e3 100644 --- a/apps/mobile-benchmark/package.json +++ b/apps/mobile-benchmark/package.json @@ -16,9 +16,9 @@ "capture:snapshots": "node node_modules/e2e/dist/cli/bin.js run --config e2e.capture.config.ts" }, "dependencies": { - "@react-navigation/bottom-tabs": "7.19.2", - "@react-navigation/native": "7.4.1", - "@react-navigation/native-stack": "7.19.2", + "@react-navigation/bottom-tabs": "7.20.0", + "@react-navigation/native": "7.5.0", + "@react-navigation/native-stack": "7.20.0", "@stripe/stripe-react-native": "0.64.0", "expo": "57.0.22", "expo-audio": "57.0.5", diff --git a/apps/web-benchmark/package.json b/apps/web-benchmark/package.json index 887ee826c..34a73a73b 100644 --- a/apps/web-benchmark/package.json +++ b/apps/web-benchmark/package.json @@ -16,7 +16,7 @@ "dependencies": { "e2e": "workspace:*", "@e2e-dev/web": "workspace:*", - "next": "16.3.6", + "next": "16.3.8", "react": "19.3.0", "react-dom": "19.3.0" }, diff --git a/docs/agent-steps.mdx b/docs/agent-steps.mdx index d8d4d0b61..4e3494913 100644 --- a/docs/agent-steps.mdx +++ b/docs/agent-steps.mdx @@ -1,6 +1,7 @@ --- title: How agent steps work description: What the model sees, what it may do, how a step ends, and what it costs. +icon: "list-check" --- An agent step is one `agent.act`, `agent.assert`, `agent.waitFor`, or diff --git a/docs/agents.mdx b/docs/agents.mdx index 0ddaca16a..66fb7f7ae 100644 --- a/docs/agents.mdx +++ b/docs/agents.mdx @@ -1,6 +1,7 @@ --- title: Agents and personas description: Configure several agents in one suite, pin tests to them, and run one flow as each persona. +icon: "users" --- Define named agents in `agents` to give tests different models, instructions, diff --git a/docs/api-testing.mdx b/docs/api-testing.mdx index 3002e20a2..b9373ff39 100644 --- a/docs/api-testing.mdx +++ b/docs/api-testing.mdx @@ -1,6 +1,7 @@ --- -title: Testing APIs +title: API description: Check your API with fetch and value matchers, in the same files and run as UI tests. +icon: "server" --- API tests run in the same files and the same run as UI tests. A test that diff --git a/docs/assertions.mdx b/docs/assertions.mdx deleted file mode 100644 index 2ab2d4dcc..000000000 --- a/docs/assertions.mdx +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: Assertions -description: Check the screen with agent.assert, wait for a condition, and extract data. ---- - -An assertion checks what is on screen. The agent has three judgment calls: -`assert` checks the screen now, `waitFor` waits until a condition holds, and -`extract` reads structured data. When you know the exact value, use an -`expect` [locator assertion](#locator-assertions) instead, with no model call. - -```ts -await agent.assert('the dashboard shows a trial badge'); -``` - -## `agent.assert` - -`assert` checks a statement against the current screen. A false statement -fails with `ASSERTION_FAILED`. If the screen does not provide enough evidence, -it fails with `ASSERTION_INCONCLUSIVE`. - -The judging model sees the statement and the current screen. It does not see -earlier steps or the acting agent's summaries. - -## `agent.waitFor` - -`assert` checks the screen now. Use `waitFor` when the condition may become true -later. It polls the screen and only calls the model when the observation changes. - -```ts -await agent.waitFor('the export finishes and a download link appears', { - interval: 500, - timeout: 120_000, -}); -``` - -## `agent.extract` - -`extract` reads structured data from the screen. Provide a -[Standard Schema](https://standardschema.dev) validator, such as Zod, to define -the result. - -```ts -import { expect } from 'e2e'; -import { z } from 'zod'; - -const data = await agent.extract('every todo title and how many remain', { - schema: z.object({ titles: z.array(z.string()), remaining: z.number().int() }), -}); -expect(data.titles).toContain('Buy milk'); -``` - - -For model-generated descriptions, check the relevant meaning rather than the -entire sentence. Use a [locator assertion](/locators) when the exact text matters. - - -## Judging pixels - -By default, judgments read a redacted text snapshot of the screen. Add -`vision: true` for visual checks, such as charts or layout. Use -`vision: 'only'` to judge the screenshot alone. - -```ts -await agent.assert('the chart trends upward', { vision: true }); -await agent.assert('the search form is not covered by a banner', { vision: 'only' }); -``` - -Images add input tokens. Screenshots are withheld for the rest of an attempt -after a secret is filled. The [agent reference](/reference/agent#vision) -lists the modes and restrictions. - -## Locator assertions - -`expect` on a locator checks an exact value and calls no model. It retries -until the check passes or the assertion timeout runs out. - -```ts -await expect(screen.getByRole('status')).toHaveText('2 remaining'); -await expect(screen.getByRole('heading', 'Dashboard')).toBeVisible(); -``` - -[Locators](/locators) covers queries and actions. The -[expect reference](/reference/expect#locator-matchers) lists every matcher. - -## Next - - - - Exact actions and checks with `screen` and `expect`. - - - Verdicts, exit codes, and what the model sees. - - diff --git a/docs/authentication.mdx b/docs/authentication.mdx index 9a1cabd9e..aabeb896d 100644 --- a/docs/authentication.mdx +++ b/docs/authentication.mdx @@ -1,6 +1,7 @@ --- title: Signing in description: Sign in once, reuse the session across tests, and pass secrets without exposing them to the model. +icon: "key" --- Sign in once in a setup test and save a named session. Other tests restore @@ -164,7 +165,7 @@ runner keeps it away from test code and the model altogether. A `Secret` has no `.value`. Three sinks accept one: the two below in a test, and an engine option that declares it, as -[`basicAuth`](/starting-your-app#protected-previews) does: +[`basicAuth`](/web#protected-previews) does: @@ -279,7 +280,7 @@ account, a username with the secrets that sign it in, while a secret is one value, so `credentials.admin` and `secrets.admin` can both exist. In `e2e.config.ts`, `secrets.get()` hands an engine option a secret it needs -before any test runs, such as a [basic-auth password](/starting-your-app#protected-previews). +before any test runs, such as a [basic-auth password](/web#protected-previews). The config load checks the name, and the value is redacted once the engine resolves it, together with the base64 `Authorization` credential it becomes. The protection is text only: screenshots and the model's pixels are kept, so diff --git a/docs/browser.mdx b/docs/browser.mdx index 1079652bb..ec77a493c 100644 --- a/docs/browser.mdx +++ b/docs/browser.mdx @@ -1,6 +1,7 @@ --- title: Working with the browser description: Mock an API, handle a dialog, download a file, reach into an iframe, and pick a browser. +icon: "window-maximize" --- `@e2e-dev/web` runs tests in Chromium, Firefox, and WebKit. It is built on @@ -144,7 +145,7 @@ await browser.setCookies([{ url: 'http://localhost:3000', name: 'tz', value: 'UT A cookie URL that does not parse, or uses a scheme other than `http:` or `https:` (`about:blank` included), is `POLICY_DENIED`; a domain cookie is checked as the origin it would be sent to. If any entry is refused, no cookies are set. To resize -the page, see [Testing viewports](/viewports). +the page, see [Viewports](/web#viewports). ## Pick a browser @@ -183,7 +184,7 @@ bunx @e2e-dev/web install webkit --with-deps `connect` attaches to a remote Chromium over CDP instead of launching one; a [browser provider](#hosted-browsers) does the same and also leases -and releases the browser. [Testing viewports](/viewports) covers sizes. +and releases the browser. [Viewports](/web#viewports) covers sizes. ## Hosted browsers @@ -198,7 +199,7 @@ provider, for [TestMu AI](/integrations/testmu-browsers), `testmuBrowsers()` from `@e2e-dev/testmu/web` is the provider, and for any other service a provider is a few dozen lines in your project. Add `viewport: null` when the service shows the window in a live view: the page then -[fills the window](/viewports#fill-the-window) instead of a fixed 1280 by +[fills the window](/web#fill-the-window) instead of a fixed 1280 by 720 area of it. ```ts @@ -286,7 +287,7 @@ same browser. A provider against a typical session API has three requests: create a session, poll it until it exposes a DevTools endpoint, stop it. -```ts title="browser-provider.ts" +```ts title="browser-provider.ts" expandable import type { BrowserProvider } from '@e2e-dev/web'; /** @@ -471,7 +472,7 @@ root declared in HTML (`