@itmetsam/samurai-framework 0.1.0 → 0.2.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 +129 -0
- package/bin/samurai.mjs +31 -0
- package/dist/api.js +3 -0
- package/dist/assert/assertion-error.d.ts +23 -0
- package/dist/assert/assertion-error.js +45 -0
- package/dist/assert/expect.d.ts +55 -0
- package/dist/assert/expect.js +188 -0
- package/dist/browser/browser-context.d.ts +91 -0
- package/dist/browser/browser-context.js +146 -0
- package/dist/browser/browser-finder.d.ts +2 -0
- package/dist/browser/browser-finder.js +95 -0
- package/dist/browser/browser.d.ts +95 -0
- package/dist/browser/browser.js +372 -0
- package/dist/browser/context-tree.d.ts +23 -0
- package/dist/browser/context-tree.js +60 -0
- package/dist/browser/dialog.d.ts +37 -0
- package/dist/browser/dialog.js +61 -0
- package/dist/browser/download.d.ts +71 -0
- package/dist/browser/download.js +137 -0
- package/dist/browser/downloads-dir.d.ts +8 -0
- package/dist/browser/downloads-dir.js +38 -0
- package/dist/browser/emulation.d.ts +61 -0
- package/dist/browser/emulation.js +144 -0
- package/dist/browser/file-chooser.d.ts +39 -0
- package/dist/browser/file-chooser.js +86 -0
- package/dist/browser/file-paths.d.ts +4 -0
- package/dist/browser/file-paths.js +18 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/navigation-error.d.ts +13 -0
- package/dist/browser/navigation-error.js +19 -0
- package/dist/browser/page-logs.d.ts +86 -0
- package/dist/browser/page-logs.js +149 -0
- package/dist/browser/page-wait.d.ts +9 -0
- package/dist/browser/page-wait.js +17 -0
- package/dist/browser/page.d.ts +370 -0
- package/dist/browser/page.js +1038 -0
- package/dist/browser/pdf.d.ts +72 -0
- package/dist/browser/pdf.js +37 -0
- package/dist/browser/screenshot.d.ts +29 -0
- package/dist/browser/screenshot.js +42 -0
- package/dist/cli/args.d.ts +35 -0
- package/dist/cli/args.js +69 -0
- package/dist/cli/cli.cli-test.d.ts +1 -0
- package/dist/cli/cli.cli-test.js +170 -0
- package/dist/cli/console-reporter.d.ts +15 -0
- package/dist/cli/console-reporter.js +52 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +61 -0
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.js +156 -0
- package/dist/config/config.d.ts +24 -0
- package/dist/config/config.js +42 -0
- package/dist/config/default.d.ts +2 -0
- package/dist/config/default.js +3 -0
- package/dist/config/mask.d.ts +11 -0
- package/dist/config/mask.js +85 -0
- package/dist/config/run-settings.d.ts +28 -0
- package/dist/config/run-settings.js +61 -0
- package/dist/config/secrets.d.ts +10 -0
- package/dist/config/secrets.js +67 -0
- package/dist/config/variables.d.ts +11 -0
- package/dist/config/variables.js +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/locator/action-timeout-error.d.ts +25 -0
- package/dist/locator/action-timeout-error.js +35 -0
- package/dist/locator/element-state.d.ts +44 -0
- package/dist/locator/element-state.js +109 -0
- package/dist/locator/label-locator.d.ts +13 -0
- package/dist/locator/label-locator.js +49 -0
- package/dist/locator/locator.d.ts +168 -0
- package/dist/locator/locator.js +591 -0
- package/dist/locator/selector-errors.d.ts +9 -0
- package/dist/locator/selector-errors.js +15 -0
- package/dist/locator/selector.d.ts +60 -0
- package/dist/locator/selector.js +113 -0
- package/dist/locator/text-locator.d.ts +13 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.js +30 -0
- package/dist/network/data-collector.d.ts +26 -0
- package/dist/network/data-collector.js +77 -0
- package/dist/network/network-tracker.d.ts +94 -0
- package/dist/network/network-tracker.js +198 -0
- package/dist/network/network-wait.d.ts +16 -0
- package/dist/network/network-wait.js +24 -0
- package/dist/network/response.d.ts +22 -0
- package/dist/network/response.js +42 -0
- package/dist/network/router.d.ts +120 -0
- package/dist/network/router.js +343 -0
- package/dist/network/url-match.d.ts +8 -0
- package/dist/network/url-match.js +38 -0
- package/dist/recorder/apply.d.ts +3 -0
- package/dist/recorder/apply.js +10 -0
- package/dist/recorder/capture.d.ts +18 -0
- package/dist/recorder/capture.js +198 -0
- package/dist/recorder/index.d.ts +3 -0
- package/dist/recorder/index.js +3 -0
- package/dist/recorder/normalise.d.ts +68 -0
- package/dist/recorder/normalise.js +104 -0
- package/dist/recorder/ranking.d.ts +9 -0
- package/dist/recorder/ranking.js +21 -0
- package/dist/recorder/recorder.d.ts +52 -0
- package/dist/recorder/recorder.js +143 -0
- package/dist/recorder/session.d.ts +49 -0
- package/dist/recorder/session.js +113 -0
- package/dist/runner/module-type.d.ts +6 -0
- package/dist/runner/module-type.js +29 -0
- package/dist/runner/page-logs-report.d.ts +40 -0
- package/dist/runner/page-logs-report.js +44 -0
- package/dist/runner/prepare-run.d.ts +14 -0
- package/dist/runner/prepare-run.js +20 -0
- package/dist/runner/project.d.ts +17 -0
- package/dist/runner/project.js +33 -0
- package/dist/runner/registry.d.ts +7 -0
- package/dist/runner/registry.js +40 -0
- package/dist/runner/reporter.d.ts +43 -0
- package/dist/runner/reporter.js +83 -0
- package/dist/runner/run.d.ts +27 -0
- package/dist/runner/run.js +34 -0
- package/dist/runner/test-error.d.ts +3 -0
- package/dist/runner/test-error.js +18 -0
- package/dist/runner/test-runner.d.ts +44 -0
- package/dist/runner/test-runner.js +205 -0
- package/dist/script/call-function.d.ts +34 -0
- package/dist/script/call-function.js +71 -0
- package/dist/script/element-handle.d.ts +10 -0
- package/{src/script/element-handle.ts → dist/script/element-handle.js} +4 -4
- package/dist/script/helpers.d.ts +28 -0
- package/dist/script/helpers.js +50 -0
- package/dist/script/preload.d.ts +31 -0
- package/dist/script/preload.js +71 -0
- package/dist/script/serialize.d.ts +29 -0
- package/dist/script/serialize.js +207 -0
- package/dist/steps/build.d.ts +5 -0
- package/dist/steps/build.js +38 -0
- package/dist/steps/edit.d.ts +30 -0
- package/dist/steps/edit.js +80 -0
- package/dist/steps/emit.d.ts +6 -0
- package/dist/steps/emit.js +73 -0
- package/dist/steps/index.d.ts +5 -0
- package/dist/steps/index.js +4 -0
- package/dist/steps/model.d.ts +103 -0
- package/dist/steps/model.js +1 -0
- package/dist/steps/parse.d.ts +3 -0
- package/dist/steps/parse.js +272 -0
- package/dist/transport/bidi-connection.d.ts +46 -0
- package/dist/transport/bidi-connection.js +176 -0
- package/dist/transport/bidi-error.d.ts +10 -0
- package/dist/transport/bidi-error.js +10 -0
- package/dist/transport/subscriptions.d.ts +36 -0
- package/dist/transport/subscriptions.js +81 -0
- package/{src → dist}/types/test.d.ts +10 -4
- package/dist/wait/wait-until.d.ts +20 -0
- package/dist/wait/wait-until.js +63 -0
- package/package.json +44 -7
- package/.claude/settings.local.json +0 -7
- package/.env.example +0 -3
- package/.github/actions/setup/action.yml +0 -16
- package/.github/workflows/ci.yml +0 -71
- package/.trunk/configs/.markdownlint.yaml +0 -2
- package/.trunk/configs/.yamllint.yaml +0 -7
- package/.trunk/configs/svgo.config.mjs +0 -14
- package/.trunk/trunk.yaml +0 -34
- package/docs/superpowers/bidi-roadmap.md +0 -60
- package/docs/superpowers/plans/2026-09-29-auto-wait.md +0 -1827
- package/docs/superpowers/plans/2026-09-29-expect.md +0 -1350
- package/docs/superpowers/plans/2026-10-01-ci.md +0 -350
- package/docs/superpowers/plans/2026-10-01-test-api-alignment.md +0 -1821
- package/docs/superpowers/recorder-roadmap.md +0 -52
- package/docs/superpowers/specs/2026-09-29-auto-wait-design.md +0 -185
- package/docs/superpowers/specs/2026-09-29-bidi-foundation-design.md +0 -130
- package/docs/superpowers/specs/2026-09-29-contexts-and-storage-design.md +0 -88
- package/docs/superpowers/specs/2026-09-29-downloads-and-file-dialogs-design.md +0 -94
- package/docs/superpowers/specs/2026-09-29-emulation-design.md +0 -88
- package/docs/superpowers/specs/2026-09-29-expect-design.md +0 -130
- package/docs/superpowers/specs/2026-09-29-locate-nodes-design.md +0 -90
- package/docs/superpowers/specs/2026-09-29-logs-and-errors-design.md +0 -87
- package/docs/superpowers/specs/2026-09-29-navigation-waits-design.md +0 -83
- package/docs/superpowers/specs/2026-09-29-network-data-design.md +0 -78
- package/docs/superpowers/specs/2026-09-29-network-interception-design.md +0 -91
- package/docs/superpowers/specs/2026-09-29-network-tracking-design.md +0 -86
- package/docs/superpowers/specs/2026-09-29-page-api-design.md +0 -90
- package/docs/superpowers/specs/2026-09-29-preload-scripts-design.md +0 -82
- package/docs/superpowers/specs/2026-09-29-script-call-function-design.md +0 -107
- package/docs/superpowers/specs/2026-09-29-set-files-design.md +0 -62
- package/docs/superpowers/specs/2026-09-29-user-prompts-design.md +0 -75
- package/docs/superpowers/specs/2026-10-01-ci-design.md +0 -106
- package/docs/superpowers/specs/2026-10-01-test-api-alignment-design.md +0 -241
- package/public/logo-dark.svg +0 -236
- package/public/logo-light.svg +0 -262
- package/samurai.config.ts +0 -7
- package/src/assert/assertion-error.test.ts +0 -55
- package/src/assert/assertion-error.ts +0 -62
- package/src/assert/expect-locator.test.ts +0 -217
- package/src/assert/expect.test.ts +0 -73
- package/src/assert/expect.ts +0 -299
- package/src/browser/browser-context.browser-test.ts +0 -201
- package/src/browser/browser-context.test.ts +0 -407
- package/src/browser/browser-context.ts +0 -208
- package/src/browser/browser-finder.ts +0 -110
- package/src/browser/browser.test.ts +0 -88
- package/src/browser/browser.ts +0 -515
- package/src/browser/context-tree.test.ts +0 -86
- package/src/browser/context-tree.ts +0 -70
- package/src/browser/dialog.browser-test.ts +0 -144
- package/src/browser/dialog.test.ts +0 -322
- package/src/browser/dialog.ts +0 -83
- package/src/browser/download.test.ts +0 -161
- package/src/browser/download.ts +0 -172
- package/src/browser/downloads-dir.test.ts +0 -75
- package/src/browser/downloads-dir.ts +0 -39
- package/src/browser/emulation.browser-test.ts +0 -186
- package/src/browser/emulation.test.ts +0 -200
- package/src/browser/emulation.ts +0 -204
- package/src/browser/file-chooser.test.ts +0 -142
- package/src/browser/file-chooser.ts +0 -99
- package/src/browser/file-paths.ts +0 -18
- package/src/browser/goto.browser-test.ts +0 -44
- package/src/browser/navigation-error.ts +0 -26
- package/src/browser/navigation.browser-test.ts +0 -172
- package/src/browser/page-api.browser-test.ts +0 -228
- package/src/browser/page-logs.browser-test.ts +0 -119
- package/src/browser/page-logs.test.ts +0 -212
- package/src/browser/page-logs.ts +0 -208
- package/src/browser/page-wait.ts +0 -19
- package/src/browser/page.test.ts +0 -1567
- package/src/browser/page.ts +0 -1176
- package/src/browser/pdf.ts +0 -62
- package/src/browser/screenshot.ts +0 -65
- package/src/browser/transfers.browser-test.ts +0 -180
- package/src/config/config.ts +0 -33
- package/src/config/default.ts +0 -5
- package/src/config/fixtures.test.ts +0 -137
- package/src/config/mask.test.ts +0 -143
- package/src/config/mask.ts +0 -87
- package/src/config/run-settings.test.ts +0 -163
- package/src/config/run-settings.ts +0 -114
- package/src/config/secrets.ts +0 -70
- package/src/config/variables.ts +0 -31
- package/src/index.ts +0 -10
- package/src/locator/action-timeout-error.test.ts +0 -83
- package/src/locator/action-timeout-error.ts +0 -72
- package/src/locator/element-state.test.ts +0 -181
- package/src/locator/element-state.ts +0 -159
- package/src/locator/locate-nodes.browser-test.ts +0 -169
- package/src/locator/locator-actions.test.ts +0 -355
- package/src/locator/locator-chain.test.ts +0 -292
- package/src/locator/locator.browser-test.ts +0 -137
- package/src/locator/locator.test.ts +0 -179
- package/src/locator/locator.ts +0 -656
- package/src/locator/selector-errors.ts +0 -18
- package/src/locator/selector.test.ts +0 -124
- package/src/locator/selector.ts +0 -107
- package/src/locator/set-files.browser-test.ts +0 -141
- package/src/locator/set-files.test.ts +0 -191
- package/src/locator/text-locator.test.ts +0 -118
- package/src/logger/index.test.ts +0 -28
- package/src/logger/index.ts +0 -40
- package/src/network/data-collector.test.ts +0 -111
- package/src/network/data-collector.ts +0 -92
- package/src/network/network-data.browser-test.ts +0 -192
- package/src/network/network-tracker.browser-test.ts +0 -151
- package/src/network/network-tracker.test.ts +0 -307
- package/src/network/network-tracker.ts +0 -286
- package/src/network/network-wait.ts +0 -28
- package/src/network/response.test.ts +0 -93
- package/src/network/response.ts +0 -58
- package/src/network/router.browser-test.ts +0 -260
- package/src/network/router.test.ts +0 -528
- package/src/network/router.ts +0 -420
- package/src/network/url-match.test.ts +0 -69
- package/src/network/url-match.ts +0 -41
- package/src/runner/page-logs-report.test.ts +0 -144
- package/src/runner/page-logs-report.ts +0 -82
- package/src/runner/prepare-run.test.ts +0 -33
- package/src/runner/prepare-run.ts +0 -36
- package/src/runner/registry.test.ts +0 -59
- package/src/runner/registry.ts +0 -46
- package/src/runner/reporter.test.ts +0 -73
- package/src/runner/reporter.ts +0 -109
- package/src/runner/test-error.test.ts +0 -39
- package/src/runner/test-error.ts +0 -20
- package/src/runner/test-runner.ts +0 -184
- package/src/script/call-function.browser-test.ts +0 -112
- package/src/script/call-function.test.ts +0 -174
- package/src/script/call-function.ts +0 -113
- package/src/script/helpers.test.ts +0 -116
- package/src/script/helpers.ts +0 -68
- package/src/script/preload.browser-test.ts +0 -144
- package/src/script/preload.test.ts +0 -180
- package/src/script/preload.ts +0 -102
- package/src/script/serialize.test.ts +0 -182
- package/src/script/serialize.ts +0 -203
- package/src/testing/browser-fixture.ts +0 -18
- package/src/testing/fake-websocket.ts +0 -72
- package/src/testing/stub-connector.ts +0 -154
- package/src/tests/index.spec.ts +0 -30
- package/src/transport/bidi-connection.test.ts +0 -155
- package/src/transport/bidi-connection.ts +0 -256
- package/src/transport/bidi-error.ts +0 -23
- package/src/transport/foundation.browser-test.ts +0 -73
- package/src/transport/subscriptions.test.ts +0 -140
- package/src/transport/subscriptions.ts +0 -110
- package/src/types/bidi-commands.test-d.ts +0 -55
- package/src/wait/wait-until.test.ts +0 -117
- package/src/wait/wait-until.ts +0 -86
- package/tsconfig.json +0 -44
- /package/{src/api.ts → dist/api.d.ts} +0 -0
- /package/{src/locator/text-locator.ts → dist/locator/text-locator.js} +0 -0
- /package/{src → dist}/types/bidi-modules/browser.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/browsing-context.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/emulation.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/input.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/log.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/network.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/permissions.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/script.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/session.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/storage.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/web-extensions.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/command.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/error.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/event.d.ts +0 -0
- /package/{src → dist}/types/bidi.d.ts +0 -0
- /package/{src → dist}/types/browser.d.ts +0 -0
- /package/{src → dist}/types/config.d.ts +0 -0
- /package/{src → dist}/types/env.d.ts +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Samurai
|
|
2
|
+
|
|
3
|
+
A browser test framework written in TypeScript. It drives a real browser over the [WebDriver BiDi](https://www.w3.org/TR/webdriver-bidi/) protocol, with no WebDriver server and no Playwright or Puppeteer underneath.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { expect, test } from "../api.js";
|
|
7
|
+
|
|
8
|
+
test("Visitor can sign in", async ({ page, env, secrets }) => {
|
|
9
|
+
await page.goto("/login");
|
|
10
|
+
await page.getByLabel("Email").fill(env.testUser as string);
|
|
11
|
+
await page.getByLabel("Password").fill(secrets.TEST_PASSWORD as string);
|
|
12
|
+
await page.getByRole("button", { name: "Sign in" }).click();
|
|
13
|
+
await expect(page.getByTestId("welcome")).toHaveText("Welcome back");
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **Auto-waiting.** Actions wait until the element is attached, visible, stable, enabled and not covered. Assertions retry until they pass or time out.
|
|
18
|
+
- **Semantic locators.** By role, label, text, test id, CSS or XPath, with ordered fallbacks.
|
|
19
|
+
- **Environments and secrets.** One test file runs against `dev`, `staging` or `production`; secrets come from the environment or a `.env.<name>` file and are masked in logs and reports.
|
|
20
|
+
- **Network control.** Mock, block or modify requests; wait for requests, responses and network idle; read response bodies.
|
|
21
|
+
- **Recorder building blocks.** Specs are parsed into steps and edited back with minimal diffs, and an engine records what a person does in the browser. The UI that ties them together is not built yet (see the [roadmap](docs/roadmap.md)).
|
|
22
|
+
|
|
23
|
+
> **Status:** early. Firefox only, run from a checkout of this repository (not published to npm yet). See [what is still open](docs/roadmap.md).
|
|
24
|
+
|
|
25
|
+
## Getting started
|
|
26
|
+
|
|
27
|
+
### 1. Requirements
|
|
28
|
+
|
|
29
|
+
- [Node.js](https://nodejs.org) 26 or newer and [Bun](https://bun.sh) 1.3 or newer (CI uses these versions)
|
|
30
|
+
- Firefox installed on the machine. It is found in the usual install locations on Linux, macOS and Windows.
|
|
31
|
+
|
|
32
|
+
### 2. Install
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
git clone https://github.com/degrootsam/samurai-framework.git
|
|
36
|
+
cd samurai-framework
|
|
37
|
+
bun install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. Write a test
|
|
41
|
+
|
|
42
|
+
Tests live in the folder `srcDir` points to (`./src/tests` in `samurai.config.ts`) and end in `.spec.ts`. Create `src/tests/example.spec.ts`:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { expect, test } from "../api.js";
|
|
46
|
+
|
|
47
|
+
test("example.com has its heading", async ({ page }) => {
|
|
48
|
+
await page.goto("https://example.com");
|
|
49
|
+
await expect(
|
|
50
|
+
page.getByRole("heading", { name: "Example Domain" }),
|
|
51
|
+
).toBeVisible();
|
|
52
|
+
await page.getByText("More information", { match: "partial" }).click();
|
|
53
|
+
await page.waitForLoadState();
|
|
54
|
+
expect(await page.url()).toContain("iana.org");
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 4. Configure
|
|
59
|
+
|
|
60
|
+
`samurai.config.ts` in the project root:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { defineConfig } from "./src/config/config.js";
|
|
64
|
+
|
|
65
|
+
export default defineConfig({
|
|
66
|
+
srcDir: "./src/tests",
|
|
67
|
+
browser: "firefox",
|
|
68
|
+
timeout: 30000,
|
|
69
|
+
environments: {
|
|
70
|
+
staging: { baseURL: "https://staging.example.com" },
|
|
71
|
+
production: { baseURL: "https://www.example.com" },
|
|
72
|
+
},
|
|
73
|
+
defaultEnvironment: "staging",
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
With `baseURL` set, `page.goto("/login")` opens `https://staging.example.com/login`.
|
|
78
|
+
|
|
79
|
+
### 5. Run
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
bun run dev # every *.spec.ts under srcDir, in the default environment
|
|
83
|
+
bun run dev --env production # another environment (or SAMURAI_ENV=production)
|
|
84
|
+
bun run dev --timeout 60000 # per-test timeout in ms; also --expect-timeout
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Once the package is installed in a project, `samurai run` does the same with a readable summary and a proper exit code, and `samurai init` scaffolds a new project (see [Command line](docs/cli.md)).
|
|
88
|
+
|
|
89
|
+
A browser window opens for each test and closes when the test ends. The result is written to `result/report.json` (status, duration and error per test); nothing summarises it on the console yet. The process exits with code 0 when every test passed and 1 otherwise (add `--headless` to hide the window). Framework logs go to the console and `logs/`.
|
|
90
|
+
|
|
91
|
+
> `src/tests/index.spec.ts` is an example that submits a real contact form. Delete it or replace it before running everything.
|
|
92
|
+
|
|
93
|
+
### 6. Secrets (optional)
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
cp .env.example .env.staging
|
|
97
|
+
# SAMURAI_SECRET_TEST_PASSWORD=...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Read it in a test as `secrets.TEST_PASSWORD`. Real environment variables win over the file, and `.env.*` files are git-ignored. See [Environments and secrets](docs/environments-and-secrets.md).
|
|
101
|
+
|
|
102
|
+
## Documentation
|
|
103
|
+
|
|
104
|
+
| Guide | What it covers |
|
|
105
|
+
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| [Writing tests](docs/writing-tests.md) | `test`, `describe`, fixtures, how a test passes or fails |
|
|
107
|
+
| [Locators](docs/locators.md) | Finding elements, chaining, fallbacks, actions and their auto-waiting |
|
|
108
|
+
| [Assertions](docs/assertions.md) | `expect` for elements and for plain values |
|
|
109
|
+
| [Pages and browsers](docs/pages-and-browsers.md) | Navigation, waits, screenshots, PDF, viewport, emulation, dialogs, downloads, contexts, cookies |
|
|
110
|
+
| [Network](docs/network.md) | Waiting for traffic, mocking, blocking, response bodies |
|
|
111
|
+
| [Configuration](docs/configuration.md) | Every option in `samurai.config.ts`, CLI flags and precedence |
|
|
112
|
+
| [Environments and secrets](docs/environments-and-secrets.md) | Per-environment settings, variables, secrets and masking |
|
|
113
|
+
| [Command line](docs/cli.md) | `samurai run`, `list`, `record` and `init`, options, output, exit codes, `--json` |
|
|
114
|
+
| [Running tests and reports](docs/running-and-reports.md) | What a run does, `report.json`, page logs, timeouts |
|
|
115
|
+
| [Steps and the recorder](docs/recorder.md) | The step codec and the recording engine |
|
|
116
|
+
| [Embedding the framework](docs/embedding.md) | Using the package from another app (such as the Electron UI): build, entry points, `runTests`, events |
|
|
117
|
+
| [Contributing](docs/contributing.md) | Project layout, running the framework's own tests, conventions |
|
|
118
|
+
| [Roadmap](docs/roadmap.md) | What is built and what is still open |
|
|
119
|
+
|
|
120
|
+
Design notes for every feature live in [`docs/superpowers/specs`](docs/superpowers/specs).
|
|
121
|
+
|
|
122
|
+
## Scripts
|
|
123
|
+
|
|
124
|
+
| Command | Does |
|
|
125
|
+
| ---------------------- | ----------------------------------------------- |
|
|
126
|
+
| `bun run dev` | Runs the test suite (`tsx src/index.ts`) |
|
|
127
|
+
| `bun run test` | Unit tests of the framework (no browser needed) |
|
|
128
|
+
| `bun run test:browser` | Browser tests of the framework (needs Firefox) |
|
|
129
|
+
| `bun run build` | Typechecks and compiles to `dist/` |
|
package/bin/samurai.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Quiet the framework's console logging unless the caller asked for more; the log files keep everything
|
|
3
|
+
process.env.SAMURAI_LOG_LEVEL ??= "warn";
|
|
4
|
+
|
|
5
|
+
// Specs are TypeScript: let this process import them. tsx is an optional peer dependency of the package
|
|
6
|
+
try {
|
|
7
|
+
const { register } = await import("tsx/esm/api");
|
|
8
|
+
register();
|
|
9
|
+
} catch (err) {
|
|
10
|
+
if (err?.code !== "ERR_MODULE_NOT_FOUND") throw err;
|
|
11
|
+
process.stderr.write(
|
|
12
|
+
"samurai loads TypeScript specs with tsx, which is not installed. Install it: npm install --save-dev tsx\n",
|
|
13
|
+
);
|
|
14
|
+
process.exit(2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { main } = await import("../dist/cli/main.js");
|
|
18
|
+
// Ctrl+C ends a recording cleanly (the browser is closed, the file is complete); a second one stops at once
|
|
19
|
+
const interrupt = new AbortController();
|
|
20
|
+
process.on("SIGINT", () => {
|
|
21
|
+
if (interrupt.signal.aborted) process.exit(130);
|
|
22
|
+
interrupt.abort();
|
|
23
|
+
});
|
|
24
|
+
const code = await main(process.argv.slice(2), {
|
|
25
|
+
out: (text) => process.stdout.write(text),
|
|
26
|
+
err: (text) => process.stderr.write(text),
|
|
27
|
+
color: Boolean(process.stdout.isTTY) && !process.env.NO_COLOR,
|
|
28
|
+
env: process.env,
|
|
29
|
+
signal: interrupt.signal,
|
|
30
|
+
});
|
|
31
|
+
process.exit(code);
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Formats a value for assertion messages */
|
|
2
|
+
export declare function format(value: unknown): string;
|
|
3
|
+
/**
|
|
4
|
+
* Converts a value into something JSON can store in the report: regexps, bigints,
|
|
5
|
+
* symbols, functions and anything else JSON.stringify rejects (e.g. circular objects)
|
|
6
|
+
* become their `format()` string; other values are kept as-is.
|
|
7
|
+
*/
|
|
8
|
+
export declare function toReportValue(value: unknown): unknown;
|
|
9
|
+
export interface AssertionErrorDetails {
|
|
10
|
+
/** Matcher name, prefixed with `not.` when negated */
|
|
11
|
+
matcher: string;
|
|
12
|
+
expected: unknown;
|
|
13
|
+
actual: unknown;
|
|
14
|
+
/** XPath of the asserted locator, only for locator assertions */
|
|
15
|
+
locator?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare class AssertionError extends Error {
|
|
18
|
+
readonly matcher: string;
|
|
19
|
+
readonly expected: unknown;
|
|
20
|
+
readonly actual: unknown;
|
|
21
|
+
readonly locator: string | undefined;
|
|
22
|
+
constructor({ matcher, expected, actual, locator }: AssertionErrorDetails);
|
|
23
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { inspect } from "node:util";
|
|
2
|
+
/** Formats a value for assertion messages */
|
|
3
|
+
export function format(value) {
|
|
4
|
+
if (typeof value === "string")
|
|
5
|
+
return JSON.stringify(value);
|
|
6
|
+
if (value instanceof RegExp)
|
|
7
|
+
return value.toString();
|
|
8
|
+
return inspect(value, { depth: 2 });
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Converts a value into something JSON can store in the report: regexps, bigints,
|
|
12
|
+
* symbols, functions and anything else JSON.stringify rejects (e.g. circular objects)
|
|
13
|
+
* become their `format()` string; other values are kept as-is.
|
|
14
|
+
*/
|
|
15
|
+
export function toReportValue(value) {
|
|
16
|
+
if (value instanceof RegExp ||
|
|
17
|
+
typeof value === "bigint" ||
|
|
18
|
+
typeof value === "symbol" ||
|
|
19
|
+
typeof value === "function") {
|
|
20
|
+
return format(value);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
JSON.stringify(value);
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return format(value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class AssertionError extends Error {
|
|
31
|
+
constructor({ matcher, expected, actual, locator }) {
|
|
32
|
+
const subject = locator === undefined ? "value" : "locator";
|
|
33
|
+
const lines = [`expect(${subject}).${matcher}`];
|
|
34
|
+
if (locator !== undefined) {
|
|
35
|
+
lines.push(` locator: ${locator}`);
|
|
36
|
+
}
|
|
37
|
+
lines.push(` expected: ${format(expected)}`, ` received: ${format(actual)}`);
|
|
38
|
+
super(lines.join("\n"));
|
|
39
|
+
this.name = "AssertionError";
|
|
40
|
+
this.matcher = matcher;
|
|
41
|
+
this.expected = expected;
|
|
42
|
+
this.actual = actual;
|
|
43
|
+
this.locator = locator;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Locator from "../locator/locator.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the matcher names of locator assertions that were never awaited and are
|
|
4
|
+
* still running or already failed, and stops tracking them. The runner calls this
|
|
5
|
+
* after a test to detect a missing `await`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function takePendingAssertions(): string[];
|
|
8
|
+
export interface LocatorAssertionOptions {
|
|
9
|
+
/** Time (ms) to keep retrying. Defaults to config `expect.timeout`, then 5000 */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class ValueAssertions<T> {
|
|
13
|
+
private readonly actual;
|
|
14
|
+
private readonly negated;
|
|
15
|
+
constructor(actual: T, negated?: boolean);
|
|
16
|
+
/** Inverts the next matcher */
|
|
17
|
+
get not(): ValueAssertions<T>;
|
|
18
|
+
private assert;
|
|
19
|
+
private compare;
|
|
20
|
+
/** Strict identity (`Object.is`) */
|
|
21
|
+
toBe(expected: unknown): void;
|
|
22
|
+
/** Deep structural equality */
|
|
23
|
+
toEqual(expected: unknown): void;
|
|
24
|
+
toBeTruthy(): void;
|
|
25
|
+
toBeFalsy(): void;
|
|
26
|
+
/** Array includes `item`, or string contains substring `item` */
|
|
27
|
+
toContain(item: unknown): void;
|
|
28
|
+
/** String contains `pattern` (string) or matches it (RegExp) */
|
|
29
|
+
toMatch(pattern: string | RegExp): void;
|
|
30
|
+
toBeGreaterThan(expected: number | bigint): void;
|
|
31
|
+
toBeGreaterThanOrEqual(expected: number | bigint): void;
|
|
32
|
+
toBeLessThan(expected: number | bigint): void;
|
|
33
|
+
toBeLessThanOrEqual(expected: number | bigint): void;
|
|
34
|
+
}
|
|
35
|
+
export declare class LocatorAssertions {
|
|
36
|
+
private readonly locator;
|
|
37
|
+
private readonly negated;
|
|
38
|
+
constructor(locator: Locator, negated?: boolean);
|
|
39
|
+
/** Inverts the next matcher: it retries until the condition is false */
|
|
40
|
+
get not(): LocatorAssertions;
|
|
41
|
+
/** Re-reads the element until `check` passes or the timeout expires */
|
|
42
|
+
private assertEventually;
|
|
43
|
+
toBeVisible(options?: LocatorAssertionOptions): Promise<void>;
|
|
44
|
+
/** Trimmed text content equals `expected` (string) or matches it (RegExp) */
|
|
45
|
+
toHaveText(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
46
|
+
/** Text content contains `expected` (string) or matches it (RegExp) */
|
|
47
|
+
toContainText(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
48
|
+
toHaveValue(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
49
|
+
toHaveAttribute(name: string, expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
50
|
+
toHaveCount(expected: number, options?: LocatorAssertionOptions): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
/** Auto-retrying assertions on a page element */
|
|
53
|
+
export declare function expect(locator: Locator): LocatorAssertions;
|
|
54
|
+
/** Synchronous assertions on a plain value */
|
|
55
|
+
export declare function expect<T>(value: T): ValueAssertions<T>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { AssertionError } from "./assertion-error.js";
|
|
3
|
+
import Locator from "../locator/locator.js";
|
|
4
|
+
import { resolveTimeout, waitUntil, WaitTimeoutError } from "../wait/wait-until.js";
|
|
5
|
+
/**
|
|
6
|
+
* Locator assertions nobody has awaited yet that are still running or have failed,
|
|
7
|
+
* mapped to their matcher name
|
|
8
|
+
*/
|
|
9
|
+
const pendingAssertions = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* Returns the matcher names of locator assertions that were never awaited and are
|
|
12
|
+
* still running or already failed, and stops tracking them. The runner calls this
|
|
13
|
+
* after a test to detect a missing `await`.
|
|
14
|
+
*/
|
|
15
|
+
export function takePendingAssertions() {
|
|
16
|
+
const names = [...pendingAssertions.values()];
|
|
17
|
+
pendingAssertions.clear();
|
|
18
|
+
return names;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Promise that stops being pending as soon as anyone subscribes to it. `await`,
|
|
22
|
+
* `.catch`, `.finally`, `Promise.all` and `assert.rejects` all go through `then`
|
|
23
|
+
* (`await` calls it because the constructor is not the native Promise).
|
|
24
|
+
*/
|
|
25
|
+
class TrackedAssertion extends Promise {
|
|
26
|
+
/** Promises derived through `then` are plain promises */
|
|
27
|
+
static get [Symbol.species]() {
|
|
28
|
+
return Promise;
|
|
29
|
+
}
|
|
30
|
+
then(onfulfilled, onrejected) {
|
|
31
|
+
pendingAssertions.delete(this);
|
|
32
|
+
return super.then(onfulfilled, onrejected);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function track(matcher, assertion) {
|
|
36
|
+
const tracked = new TrackedAssertion((resolve, reject) => {
|
|
37
|
+
assertion.then(resolve, reject);
|
|
38
|
+
});
|
|
39
|
+
pendingAssertions.set(tracked, matcher);
|
|
40
|
+
// The native then does not count as awaiting: a pass stops tracking, a failure stays
|
|
41
|
+
// tracked. Handling the rejection keeps an un-awaited failure from being unhandled.
|
|
42
|
+
Promise.prototype.then.call(tracked, () => pendingAssertions.delete(tracked), () => { });
|
|
43
|
+
return tracked;
|
|
44
|
+
}
|
|
45
|
+
function matchesText(actual, expected, mode) {
|
|
46
|
+
// search() ignores lastIndex, so global regexps behave the same on every call
|
|
47
|
+
if (expected instanceof RegExp)
|
|
48
|
+
return actual.search(expected) !== -1;
|
|
49
|
+
return mode === "exact" ? actual === expected : actual.includes(expected);
|
|
50
|
+
}
|
|
51
|
+
function isNumeric(value) {
|
|
52
|
+
return typeof value === "number" || typeof value === "bigint";
|
|
53
|
+
}
|
|
54
|
+
export class ValueAssertions {
|
|
55
|
+
constructor(actual, negated = false) {
|
|
56
|
+
this.actual = actual;
|
|
57
|
+
this.negated = negated;
|
|
58
|
+
}
|
|
59
|
+
/** Inverts the next matcher */
|
|
60
|
+
get not() {
|
|
61
|
+
return new ValueAssertions(this.actual, !this.negated);
|
|
62
|
+
}
|
|
63
|
+
assert(pass, matcher, expected) {
|
|
64
|
+
if (pass !== this.negated)
|
|
65
|
+
return;
|
|
66
|
+
throw new AssertionError({
|
|
67
|
+
matcher: this.negated ? `not.${matcher}` : matcher,
|
|
68
|
+
expected,
|
|
69
|
+
actual: this.actual,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
compare(matcher, expected, check) {
|
|
73
|
+
const actual = this.actual;
|
|
74
|
+
if (!isNumeric(actual) || !isNumeric(expected)) {
|
|
75
|
+
throw new TypeError(`${matcher} expects numbers, received ${typeof actual} and ${typeof expected}`);
|
|
76
|
+
}
|
|
77
|
+
this.assert(check(actual, expected), matcher, expected);
|
|
78
|
+
}
|
|
79
|
+
/** Strict identity (`Object.is`) */
|
|
80
|
+
toBe(expected) {
|
|
81
|
+
this.assert(Object.is(this.actual, expected), "toBe", expected);
|
|
82
|
+
}
|
|
83
|
+
/** Deep structural equality */
|
|
84
|
+
toEqual(expected) {
|
|
85
|
+
this.assert(isDeepStrictEqual(this.actual, expected), "toEqual", expected);
|
|
86
|
+
}
|
|
87
|
+
toBeTruthy() {
|
|
88
|
+
this.assert(Boolean(this.actual), "toBeTruthy", "truthy");
|
|
89
|
+
}
|
|
90
|
+
toBeFalsy() {
|
|
91
|
+
this.assert(!this.actual, "toBeFalsy", "falsy");
|
|
92
|
+
}
|
|
93
|
+
/** Array includes `item`, or string contains substring `item` */
|
|
94
|
+
toContain(item) {
|
|
95
|
+
const actual = this.actual;
|
|
96
|
+
if (typeof actual === "string") {
|
|
97
|
+
if (typeof item !== "string") {
|
|
98
|
+
throw new TypeError(`toContain on a string expects a string, received ${typeof item}`);
|
|
99
|
+
}
|
|
100
|
+
this.assert(actual.includes(item), "toContain", item);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(actual)) {
|
|
104
|
+
this.assert(actual.includes(item), "toContain", item);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
throw new TypeError(`toContain expects an array or string, received ${typeof actual}`);
|
|
108
|
+
}
|
|
109
|
+
/** String contains `pattern` (string) or matches it (RegExp) */
|
|
110
|
+
toMatch(pattern) {
|
|
111
|
+
const actual = this.actual;
|
|
112
|
+
if (typeof actual !== "string") {
|
|
113
|
+
throw new TypeError(`toMatch expects a string, received ${typeof actual}`);
|
|
114
|
+
}
|
|
115
|
+
this.assert(matchesText(actual, pattern, "contains"), "toMatch", pattern);
|
|
116
|
+
}
|
|
117
|
+
toBeGreaterThan(expected) {
|
|
118
|
+
this.compare("toBeGreaterThan", expected, (a, b) => a > b);
|
|
119
|
+
}
|
|
120
|
+
toBeGreaterThanOrEqual(expected) {
|
|
121
|
+
this.compare("toBeGreaterThanOrEqual", expected, (a, b) => a >= b);
|
|
122
|
+
}
|
|
123
|
+
toBeLessThan(expected) {
|
|
124
|
+
this.compare("toBeLessThan", expected, (a, b) => a < b);
|
|
125
|
+
}
|
|
126
|
+
toBeLessThanOrEqual(expected) {
|
|
127
|
+
this.compare("toBeLessThanOrEqual", expected, (a, b) => a <= b);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export class LocatorAssertions {
|
|
131
|
+
constructor(locator, negated = false) {
|
|
132
|
+
this.locator = locator;
|
|
133
|
+
this.negated = negated;
|
|
134
|
+
}
|
|
135
|
+
/** Inverts the next matcher: it retries until the condition is false */
|
|
136
|
+
get not() {
|
|
137
|
+
return new LocatorAssertions(this.locator, !this.negated);
|
|
138
|
+
}
|
|
139
|
+
/** Re-reads the element until `check` passes or the timeout expires */
|
|
140
|
+
assertEventually(matcher, expected, read, check, options) {
|
|
141
|
+
const name = this.negated ? `not.${matcher}` : matcher;
|
|
142
|
+
return track(name, (async () => {
|
|
143
|
+
const timeout = await resolveTimeout(options?.timeout);
|
|
144
|
+
try {
|
|
145
|
+
await waitUntil(read, (actual) => check(actual) !== this.negated, { timeout });
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
if (!(err instanceof WaitTimeoutError))
|
|
149
|
+
throw err;
|
|
150
|
+
// Reads never resolve to undefined, so an undefined `last` means no read completed
|
|
151
|
+
if (err.last === undefined) {
|
|
152
|
+
throw new Error(`expect(locator).${name}: could not read ${this.locator.selector} within ${timeout}ms`);
|
|
153
|
+
}
|
|
154
|
+
throw new AssertionError({
|
|
155
|
+
matcher: name,
|
|
156
|
+
expected,
|
|
157
|
+
actual: err.last,
|
|
158
|
+
locator: this.locator.selector,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
})());
|
|
162
|
+
}
|
|
163
|
+
toBeVisible(options) {
|
|
164
|
+
return this.assertEventually("toBeVisible", true, () => this.locator.isVisible(), (visible) => visible, options);
|
|
165
|
+
}
|
|
166
|
+
/** Trimmed text content equals `expected` (string) or matches it (RegExp) */
|
|
167
|
+
toHaveText(expected, options) {
|
|
168
|
+
return this.assertEventually("toHaveText", expected, () => this.locator.textContent(), (text) => text !== null && matchesText(text, expected, "exact"), options);
|
|
169
|
+
}
|
|
170
|
+
/** Text content contains `expected` (string) or matches it (RegExp) */
|
|
171
|
+
toContainText(expected, options) {
|
|
172
|
+
return this.assertEventually("toContainText", expected, () => this.locator.textContent(), (text) => text !== null && matchesText(text, expected, "contains"), options);
|
|
173
|
+
}
|
|
174
|
+
toHaveValue(expected, options) {
|
|
175
|
+
return this.assertEventually("toHaveValue", expected, () => this.locator.inputValue(), (value) => value !== null && matchesText(value, expected, "exact"), options);
|
|
176
|
+
}
|
|
177
|
+
toHaveAttribute(name, expected, options) {
|
|
178
|
+
return this.assertEventually("toHaveAttribute", expected, () => this.locator.getAttribute(name), (value) => value !== null && matchesText(value, expected, "exact"), options);
|
|
179
|
+
}
|
|
180
|
+
toHaveCount(expected, options) {
|
|
181
|
+
return this.assertEventually("toHaveCount", expected, () => this.locator.count(), (count) => count === expected, options);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export function expect(value) {
|
|
185
|
+
return value instanceof Locator
|
|
186
|
+
? new LocatorAssertions(value)
|
|
187
|
+
: new ValueAssertions(value);
|
|
188
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
import type { SameSite } from "../types/bidi-modules/network.js";
|
|
3
|
+
import { type EmulationOptions } from "./emulation.js";
|
|
4
|
+
import type Page from "./page.js";
|
|
5
|
+
export type { SameSite } from "../types/bidi-modules/network.js";
|
|
6
|
+
/** A cookie as `context.cookies()` returns it */
|
|
7
|
+
export interface Cookie {
|
|
8
|
+
name: string;
|
|
9
|
+
value: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
path: string;
|
|
12
|
+
httpOnly: boolean;
|
|
13
|
+
secure: boolean;
|
|
14
|
+
sameSite: SameSite;
|
|
15
|
+
/** Seconds since the epoch; absent for a session cookie */
|
|
16
|
+
expiry?: number;
|
|
17
|
+
}
|
|
18
|
+
/** A cookie to add: `domain`, or a `url` to take domain, path and `secure` from */
|
|
19
|
+
export interface CookieInput {
|
|
20
|
+
name: string;
|
|
21
|
+
value: string;
|
|
22
|
+
domain?: string;
|
|
23
|
+
/** @default "/", or the path of `url` */
|
|
24
|
+
path?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
httpOnly?: boolean;
|
|
27
|
+
secure?: boolean;
|
|
28
|
+
sameSite?: SameSite;
|
|
29
|
+
/** Seconds since the epoch */
|
|
30
|
+
expiry?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface CookieFilter {
|
|
33
|
+
name?: string;
|
|
34
|
+
domain?: string;
|
|
35
|
+
path?: string;
|
|
36
|
+
}
|
|
37
|
+
/** What a context needs from its browser */
|
|
38
|
+
export interface ContextHost {
|
|
39
|
+
connector: BiDiConnector;
|
|
40
|
+
newPage(options: {
|
|
41
|
+
type: "tab";
|
|
42
|
+
userContext?: string;
|
|
43
|
+
}): Promise<Page>;
|
|
44
|
+
/** Called when a context has been closed */
|
|
45
|
+
forget(context: BrowserContext): void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* An isolated browsing session inside one browser: its own cookies, storage and cache. Pages opened in it
|
|
49
|
+
* share them; pages of other contexts see none of it.
|
|
50
|
+
*/
|
|
51
|
+
export declare class BrowserContext {
|
|
52
|
+
private host;
|
|
53
|
+
/** The BiDi user context id; "default" for the browser's own */
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly isDefault: boolean;
|
|
56
|
+
private opened;
|
|
57
|
+
private isClosed;
|
|
58
|
+
constructor(host: ContextHost,
|
|
59
|
+
/** The BiDi user context id; "default" for the browser's own */
|
|
60
|
+
id: string, isDefault: boolean);
|
|
61
|
+
/** True once `close()` has run */
|
|
62
|
+
get closed(): boolean;
|
|
63
|
+
/** Opens a tab in this context */
|
|
64
|
+
newPage(): Promise<Page>;
|
|
65
|
+
/** The pages opened in this context that are still open */
|
|
66
|
+
pages(): Page[];
|
|
67
|
+
/** @internal Called when a page is opened in this context */
|
|
68
|
+
register(page: Page): void;
|
|
69
|
+
/** Adds cookies to this context's storage. Every cookie is checked before any is stored */
|
|
70
|
+
addCookies(cookies: CookieInput[]): Promise<void>;
|
|
71
|
+
/** The cookies of this context, optionally only those matching `filter` */
|
|
72
|
+
cookies(filter?: CookieFilter): Promise<Cookie[]>;
|
|
73
|
+
/** Deletes all cookies of this context, or those matching `filter` */
|
|
74
|
+
clearCookies(filter?: CookieFilter): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Pretends things about every page of this context, including pages opened later: locale, timezone, user agent,
|
|
77
|
+
* geolocation, offline, orientation, screen size. A page's own `emulate()` wins over the context's.
|
|
78
|
+
* @example
|
|
79
|
+
* await context.emulate({ locale: "nl-NL", timezone: "Europe/Amsterdam", offline: false });
|
|
80
|
+
*/
|
|
81
|
+
emulate(options: EmulationOptions): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Grants, denies or resets a browser permission ("geolocation", "notifications", "camera", …) for `origin`
|
|
84
|
+
* in this context. Geolocation emulation only answers once the permission is granted.
|
|
85
|
+
*/
|
|
86
|
+
setPermission(name: string, state: "granted" | "denied" | "prompt", origin: string): Promise<void>;
|
|
87
|
+
/** Removes the context; the browser closes its pages. The default context cannot be closed */
|
|
88
|
+
close(): Promise<void>;
|
|
89
|
+
private partition;
|
|
90
|
+
private assertOpen;
|
|
91
|
+
}
|