@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,208 +0,0 @@
|
|
|
1
|
-
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
-
import { BiDiError } from "../transport/bidi-error.js";
|
|
3
|
-
import type { Cookie as BiDiCookie, SameSite } from "../types/bidi-modules/network.js";
|
|
4
|
-
import type { PartialCookie, PartitionDescriptor } from "../types/bidi-modules/storage.js";
|
|
5
|
-
import { applyEmulation, type EmulationOptions } from "./emulation.js";
|
|
6
|
-
import type Page from "./page.js";
|
|
7
|
-
|
|
8
|
-
export type { SameSite } from "../types/bidi-modules/network.js";
|
|
9
|
-
|
|
10
|
-
/** A cookie as `context.cookies()` returns it */
|
|
11
|
-
export interface Cookie {
|
|
12
|
-
name: string;
|
|
13
|
-
value: string;
|
|
14
|
-
domain: string;
|
|
15
|
-
path: string;
|
|
16
|
-
httpOnly: boolean;
|
|
17
|
-
secure: boolean;
|
|
18
|
-
sameSite: SameSite;
|
|
19
|
-
/** Seconds since the epoch; absent for a session cookie */
|
|
20
|
-
expiry?: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** A cookie to add: `domain`, or a `url` to take domain, path and `secure` from */
|
|
24
|
-
export interface CookieInput {
|
|
25
|
-
name: string;
|
|
26
|
-
value: string;
|
|
27
|
-
domain?: string;
|
|
28
|
-
/** @default "/", or the path of `url` */
|
|
29
|
-
path?: string;
|
|
30
|
-
url?: string;
|
|
31
|
-
httpOnly?: boolean;
|
|
32
|
-
secure?: boolean;
|
|
33
|
-
sameSite?: SameSite;
|
|
34
|
-
/** Seconds since the epoch */
|
|
35
|
-
expiry?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface CookieFilter {
|
|
39
|
-
name?: string;
|
|
40
|
-
domain?: string;
|
|
41
|
-
path?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** What a context needs from its browser */
|
|
45
|
-
export interface ContextHost {
|
|
46
|
-
connector: BiDiConnector;
|
|
47
|
-
newPage(options: { type: "tab"; userContext?: string }): Promise<Page>;
|
|
48
|
-
/** Called when a context has been closed */
|
|
49
|
-
forget(context: BrowserContext): void;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* An isolated browsing session inside one browser: its own cookies, storage and cache. Pages opened in it
|
|
54
|
-
* share them; pages of other contexts see none of it.
|
|
55
|
-
*/
|
|
56
|
-
export class BrowserContext {
|
|
57
|
-
private opened: Page[] = [];
|
|
58
|
-
private isClosed = false;
|
|
59
|
-
|
|
60
|
-
constructor(
|
|
61
|
-
private host: ContextHost,
|
|
62
|
-
/** The BiDi user context id; "default" for the browser's own */
|
|
63
|
-
public readonly id: string,
|
|
64
|
-
public readonly isDefault: boolean,
|
|
65
|
-
) {}
|
|
66
|
-
|
|
67
|
-
/** True once `close()` has run */
|
|
68
|
-
public get closed(): boolean {
|
|
69
|
-
return this.isClosed;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Opens a tab in this context */
|
|
73
|
-
public async newPage(): Promise<Page> {
|
|
74
|
-
this.assertOpen();
|
|
75
|
-
return this.host.newPage({ type: "tab", ...(!this.isDefault && { userContext: this.id }) });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** The pages opened in this context that are still open */
|
|
79
|
-
public pages(): Page[] {
|
|
80
|
-
this.opened = this.opened.filter((page) => !page.closed);
|
|
81
|
-
return [...this.opened];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** @internal Called when a page is opened in this context */
|
|
85
|
-
public register(page: Page): void {
|
|
86
|
-
this.opened.push(page);
|
|
87
|
-
page.setContext(this);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Adds cookies to this context's storage. Every cookie is checked before any is stored */
|
|
91
|
-
public async addCookies(cookies: CookieInput[]): Promise<void> {
|
|
92
|
-
this.assertOpen();
|
|
93
|
-
const prepared = cookies.map(toBiDiCookie);
|
|
94
|
-
for (const cookie of prepared) {
|
|
95
|
-
await this.host.connector.send("storage.setCookie", { cookie, partition: this.partition() });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** The cookies of this context, optionally only those matching `filter` */
|
|
100
|
-
public async cookies(filter?: CookieFilter): Promise<Cookie[]> {
|
|
101
|
-
this.assertOpen();
|
|
102
|
-
const { cookies } = await this.host.connector.send("storage.getCookies", {
|
|
103
|
-
...(filter && { filter }),
|
|
104
|
-
partition: this.partition(),
|
|
105
|
-
});
|
|
106
|
-
return cookies.map(toCookie);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Deletes all cookies of this context, or those matching `filter` */
|
|
110
|
-
public async clearCookies(filter?: CookieFilter): Promise<void> {
|
|
111
|
-
this.assertOpen();
|
|
112
|
-
await this.host.connector.send("storage.deleteCookies", {
|
|
113
|
-
...(filter && { filter }),
|
|
114
|
-
partition: this.partition(),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Pretends things about every page of this context, including pages opened later: locale, timezone, user agent,
|
|
120
|
-
* geolocation, offline, orientation, screen size. A page's own `emulate()` wins over the context's.
|
|
121
|
-
* @example
|
|
122
|
-
* await context.emulate({ locale: "nl-NL", timezone: "Europe/Amsterdam", offline: false });
|
|
123
|
-
*/
|
|
124
|
-
public async emulate(options: EmulationOptions): Promise<void> {
|
|
125
|
-
this.assertOpen();
|
|
126
|
-
await applyEmulation(this.host.connector, { userContexts: [this.id] }, options);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Grants, denies or resets a browser permission ("geolocation", "notifications", "camera", …) for `origin`
|
|
131
|
-
* in this context. Geolocation emulation only answers once the permission is granted.
|
|
132
|
-
*/
|
|
133
|
-
public async setPermission(
|
|
134
|
-
name: string,
|
|
135
|
-
state: "granted" | "denied" | "prompt",
|
|
136
|
-
origin: string,
|
|
137
|
-
): Promise<void> {
|
|
138
|
-
this.assertOpen();
|
|
139
|
-
await this.host.connector.send("permissions.setPermission", {
|
|
140
|
-
descriptor: { name },
|
|
141
|
-
state,
|
|
142
|
-
origin,
|
|
143
|
-
...(!this.isDefault && { userContext: this.id }),
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Removes the context; the browser closes its pages. The default context cannot be closed */
|
|
148
|
-
public async close(): Promise<void> {
|
|
149
|
-
if (this.isDefault) throw new Error("cannot close the default context");
|
|
150
|
-
if (this.isClosed) return;
|
|
151
|
-
try {
|
|
152
|
-
await this.host.connector.send("browser.removeUserContext", { userContext: this.id });
|
|
153
|
-
} catch (err) {
|
|
154
|
-
// Already gone: the goal is met
|
|
155
|
-
if (!(err instanceof BiDiError && err.code === "no such user context")) throw err;
|
|
156
|
-
}
|
|
157
|
-
this.isClosed = true;
|
|
158
|
-
const pages = this.opened;
|
|
159
|
-
this.opened = [];
|
|
160
|
-
await Promise.all(pages.map((page) => page.detach()));
|
|
161
|
-
this.host.forget(this);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
private partition(): PartitionDescriptor {
|
|
165
|
-
return { type: "storageKey", userContext: this.id };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private assertOpen() {
|
|
169
|
-
if (this.isClosed) throw new Error("context closed");
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function toBiDiCookie(input: CookieInput): PartialCookie {
|
|
174
|
-
let { domain, path, secure } = input;
|
|
175
|
-
if (input.url !== undefined) {
|
|
176
|
-
const url = new URL(input.url);
|
|
177
|
-
domain ??= url.hostname;
|
|
178
|
-
path ??= url.pathname;
|
|
179
|
-
secure ??= url.protocol === "https:";
|
|
180
|
-
}
|
|
181
|
-
if (domain === undefined) throw new TypeError(`cookie "${input.name}" needs a domain or a url`);
|
|
182
|
-
return {
|
|
183
|
-
name: input.name,
|
|
184
|
-
value: { type: "string", value: input.value },
|
|
185
|
-
domain,
|
|
186
|
-
path: path ?? "/",
|
|
187
|
-
...(secure !== undefined && { secure }),
|
|
188
|
-
...(input.httpOnly !== undefined && { httpOnly: input.httpOnly }),
|
|
189
|
-
...(input.sameSite !== undefined && { sameSite: input.sameSite }),
|
|
190
|
-
...(input.expiry !== undefined && { expiry: input.expiry }),
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function toCookie(cookie: BiDiCookie): Cookie {
|
|
195
|
-
return {
|
|
196
|
-
name: cookie.name,
|
|
197
|
-
value:
|
|
198
|
-
cookie.value.type === "base64"
|
|
199
|
-
? Buffer.from(cookie.value.value, "base64").toString("utf8")
|
|
200
|
-
: cookie.value.value,
|
|
201
|
-
domain: cookie.domain,
|
|
202
|
-
path: cookie.path,
|
|
203
|
-
httpOnly: cookie.httpOnly,
|
|
204
|
-
secure: cookie.secure,
|
|
205
|
-
sameSite: cookie.sameSite,
|
|
206
|
-
...(cookie.expiry !== undefined && { expiry: cookie.expiry }),
|
|
207
|
-
};
|
|
208
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { execSync } from "node:child_process";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import type { SupportedBrowser } from "../types/browser.js";
|
|
5
|
-
import logger from "../logger/index.js";
|
|
6
|
-
|
|
7
|
-
const supportedBrowsers: SupportedBrowser[] = ["firefox"];
|
|
8
|
-
const macBrowserPaths: Record<SupportedBrowser, string> = {
|
|
9
|
-
// chrome: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
10
|
-
firefox: "/Applications/Firefox.app/Contents/MacOS/Firefox",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function findBrowser(browserName: SupportedBrowser) {
|
|
14
|
-
logger.verbose("Checking install location for %s", browserName);
|
|
15
|
-
if (!supportedBrowsers.includes(browserName)) {
|
|
16
|
-
throw new Error(
|
|
17
|
-
"This browser is not supported. Supported browsers include: " +
|
|
18
|
-
supportedBrowsers.join(", "),
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let browserLocation = "";
|
|
23
|
-
const notFoundMsg = `Cannot find browser ${browserName} on this machine! Is it installed?`;
|
|
24
|
-
try {
|
|
25
|
-
browserLocation = tryFindBrowser(browserName);
|
|
26
|
-
} catch (err) {
|
|
27
|
-
throw new Error(notFoundMsg);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
assert(browserLocation, notFoundMsg);
|
|
31
|
-
logger.verbose("Browser found at: %s", browserLocation);
|
|
32
|
-
|
|
33
|
-
return browserLocation;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function tryFindBrowser(browserName: SupportedBrowser): string {
|
|
37
|
-
logger.debug("Current platform: %s", process.platform);
|
|
38
|
-
switch (process.platform) {
|
|
39
|
-
case "win32":
|
|
40
|
-
return findBrowserWindows(browserName);
|
|
41
|
-
case "darwin":
|
|
42
|
-
return findBrowserMac(browserName);
|
|
43
|
-
case "linux":
|
|
44
|
-
return findBrowserLinux(browserName);
|
|
45
|
-
default:
|
|
46
|
-
throw new Error("This platform is not supported");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function findBrowserWindows(browserName: SupportedBrowser): string {
|
|
51
|
-
// Function to prevent search the entire file system
|
|
52
|
-
// We'll only try the most common paths
|
|
53
|
-
const possibleDirs = ["Program\ Files", "Program\ Files (x86)"];
|
|
54
|
-
|
|
55
|
-
const where = (dirPath: string) => {
|
|
56
|
-
const command: string = `where /r ${dirPath} ${browserName}.exe`;
|
|
57
|
-
try {
|
|
58
|
-
logger.verbose("Running command: %s", command);
|
|
59
|
-
|
|
60
|
-
const whereResults = execSync(command, {
|
|
61
|
-
encoding: "utf8",
|
|
62
|
-
});
|
|
63
|
-
logger.debug("Command results: \n", whereResults);
|
|
64
|
-
const results = whereResults.split(/\n/g);
|
|
65
|
-
return results[0];
|
|
66
|
-
} catch (error) {
|
|
67
|
-
logger.verbose("Error while executing %s", command, { error });
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
let result = undefined;
|
|
72
|
-
|
|
73
|
-
for (const possibleDir of possibleDirs) {
|
|
74
|
-
logger.debug("Checking directory: %s", possibleDir);
|
|
75
|
-
result = where(possibleDir);
|
|
76
|
-
if (result) break;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!result) {
|
|
80
|
-
throw new Error("'where' yielded no results");
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function findBrowserMac(browserName: SupportedBrowser): string {
|
|
86
|
-
const checkPathExists = (path: string) => {
|
|
87
|
-
logger.debug("Checking path: %s", path);
|
|
88
|
-
const exists = existsSync(path);
|
|
89
|
-
if (!exists) {
|
|
90
|
-
throw new Error(`Did not find ${browserName} at ${path}`);
|
|
91
|
-
}
|
|
92
|
-
return path;
|
|
93
|
-
};
|
|
94
|
-
return checkPathExists(macBrowserPaths[browserName]);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const linuxBinaryNames: Record<SupportedBrowser, string> = {
|
|
98
|
-
// chrome: "google-chrome",
|
|
99
|
-
firefox: "firefox",
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
function findBrowserLinux(browserName: SupportedBrowser): string {
|
|
103
|
-
const command = `which ${linuxBinaryNames[browserName]}`;
|
|
104
|
-
logger.debug("Running command: %s", command);
|
|
105
|
-
const result = execSync(command, {
|
|
106
|
-
encoding: "utf8",
|
|
107
|
-
stdio: "pipe",
|
|
108
|
-
});
|
|
109
|
-
return result.replace(/\n/g, "");
|
|
110
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
import { autoReply, FakeWebSocket } from "../testing/fake-websocket.js";
|
|
4
|
-
import { BiDiConnector } from "../transport/bidi-connection.js";
|
|
5
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import { abandonLaunch, Browser, browserLaunchFlag, ensureBrowserProfile } from "./browser.js";
|
|
9
|
-
|
|
10
|
-
function fakeBrowser(handlers: Record<string, (params: any) => object | Error> = {}) {
|
|
11
|
-
const ws = new FakeWebSocket();
|
|
12
|
-
const connector = new BiDiConnector(ws as unknown as WebSocket);
|
|
13
|
-
const stop = autoReply(ws, { "browsingContext.getTree": () => ({ contexts: [] }), ...handlers });
|
|
14
|
-
const browser = new Browser({ browserProc: {} as never, biDiConnector: connector });
|
|
15
|
-
return { ws, browser, stop };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
test("newPage returns a page bound to the created context", async () => {
|
|
19
|
-
const { ws, browser, stop } = fakeBrowser({
|
|
20
|
-
"browsingContext.create": () => ({ context: "ctx-42", userContext: "default" }),
|
|
21
|
-
});
|
|
22
|
-
const page = await browser.newPage({ type: "tab" });
|
|
23
|
-
assert.equal(page.contextId, "ctx-42");
|
|
24
|
-
assert.deepEqual(
|
|
25
|
-
ws.sent.find((m) => m.method === "browsingContext.create")!.params,
|
|
26
|
-
{ type: "tab" },
|
|
27
|
-
);
|
|
28
|
-
stop();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("newPage passes background and userContext through", async () => {
|
|
32
|
-
const { ws, browser, stop } = fakeBrowser({
|
|
33
|
-
"browsingContext.create": () => ({ context: "c", userContext: "u1" }),
|
|
34
|
-
});
|
|
35
|
-
await browser.newPage({ type: "window", background: true, userContext: "u1" });
|
|
36
|
-
assert.deepEqual(ws.sent[0]!.params, { type: "window", background: true, userContext: "u1" });
|
|
37
|
-
stop();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("newPage surfaces a failing create as a BiDiError", async () => {
|
|
41
|
-
const { browser, stop } = fakeBrowser({
|
|
42
|
-
"browsingContext.create": () => new Error("no window"),
|
|
43
|
-
});
|
|
44
|
-
await assert.rejects(browser.newPage({ type: "tab" }), { name: "BiDiError", code: "unknown error" });
|
|
45
|
-
stop();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("firefox starts on about:blank, not its privileged home page", () => {
|
|
49
|
-
assert.deepEqual(browserLaunchFlag("firefox", { port: 9300, profileDir: "/tmp/profile", headless: true }), [
|
|
50
|
-
"--no-sandbox",
|
|
51
|
-
"--no-remote",
|
|
52
|
-
"--headless",
|
|
53
|
-
"--remote-debugging-port=9300",
|
|
54
|
-
"--profile",
|
|
55
|
-
"/tmp/profile",
|
|
56
|
-
"about:blank",
|
|
57
|
-
]);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("a failed launch stops the connection and the browser it started", () => {
|
|
61
|
-
const calls: string[] = [];
|
|
62
|
-
abandonLaunch({ kill: () => calls.push("browser") > 0 }, { kill: () => void calls.push("connector") });
|
|
63
|
-
assert.deepEqual(calls, ["connector", "browser"]);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("a launch that failed before connecting still stops the browser", () => {
|
|
67
|
-
const calls: string[] = [];
|
|
68
|
-
abandonLaunch({ kill: () => calls.push("browser") > 0 }, undefined);
|
|
69
|
-
assert.deepEqual(calls, ["browser"]);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("the firefox profile is written with a blank start page, and an outdated one is rewritten", () => {
|
|
73
|
-
const dir = mkdtempSync(path.join(tmpdir(), "samurai-profile-"));
|
|
74
|
-
try {
|
|
75
|
-
const userJs = path.join(dir, "firefox", "user.js");
|
|
76
|
-
ensureBrowserProfile("firefox", userJs);
|
|
77
|
-
const written = readFileSync(userJs, "utf8");
|
|
78
|
-
assert.match(written, /user_pref\("browser\.startup\.page", 0\);/);
|
|
79
|
-
assert.match(written, /user_pref\("browser\.startup\.homepage", "about:blank"\);/);
|
|
80
|
-
assert.match(written, /user_pref\("browser\.newtabpage\.enabled", false\);/);
|
|
81
|
-
|
|
82
|
-
writeFileSync(userJs, 'user_pref("devtools.chrome.enabled", true);');
|
|
83
|
-
ensureBrowserProfile("firefox", userJs);
|
|
84
|
-
assert.equal(readFileSync(userJs, "utf8"), written);
|
|
85
|
-
} finally {
|
|
86
|
-
rmSync(dir, { recursive: true, force: true });
|
|
87
|
-
}
|
|
88
|
-
});
|