@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
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { createServer, type Server } from "node:http";
|
|
3
|
-
import type { AddressInfo, Socket } from "node:net";
|
|
4
|
-
import { after, before, test } from "node:test";
|
|
5
|
-
import { callFunction } from "../script/call-function.js";
|
|
6
|
-
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
7
|
-
import { Browser } from "./browser.js";
|
|
8
|
-
import type { BrowserContext } from "./browser-context.js";
|
|
9
|
-
import type Page from "./page.js";
|
|
10
|
-
|
|
11
|
-
let browser: Browser;
|
|
12
|
-
let server: Server;
|
|
13
|
-
let base: string;
|
|
14
|
-
const sockets = new Set<Socket>();
|
|
15
|
-
const createdIds: string[] = [];
|
|
16
|
-
|
|
17
|
-
before(async () => {
|
|
18
|
-
server = createServer((req, res) => {
|
|
19
|
-
res.setHeader("content-type", "text/html").end(`<!doctype html><body>${req.url}</body>`);
|
|
20
|
-
});
|
|
21
|
-
server.on("connection", (socket) => {
|
|
22
|
-
sockets.add(socket);
|
|
23
|
-
socket.on("close", () => sockets.delete(socket));
|
|
24
|
-
});
|
|
25
|
-
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
26
|
-
base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
|
|
27
|
-
({ browser } = await Browser.launch("firefox", { port: 9245, headless: true }));
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
after(async () => {
|
|
31
|
-
await browser?.close();
|
|
32
|
-
sockets.forEach((socket) => socket.destroy());
|
|
33
|
-
await new Promise((resolve) => server.close(resolve));
|
|
34
|
-
|
|
35
|
-
// Closing the browser took the contexts it had created out of the profile
|
|
36
|
-
const { browser: again, page } = await Browser.launch("firefox", { port: 9246, headless: true });
|
|
37
|
-
try {
|
|
38
|
-
const connector = (page as unknown as { biDiConnector: BiDiConnector }).biDiConnector;
|
|
39
|
-
const { userContexts } = await connector.send("browser.getUserContexts", {});
|
|
40
|
-
const remaining = userContexts.map((context) => context.userContext);
|
|
41
|
-
assert.deepEqual(createdIds.filter((id) => remaining.includes(id)), [], "contexts left behind in the profile");
|
|
42
|
-
} finally {
|
|
43
|
-
await page.dispose();
|
|
44
|
-
await again.close();
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const OPTIONS = { timeout: 40000 };
|
|
49
|
-
const connectorOf = (page: Page) => (page as unknown as { biDiConnector: BiDiConnector }).biDiConnector;
|
|
50
|
-
const evaluate = <T>(page: Page, source: string) => callFunction<T>(connectorOf(page), page.contextId, source);
|
|
51
|
-
const cookieOf = (page: Page) => evaluate<string>(page, "() => document.cookie");
|
|
52
|
-
|
|
53
|
-
async function context(options?: Parameters<Browser["newContext"]>[0]): Promise<BrowserContext> {
|
|
54
|
-
const created = await browser.newContext(options);
|
|
55
|
-
createdIds.push(created.id);
|
|
56
|
-
return created;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
test("a context opens pages that can navigate, and knows them", OPTIONS, async () => {
|
|
60
|
-
const alice = await context();
|
|
61
|
-
const page = await alice.newPage();
|
|
62
|
-
await page.navigateTo(`${base}/alice`);
|
|
63
|
-
assert.equal(await page.locator("body").textContent(), "/alice");
|
|
64
|
-
assert.equal(page.context(), alice);
|
|
65
|
-
assert.deepEqual(alice.pages(), [page]);
|
|
66
|
-
await alice.close();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("cookies of one context are invisible to another, and to the default one", OPTIONS, async () => {
|
|
70
|
-
const alice = await context();
|
|
71
|
-
const bob = await context();
|
|
72
|
-
const pageA = await alice.newPage();
|
|
73
|
-
const pageB = await bob.newPage();
|
|
74
|
-
await pageA.navigateTo(`${base}/`);
|
|
75
|
-
await pageB.navigateTo(`${base}/`);
|
|
76
|
-
|
|
77
|
-
await evaluate(pageA, `() => { document.cookie = "who=alice; path=/"; }`);
|
|
78
|
-
assert.equal(await cookieOf(pageA), "who=alice");
|
|
79
|
-
assert.equal(await cookieOf(pageB), "", "bob does not see alice's cookie");
|
|
80
|
-
assert.deepEqual((await bob.cookies()).map((c) => c.name), []);
|
|
81
|
-
assert.deepEqual((await alice.cookies()).map((c) => `${c.name}=${c.value}`), ["who=alice"]);
|
|
82
|
-
|
|
83
|
-
await alice.close();
|
|
84
|
-
await bob.close();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("addCookies puts cookies where the context's pages read them", OPTIONS, async () => {
|
|
88
|
-
const shopper = await context();
|
|
89
|
-
const page = await shopper.newPage();
|
|
90
|
-
await shopper.addCookies([
|
|
91
|
-
{ name: "sid", value: "abc123", url: base },
|
|
92
|
-
{ name: "theme", value: "dark", domain: "127.0.0.1", path: "/" },
|
|
93
|
-
]);
|
|
94
|
-
await page.navigateTo(`${base}/`);
|
|
95
|
-
assert.deepEqual((await cookieOf(page)).split("; ").sort(), ["sid=abc123", "theme=dark"]);
|
|
96
|
-
await shopper.close();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("cookies() reports what was set, with its attributes", OPTIONS, async () => {
|
|
100
|
-
const shopper = await context();
|
|
101
|
-
await shopper.addCookies([
|
|
102
|
-
{ name: "a", value: "1", domain: "127.0.0.1", httpOnly: true, sameSite: "lax", expiry: Math.floor(Date.now() / 1000) + 3600 },
|
|
103
|
-
{ name: "b", value: "2", domain: "127.0.0.1" },
|
|
104
|
-
]);
|
|
105
|
-
const cookies = await shopper.cookies();
|
|
106
|
-
const a = cookies.find((cookie) => cookie.name === "a")!;
|
|
107
|
-
assert.equal(a.value, "1");
|
|
108
|
-
assert.equal(a.httpOnly, true);
|
|
109
|
-
assert.equal(a.sameSite, "lax");
|
|
110
|
-
assert.equal(a.path, "/");
|
|
111
|
-
assert.ok(a.expiry && a.expiry > Date.now() / 1000);
|
|
112
|
-
const b = cookies.find((cookie) => cookie.name === "b")!;
|
|
113
|
-
assert.equal(b.expiry, undefined, "a session cookie has no expiry");
|
|
114
|
-
assert.deepEqual((await shopper.cookies({ name: "a" })).map((cookie) => cookie.name), ["a"]);
|
|
115
|
-
assert.deepEqual((await shopper.cookies({ domain: "elsewhere.test" })).map((cookie) => cookie.name), []);
|
|
116
|
-
await shopper.close();
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("clearCookies removes some or all", OPTIONS, async () => {
|
|
120
|
-
const shopper = await context();
|
|
121
|
-
await shopper.addCookies([
|
|
122
|
-
{ name: "keep", value: "1", domain: "127.0.0.1" },
|
|
123
|
-
{ name: "drop", value: "2", domain: "127.0.0.1" },
|
|
124
|
-
]);
|
|
125
|
-
await shopper.clearCookies({ name: "drop" });
|
|
126
|
-
assert.deepEqual((await shopper.cookies()).map((cookie) => cookie.name), ["keep"]);
|
|
127
|
-
await shopper.clearCookies();
|
|
128
|
-
assert.deepEqual(await shopper.cookies(), []);
|
|
129
|
-
await shopper.close();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("cookies are readable through a page's own context", OPTIONS, async () => {
|
|
133
|
-
const shopper = await context();
|
|
134
|
-
const page = await shopper.newPage();
|
|
135
|
-
await page.navigateTo(`${base}/`);
|
|
136
|
-
await evaluate(page, `() => { document.cookie = "from=page; path=/"; }`);
|
|
137
|
-
assert.deepEqual((await page.context().cookies()).map((cookie) => `${cookie.name}=${cookie.value}`), ["from=page"]);
|
|
138
|
-
await shopper.close();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test("closing a context closes its pages and refuses more use", OPTIONS, async () => {
|
|
142
|
-
const temp = await context();
|
|
143
|
-
const first = await temp.newPage();
|
|
144
|
-
const second = await temp.newPage();
|
|
145
|
-
await first.navigateTo(`${base}/one`);
|
|
146
|
-
await temp.close();
|
|
147
|
-
assert.equal(first.closed, true);
|
|
148
|
-
assert.equal(second.closed, true);
|
|
149
|
-
await assert.rejects(first.url(), /page closed/);
|
|
150
|
-
assert.deepEqual(temp.pages(), []);
|
|
151
|
-
await assert.rejects(temp.newPage(), /context closed/);
|
|
152
|
-
assert.equal(browser.contexts().includes(temp), false);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("the default context cannot be closed, and closing twice is harmless", OPTIONS, async () => {
|
|
156
|
-
await assert.rejects(browser.defaultContext.close(), /cannot close the default context/);
|
|
157
|
-
const temp = await context();
|
|
158
|
-
await temp.close();
|
|
159
|
-
await assert.doesNotReject(temp.close());
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test("a dialog in a new context is handled like anywhere else", OPTIONS, async () => {
|
|
163
|
-
const dialogs = await context();
|
|
164
|
-
const page = await dialogs.newPage();
|
|
165
|
-
await page.navigateTo(`${base}/dialog`);
|
|
166
|
-
const seen: string[] = [];
|
|
167
|
-
page.on("dialog", async (dialog) => {
|
|
168
|
-
seen.push(dialog.message());
|
|
169
|
-
await dialog.accept();
|
|
170
|
-
});
|
|
171
|
-
await callFunction(connectorOf(page), page.contextId, `() => { setTimeout(() => { window.__after = confirm("in a context"); }, 0); }`, [], { awaitPromise: false });
|
|
172
|
-
const deadline = Date.now() + 5000;
|
|
173
|
-
while (Date.now() < deadline && (await evaluate<unknown>(page, "() => window.__after")) === undefined) {
|
|
174
|
-
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
175
|
-
}
|
|
176
|
-
assert.equal(await evaluate<boolean>(page, "() => window.__after"), true);
|
|
177
|
-
assert.deepEqual(seen, ["in a context"]);
|
|
178
|
-
await dialogs.close();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("pages get the default viewport in every context", OPTIONS, async () => {
|
|
182
|
-
const sized = await context();
|
|
183
|
-
const page = await sized.newPage();
|
|
184
|
-
await page.navigateTo(`${base}/size`);
|
|
185
|
-
assert.deepEqual((await evaluate<number[]>(page, "() => [innerWidth, innerHeight]")), [1280, 720]);
|
|
186
|
-
await sized.close();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("acceptInsecureCerts is accepted when a context is created", OPTIONS, async () => {
|
|
190
|
-
const lenient = await context({ acceptInsecureCerts: true });
|
|
191
|
-
const page = await lenient.newPage();
|
|
192
|
-
await page.navigateTo(`${base}/lenient`);
|
|
193
|
-
assert.equal(await page.locator("body").textContent(), "/lenient");
|
|
194
|
-
await lenient.close();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("the contexts a test forgot are removed with the browser (checked after it closed)", OPTIONS, async () => {
|
|
198
|
-
const forgotten = await context();
|
|
199
|
-
await forgotten.newPage();
|
|
200
|
-
assert.ok(browser.contexts().includes(forgotten));
|
|
201
|
-
});
|
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import EventEmitter from "node:events";
|
|
3
|
-
import { afterEach, describe, it } from "node:test";
|
|
4
|
-
import { autoReply, FakeWebSocket, tick } from "../testing/fake-websocket.js";
|
|
5
|
-
import { BiDiConnector } from "../transport/bidi-connection.js";
|
|
6
|
-
import { Browser } from "./browser.js";
|
|
7
|
-
|
|
8
|
-
const stops: Array<() => void> = [];
|
|
9
|
-
afterEach(() => stops.splice(0).forEach((stop) => stop()));
|
|
10
|
-
|
|
11
|
-
const failing = (code: string, message = "nope") => Object.assign(new Error(message), { code });
|
|
12
|
-
|
|
13
|
-
function setup(handlers: Record<string, (params: any) => object | Error> = {}) {
|
|
14
|
-
const ws = new FakeWebSocket();
|
|
15
|
-
const connector = new BiDiConnector(ws as unknown as WebSocket);
|
|
16
|
-
let contexts = 0;
|
|
17
|
-
let tabs = 0;
|
|
18
|
-
stops.push(
|
|
19
|
-
autoReply(ws, {
|
|
20
|
-
"browsingContext.getTree": () => ({ contexts: [] }),
|
|
21
|
-
"browser.createUserContext": () => ({ userContext: `u${++contexts}` }),
|
|
22
|
-
"browser.removeUserContext": () => ({}),
|
|
23
|
-
"browsingContext.create": (params) => ({ context: `tab-${++tabs}`, userContext: params.userContext ?? "default" }),
|
|
24
|
-
"storage.setCookie": () => ({ partitionKey: {} }),
|
|
25
|
-
"storage.getCookies": () => ({ cookies: [], partitionKey: {} }),
|
|
26
|
-
"storage.deleteCookies": () => ({ partitionKey: {} }),
|
|
27
|
-
...handlers,
|
|
28
|
-
}),
|
|
29
|
-
);
|
|
30
|
-
const proc = Object.assign(new EventEmitter(), {
|
|
31
|
-
exitCode: null as number | null,
|
|
32
|
-
signalCode: null as string | null,
|
|
33
|
-
kill() {
|
|
34
|
-
queueMicrotask(() => proc.emit("exit"));
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
const browser = new Browser({ browserProc: proc as never, biDiConnector: connector });
|
|
38
|
-
const commands = (method: string) => ws.sent.filter((m) => m.method === method).map((m) => m.params as any);
|
|
39
|
-
return { ws, browser, commands };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
describe("Browser.newContext", () => {
|
|
43
|
-
it("creates a user context and returns a BrowserContext with its id", async () => {
|
|
44
|
-
const { browser, commands } = setup();
|
|
45
|
-
const context = await browser.newContext();
|
|
46
|
-
assert.equal(context.id, "u1");
|
|
47
|
-
assert.equal(context.isDefault, false);
|
|
48
|
-
assert.deepEqual(commands("browser.createUserContext"), [{}]);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("passes its options on", async () => {
|
|
52
|
-
const { browser, commands } = setup();
|
|
53
|
-
await browser.newContext({ acceptInsecureCerts: true, unhandledPromptBehavior: { default: "dismiss" } });
|
|
54
|
-
assert.deepEqual(commands("browser.createUserContext"), [
|
|
55
|
-
{ acceptInsecureCerts: true, unhandledPromptBehavior: { default: "dismiss" } },
|
|
56
|
-
]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("knows the default context, and lists the ones this browser created", async () => {
|
|
60
|
-
const { browser } = setup();
|
|
61
|
-
assert.equal(browser.defaultContext.id, "default");
|
|
62
|
-
assert.equal(browser.defaultContext.isDefault, true);
|
|
63
|
-
assert.deepEqual(browser.contexts().map((c) => c.id), ["default"]);
|
|
64
|
-
const a = await browser.newContext();
|
|
65
|
-
const b = await browser.newContext();
|
|
66
|
-
assert.deepEqual(browser.contexts().map((c) => c.id), ["default", "u1", "u2"]);
|
|
67
|
-
await a.close();
|
|
68
|
-
assert.deepEqual(browser.contexts(), [browser.defaultContext, b]);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe("BrowserContext.newPage", () => {
|
|
73
|
-
it("opens a tab in the user context and binds the page to it", async () => {
|
|
74
|
-
const { browser, commands } = setup();
|
|
75
|
-
const context = await browser.newContext();
|
|
76
|
-
const page = await context.newPage();
|
|
77
|
-
assert.deepEqual(commands("browsingContext.create"), [{ type: "tab", userContext: "u1" }]);
|
|
78
|
-
assert.equal(page.contextId, "tab-1");
|
|
79
|
-
assert.equal(page.context(), context);
|
|
80
|
-
assert.deepEqual(context.pages(), [page]);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("the default context opens tabs without naming a user context", async () => {
|
|
84
|
-
const { browser, commands } = setup();
|
|
85
|
-
const page = await browser.defaultContext.newPage();
|
|
86
|
-
assert.deepEqual(commands("browsingContext.create"), [{ type: "tab" }]);
|
|
87
|
-
assert.equal(page.context(), browser.defaultContext);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("Browser.newPage puts its page in the context it was created in", async () => {
|
|
91
|
-
const { browser } = setup();
|
|
92
|
-
const context = await browser.newContext();
|
|
93
|
-
const inContext = await browser.newPage({ type: "tab", userContext: context.id });
|
|
94
|
-
const inDefault = await browser.newPage({ type: "tab" });
|
|
95
|
-
assert.equal(inContext.context(), context);
|
|
96
|
-
assert.equal(inDefault.context(), browser.defaultContext);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("a page that closed is no longer listed", async () => {
|
|
100
|
-
const { browser } = setup({ "browsingContext.close": () => ({}) });
|
|
101
|
-
const context = await browser.newContext();
|
|
102
|
-
const first = await context.newPage();
|
|
103
|
-
const second = await context.newPage();
|
|
104
|
-
await first.close();
|
|
105
|
-
assert.deepEqual(context.pages(), [second]);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("a page built by hand has no context", async () => {
|
|
109
|
-
const { browser } = setup();
|
|
110
|
-
const { default: Page } = await import("./page.js");
|
|
111
|
-
const stray = new Page((browser as any).biDiConnector, "x");
|
|
112
|
-
assert.throws(() => stray.context(), /no browser context/);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe("cookies", () => {
|
|
117
|
-
it("addCookies stores each cookie in this context's storage", async () => {
|
|
118
|
-
const { browser, commands } = setup();
|
|
119
|
-
const context = await browser.newContext();
|
|
120
|
-
await context.addCookies([
|
|
121
|
-
{ name: "sid", value: "1", domain: "example.test" },
|
|
122
|
-
{ name: "pref", value: "dark", domain: "example.test", path: "/app", httpOnly: true, secure: true, sameSite: "lax", expiry: 1900000000 },
|
|
123
|
-
]);
|
|
124
|
-
assert.deepEqual(commands("storage.setCookie"), [
|
|
125
|
-
{
|
|
126
|
-
cookie: { name: "sid", value: { type: "string", value: "1" }, domain: "example.test", path: "/" },
|
|
127
|
-
partition: { type: "storageKey", userContext: "u1" },
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
cookie: {
|
|
131
|
-
name: "pref",
|
|
132
|
-
value: { type: "string", value: "dark" },
|
|
133
|
-
domain: "example.test",
|
|
134
|
-
path: "/app",
|
|
135
|
-
httpOnly: true,
|
|
136
|
-
secure: true,
|
|
137
|
-
sameSite: "lax",
|
|
138
|
-
expiry: 1900000000,
|
|
139
|
-
},
|
|
140
|
-
partition: { type: "storageKey", userContext: "u1" },
|
|
141
|
-
},
|
|
142
|
-
]);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("a url supplies domain, path and secure", async () => {
|
|
146
|
-
const { browser, commands } = setup();
|
|
147
|
-
const context = await browser.newContext();
|
|
148
|
-
await context.addCookies([
|
|
149
|
-
{ name: "a", value: "1", url: "https://shop.example.test/cart/items?x=1" },
|
|
150
|
-
{ name: "b", value: "2", url: "http://localhost:3000/" },
|
|
151
|
-
{ name: "c", value: "3", url: "https://example.test/", secure: false, path: "/x" },
|
|
152
|
-
]);
|
|
153
|
-
const cookies = commands("storage.setCookie").map((c) => c.cookie);
|
|
154
|
-
assert.deepEqual(cookies[0], { name: "a", value: { type: "string", value: "1" }, domain: "shop.example.test", path: "/cart/items", secure: true });
|
|
155
|
-
assert.deepEqual(cookies[1], { name: "b", value: { type: "string", value: "2" }, domain: "localhost", path: "/", secure: false });
|
|
156
|
-
assert.deepEqual(cookies[2], { name: "c", value: { type: "string", value: "3" }, domain: "example.test", path: "/x", secure: false });
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("needs a domain or a url, and nothing is stored when one cookie is wrong", async () => {
|
|
160
|
-
const { browser, commands } = setup();
|
|
161
|
-
const context = await browser.newContext();
|
|
162
|
-
await assert.rejects(
|
|
163
|
-
context.addCookies([{ name: "ok", value: "1", domain: "example.test" }, { name: "bad", value: "2" }]),
|
|
164
|
-
/cookie "bad" needs a domain or a url/,
|
|
165
|
-
);
|
|
166
|
-
await assert.rejects(context.addCookies([{ name: "x", value: "1", url: "not a url" }]), TypeError);
|
|
167
|
-
assert.equal(commands("storage.setCookie").length, 0);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("the default context's cookies live in the default user context", async () => {
|
|
171
|
-
const { browser, commands } = setup();
|
|
172
|
-
await browser.defaultContext.addCookies([{ name: "a", value: "1", domain: "example.test" }]);
|
|
173
|
-
assert.deepEqual(commands("storage.setCookie")[0].partition, { type: "storageKey", userContext: "default" });
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("a cookie the browser refuses is thrown as a BiDiError", async () => {
|
|
177
|
-
const { browser } = setup({ "storage.setCookie": () => failing("unable to set cookie") });
|
|
178
|
-
const context = await browser.newContext();
|
|
179
|
-
await assert.rejects(context.addCookies([{ name: "a", value: "1", domain: "example.test" }]), {
|
|
180
|
-
name: "BiDiError",
|
|
181
|
-
code: "unable to set cookie",
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("cookies() returns plain cookies, decoding values and leaving out what is not set", async () => {
|
|
186
|
-
const { browser, commands } = setup({
|
|
187
|
-
"storage.getCookies": () => ({
|
|
188
|
-
partitionKey: {},
|
|
189
|
-
cookies: [
|
|
190
|
-
{ name: "sid", value: { type: "string", value: "1" }, domain: "example.test", path: "/", size: 4, httpOnly: false, secure: false, sameSite: "default" },
|
|
191
|
-
{ name: "bin", value: { type: "base64", value: Buffer.from("héllo").toString("base64") }, domain: "example.test", path: "/a", size: 9, httpOnly: true, secure: true, sameSite: "lax", expiry: 1900000000 },
|
|
192
|
-
],
|
|
193
|
-
}),
|
|
194
|
-
});
|
|
195
|
-
const context = await browser.newContext();
|
|
196
|
-
const cookies = await context.cookies();
|
|
197
|
-
assert.deepEqual(cookies, [
|
|
198
|
-
{ name: "sid", value: "1", domain: "example.test", path: "/", httpOnly: false, secure: false, sameSite: "default" },
|
|
199
|
-
{ name: "bin", value: "héllo", domain: "example.test", path: "/a", httpOnly: true, secure: true, sameSite: "lax", expiry: 1900000000 },
|
|
200
|
-
]);
|
|
201
|
-
assert.deepEqual(commands("storage.getCookies"), [{ partition: { type: "storageKey", userContext: "u1" } }]);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it("cookies(filter) filters by name, domain and path", async () => {
|
|
205
|
-
const { browser, commands } = setup();
|
|
206
|
-
const context = await browser.newContext();
|
|
207
|
-
await context.cookies({ name: "sid", domain: "example.test", path: "/" });
|
|
208
|
-
await context.cookies({ name: "only-name" });
|
|
209
|
-
assert.deepEqual(commands("storage.getCookies"), [
|
|
210
|
-
{ filter: { name: "sid", domain: "example.test", path: "/" }, partition: { type: "storageKey", userContext: "u1" } },
|
|
211
|
-
{ filter: { name: "only-name" }, partition: { type: "storageKey", userContext: "u1" } },
|
|
212
|
-
]);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("clearCookies deletes all, or those the filter names", async () => {
|
|
216
|
-
const { browser, commands } = setup();
|
|
217
|
-
const context = await browser.newContext();
|
|
218
|
-
await context.clearCookies();
|
|
219
|
-
await context.clearCookies({ name: "sid" });
|
|
220
|
-
assert.deepEqual(commands("storage.deleteCookies"), [
|
|
221
|
-
{ partition: { type: "storageKey", userContext: "u1" } },
|
|
222
|
-
{ filter: { name: "sid" }, partition: { type: "storageKey", userContext: "u1" } },
|
|
223
|
-
]);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it("a page reaches its context's cookies through page.context()", async () => {
|
|
227
|
-
const { browser, commands } = setup();
|
|
228
|
-
const context = await browser.newContext();
|
|
229
|
-
const page = await context.newPage();
|
|
230
|
-
await page.context().addCookies([{ name: "a", value: "1", domain: "example.test" }]);
|
|
231
|
-
assert.equal(commands("storage.setCookie")[0].partition.userContext, "u1");
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe("BrowserContext.close", () => {
|
|
236
|
-
it("removes the user context, and its pages are closed for good", async () => {
|
|
237
|
-
const { browser, commands } = setup();
|
|
238
|
-
const context = await browser.newContext();
|
|
239
|
-
const page = await context.newPage();
|
|
240
|
-
await context.close();
|
|
241
|
-
assert.deepEqual(commands("browser.removeUserContext"), [{ userContext: "u1" }]);
|
|
242
|
-
assert.equal(page.closed, true);
|
|
243
|
-
await assert.rejects(page.url(), /page closed/);
|
|
244
|
-
assert.deepEqual(context.pages(), []);
|
|
245
|
-
assert.equal(context.closed, true);
|
|
246
|
-
await assert.rejects(context.newPage(), /context closed/);
|
|
247
|
-
await assert.rejects(context.cookies(), /context closed/);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it("closing twice sends nothing more", async () => {
|
|
251
|
-
const { browser, commands } = setup();
|
|
252
|
-
const context = await browser.newContext();
|
|
253
|
-
await context.close();
|
|
254
|
-
await context.close();
|
|
255
|
-
assert.equal(commands("browser.removeUserContext").length, 1);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("the default context cannot be closed", async () => {
|
|
259
|
-
const { browser, commands } = setup();
|
|
260
|
-
await assert.rejects(browser.defaultContext.close(), /cannot close the default context/);
|
|
261
|
-
assert.equal(commands("browser.removeUserContext").length, 0);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it("a context the browser no longer knows counts as closed", async () => {
|
|
265
|
-
const { browser } = setup({ "browser.removeUserContext": () => failing("no such user context") });
|
|
266
|
-
const context = await browser.newContext();
|
|
267
|
-
await assert.doesNotReject(context.close());
|
|
268
|
-
assert.equal(context.closed, true);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it("other errors are thrown and the context stays open", async () => {
|
|
272
|
-
const { browser } = setup({ "browser.removeUserContext": () => failing("unknown error") });
|
|
273
|
-
const context = await browser.newContext();
|
|
274
|
-
await assert.rejects(context.close(), { code: "unknown error" });
|
|
275
|
-
assert.equal(context.closed, false);
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe("Browser.close", () => {
|
|
280
|
-
it("removes the contexts it created before the browser goes, so they do not pile up in the profile", async () => {
|
|
281
|
-
const { ws, browser } = setup({ "browser.close": () => ({}) });
|
|
282
|
-
await browser.newContext();
|
|
283
|
-
await browser.newContext();
|
|
284
|
-
await browser.close();
|
|
285
|
-
const order = ws.sent.map((m) => m.method).filter((m) => m === "browser.removeUserContext" || m === "browser.close");
|
|
286
|
-
assert.deepEqual(order, ["browser.removeUserContext", "browser.removeUserContext", "browser.close"]);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it("a context that cannot be removed does not keep the browser open", async () => {
|
|
290
|
-
const { browser } = setup({
|
|
291
|
-
"browser.removeUserContext": () => failing("unknown error"),
|
|
292
|
-
"browser.close": () => ({}),
|
|
293
|
-
});
|
|
294
|
-
await browser.newContext();
|
|
295
|
-
await assert.doesNotReject(browser.close());
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe("emulation", () => {
|
|
300
|
-
const url = (value: string) => ({ "script.callFunction": () => ({ type: "success", realm: "r", result: { type: "string", value } }) });
|
|
301
|
-
|
|
302
|
-
it("context.emulate applies to the whole user context", async () => {
|
|
303
|
-
const { browser, commands } = setup();
|
|
304
|
-
const context = await browser.newContext();
|
|
305
|
-
await context.emulate({ locale: "de-DE", timezone: "Europe/Berlin" });
|
|
306
|
-
assert.deepEqual(commands("emulation.setLocaleOverride"), [{ locale: "de-DE", userContexts: ["u1"] }]);
|
|
307
|
-
assert.deepEqual(commands("emulation.setTimezoneOverride"), [{ timezone: "Europe/Berlin", userContexts: ["u1"] }]);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it("the default context is targeted as the default user context", async () => {
|
|
311
|
-
const { browser, commands } = setup();
|
|
312
|
-
await browser.defaultContext.emulate({ offline: true });
|
|
313
|
-
assert.deepEqual(commands("emulation.setNetworkConditions"), [
|
|
314
|
-
{ networkConditions: { type: "offline" }, userContexts: ["default"] },
|
|
315
|
-
]);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it("page.emulate applies to that page only", async () => {
|
|
319
|
-
const { browser, commands } = setup();
|
|
320
|
-
const context = await browser.newContext();
|
|
321
|
-
const page = await context.newPage();
|
|
322
|
-
await page.emulate({ locale: "fr-FR", geolocation: null });
|
|
323
|
-
assert.deepEqual(commands("emulation.setLocaleOverride"), [{ locale: "fr-FR", contexts: ["tab-1"] }]);
|
|
324
|
-
assert.deepEqual(commands("emulation.setGeolocationOverride"), [{ coordinates: null, contexts: ["tab-1"] }]);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it("page.emulate on a closed page is refused", async () => {
|
|
328
|
-
const { browser } = setup({ "browsingContext.close": () => ({}) });
|
|
329
|
-
const page = await (await browser.newContext()).newPage();
|
|
330
|
-
await page.close();
|
|
331
|
-
await assert.rejects(page.emulate({ locale: "nl-NL" }), /page closed/);
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
it("context.emulate on a closed context is refused", async () => {
|
|
335
|
-
const { browser } = setup();
|
|
336
|
-
const context = await browser.newContext();
|
|
337
|
-
await context.close();
|
|
338
|
-
await assert.rejects(context.emulate({ locale: "nl-NL" }), /context closed/);
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it("newContext applies emulation options before the first page can exist", async () => {
|
|
342
|
-
const { ws, browser, commands } = setup();
|
|
343
|
-
const context = await browser.newContext({ acceptInsecureCerts: true, locale: "nl-NL", timezone: "Europe/Amsterdam", offline: false });
|
|
344
|
-
assert.deepEqual(commands("browser.createUserContext"), [{ acceptInsecureCerts: true }], "only the context options go to createUserContext");
|
|
345
|
-
assert.deepEqual(commands("emulation.setLocaleOverride"), [{ locale: "nl-NL", userContexts: [context.id] }]);
|
|
346
|
-
assert.deepEqual(commands("emulation.setNetworkConditions"), [{ networkConditions: null, userContexts: [context.id] }]);
|
|
347
|
-
await context.newPage();
|
|
348
|
-
const order = ws.sent.map((m) => m.method).filter((m) => m.startsWith("emulation.") || m === "browsingContext.create");
|
|
349
|
-
assert.equal(order[order.length - 1], "browsingContext.create");
|
|
350
|
-
assert.equal(order.filter((m) => m === "browsingContext.create").length, 1);
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
it("a context whose emulation fails is removed again, so nothing is left in the profile", async () => {
|
|
354
|
-
const { browser, commands } = setup({ "emulation.setTimezoneOverride": () => failing("invalid argument", "bad tz") });
|
|
355
|
-
await assert.rejects(browser.newContext({ locale: "nl-NL", timezone: "Europe/Amsterdam" }), { name: "EmulationError" });
|
|
356
|
-
assert.deepEqual(commands("browser.removeUserContext"), [{ userContext: "u1" }]);
|
|
357
|
-
assert.deepEqual(browser.contexts().map((c) => c.id), ["default"]);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
it("an unsupported option removes the new context too", async () => {
|
|
361
|
-
const { browser, commands } = setup({ "emulation.setTouchOverride": () => failing("unknown command") });
|
|
362
|
-
await assert.rejects(browser.newContext({ touch: 3 }), { name: "EmulationUnsupportedError" });
|
|
363
|
-
assert.equal(commands("browser.removeUserContext").length, 1);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it("an invalid value is refused before a context is even created", async () => {
|
|
367
|
-
const { browser, commands } = setup();
|
|
368
|
-
await assert.rejects(browser.newContext({ timezone: "Not/AZone" }), RangeError);
|
|
369
|
-
assert.equal(commands("browser.createUserContext").length, 0);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it("no emulation options, no emulation commands", async () => {
|
|
373
|
-
const { ws, browser } = setup();
|
|
374
|
-
await browser.newContext({ acceptInsecureCerts: false });
|
|
375
|
-
assert.equal(ws.sent.filter((m) => m.method.startsWith("emulation.")).length, 0);
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it("setPermission names the user context; the default context leaves it out", async () => {
|
|
379
|
-
const { browser, commands } = setup({ "permissions.setPermission": () => ({}) });
|
|
380
|
-
const context = await browser.newContext();
|
|
381
|
-
await context.setPermission("geolocation", "granted", "https://example.test");
|
|
382
|
-
await browser.defaultContext.setPermission("notifications", "denied", "https://example.test");
|
|
383
|
-
assert.deepEqual(commands("permissions.setPermission"), [
|
|
384
|
-
{ descriptor: { name: "geolocation" }, state: "granted", origin: "https://example.test", userContext: "u1" },
|
|
385
|
-
{ descriptor: { name: "notifications" }, state: "denied", origin: "https://example.test" },
|
|
386
|
-
]);
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
it("page.grantPermission grants for the origin the page is on, in its context", async () => {
|
|
390
|
-
const { browser, commands } = setup({ "permissions.setPermission": () => ({}), ...url("https://shop.example.test:8443/cart?x=1") });
|
|
391
|
-
const context = await browser.newContext();
|
|
392
|
-
const page = await context.newPage();
|
|
393
|
-
await page.grantPermission("geolocation");
|
|
394
|
-
await page.grantPermission("camera", { origin: "https://other.test" });
|
|
395
|
-
assert.deepEqual(commands("permissions.setPermission"), [
|
|
396
|
-
{ descriptor: { name: "geolocation" }, state: "granted", origin: "https://shop.example.test:8443", userContext: "u1" },
|
|
397
|
-
{ descriptor: { name: "camera" }, state: "granted", origin: "https://other.test", userContext: "u1" },
|
|
398
|
-
]);
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it("page.grantPermission needs a real origin", async () => {
|
|
402
|
-
const { browser } = setup({ "permissions.setPermission": () => ({}), ...url("about:blank") });
|
|
403
|
-
const page = await (await browser.newContext()).newPage();
|
|
404
|
-
await assert.rejects(page.grantPermission("geolocation"), /needs a page on a web origin/);
|
|
405
|
-
await assert.doesNotReject(page.grantPermission("geolocation", { origin: "https://example.test" }));
|
|
406
|
-
});
|
|
407
|
-
});
|