@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
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { glob } from "node:fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
|
+
import { Browser } from "../browser/browser.js";
|
|
6
|
+
import { projectDir, readConfig } from "../config/config.js";
|
|
7
|
+
import logger from "../logger/index.js";
|
|
8
|
+
import { assertModuleProject } from "./module-type.js";
|
|
9
|
+
import { clearRegistry, registeredTests } from "./registry.js";
|
|
10
|
+
import { createEnvFixture } from "../config/variables.js";
|
|
11
|
+
import { createSecretsFixture } from "../config/secrets.js";
|
|
12
|
+
import TestReporter, {} from "./reporter.js";
|
|
13
|
+
import Page from "../browser/page.js";
|
|
14
|
+
import { takePendingAssertions } from "../assert/expect.js";
|
|
15
|
+
import { toTestError } from "./test-error.js";
|
|
16
|
+
import { judgePageLogs } from "./page-logs-report.js";
|
|
17
|
+
/** Whether a test with this full name is selected by the options */
|
|
18
|
+
export function isSelected(name, { grep, testNames }) {
|
|
19
|
+
if (testNames && !testNames.includes(name))
|
|
20
|
+
return false;
|
|
21
|
+
if (grep === undefined)
|
|
22
|
+
return true;
|
|
23
|
+
if (typeof grep === "string")
|
|
24
|
+
return name.includes(grep);
|
|
25
|
+
grep.lastIndex = 0;
|
|
26
|
+
return grep.test(name);
|
|
27
|
+
}
|
|
28
|
+
export default class TestRunner {
|
|
29
|
+
constructor(testFiles, run, group, options = {}) {
|
|
30
|
+
this.testFiles = testFiles;
|
|
31
|
+
this.run = run;
|
|
32
|
+
this.group = group;
|
|
33
|
+
this.options = options;
|
|
34
|
+
}
|
|
35
|
+
static async init(run, group, options = {}) {
|
|
36
|
+
logger.verbose("Initializing test runner");
|
|
37
|
+
let srcDir = "";
|
|
38
|
+
if (group && group.src) {
|
|
39
|
+
srcDir = group.src;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
srcDir = (await readConfig("srcDir")) ?? "./src";
|
|
43
|
+
}
|
|
44
|
+
assert(srcDir, "No srcDir declared!");
|
|
45
|
+
const cwd = path.resolve(projectDir(), srcDir);
|
|
46
|
+
if (options.files) {
|
|
47
|
+
return new TestRunner(options.files.map((file) => path.resolve(projectDir(), file)), run, group, options);
|
|
48
|
+
}
|
|
49
|
+
logger.verbose("Reading test files from: %s", cwd);
|
|
50
|
+
const testFiles = [];
|
|
51
|
+
for await (const entry of glob("**/*.spec.ts", {
|
|
52
|
+
cwd,
|
|
53
|
+
})) {
|
|
54
|
+
testFiles.push(entry);
|
|
55
|
+
}
|
|
56
|
+
logger.debug("Found %d tests in srcDir", testFiles.length);
|
|
57
|
+
return new TestRunner(testFiles.map((file) => path.join(cwd, file)), run, group, options);
|
|
58
|
+
}
|
|
59
|
+
/** Runs the selected tests and returns the summary that was also written to the report */
|
|
60
|
+
/**
|
|
61
|
+
* Imports the spec files and returns the tests the options select. A fresh registry and a fresh import of each
|
|
62
|
+
* file, so a second run in this process registers its tests again (and sees edited specs, for files loaded as
|
|
63
|
+
* ES modules; CommonJS files stay cached until the process ends)
|
|
64
|
+
*/
|
|
65
|
+
async register() {
|
|
66
|
+
clearRegistry();
|
|
67
|
+
const stamp = `?run=${Date.now().toString(36)}`;
|
|
68
|
+
for (const file of this.testFiles)
|
|
69
|
+
assertModuleProject(file);
|
|
70
|
+
for (const file of this.testFiles) {
|
|
71
|
+
logger.verbose("Trying to register file: %s", file);
|
|
72
|
+
await import(pathToFileURL(file).href + stamp);
|
|
73
|
+
}
|
|
74
|
+
// TODO: Implement test grouping
|
|
75
|
+
return registeredTests().filter((test) => isSelected(test.name, this.options));
|
|
76
|
+
}
|
|
77
|
+
/** Runs the selected tests and returns the summary that was also written to the report */
|
|
78
|
+
async start() {
|
|
79
|
+
const { reportPath, onEvent, signal } = this.options;
|
|
80
|
+
const reporter = new TestReporter({
|
|
81
|
+
environment: this.run.settings.environment,
|
|
82
|
+
...(reportPath !== undefined && { output: reportPath }),
|
|
83
|
+
...(onEvent && { onEvent }),
|
|
84
|
+
});
|
|
85
|
+
const selected = await this.register();
|
|
86
|
+
reporter.onStart(selected.length);
|
|
87
|
+
for (const test of selected) {
|
|
88
|
+
if (signal?.aborted) {
|
|
89
|
+
reporter.onTestStart(test);
|
|
90
|
+
reporter.onTestEnd(test, { message: "Run aborted", type: "error" });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
await this.executeTestCase(test, reporter);
|
|
94
|
+
}
|
|
95
|
+
return reporter.onEnd();
|
|
96
|
+
}
|
|
97
|
+
async executeTestCase(test, reporter) {
|
|
98
|
+
logger.verbose("Starting test: %s", test.name);
|
|
99
|
+
reporter.onTestStart(test);
|
|
100
|
+
const selectedBrowser = (await readConfig("browser")) ?? "firefox";
|
|
101
|
+
const { settings, secrets } = this.run;
|
|
102
|
+
const timeout = settings.timeout;
|
|
103
|
+
let browser;
|
|
104
|
+
let page;
|
|
105
|
+
let timeoutTimer;
|
|
106
|
+
let onRunAborted;
|
|
107
|
+
const timeoutController = new AbortController();
|
|
108
|
+
await Promise.race([
|
|
109
|
+
new Promise((resolve) => {
|
|
110
|
+
timeoutTimer = setTimeout(() => {
|
|
111
|
+
reporter.onTestEnd(test, {
|
|
112
|
+
message: `Test timed out after ${timeout / 1000} seconds`,
|
|
113
|
+
type: "timeout",
|
|
114
|
+
});
|
|
115
|
+
timeoutController.abort(new Error("Test timed out"));
|
|
116
|
+
resolve();
|
|
117
|
+
}, timeout);
|
|
118
|
+
onRunAborted = () => {
|
|
119
|
+
if (timeoutController.signal.aborted)
|
|
120
|
+
return;
|
|
121
|
+
reporter.onTestEnd(test, { message: "Run aborted", type: "error" });
|
|
122
|
+
timeoutController.abort(new Error("Run aborted"));
|
|
123
|
+
resolve();
|
|
124
|
+
};
|
|
125
|
+
this.options.signal?.addEventListener("abort", onRunAborted, {
|
|
126
|
+
once: true,
|
|
127
|
+
});
|
|
128
|
+
}),
|
|
129
|
+
new Promise(async (resolve, reject) => {
|
|
130
|
+
try {
|
|
131
|
+
const launched = await Browser.launch(selectedBrowser, {
|
|
132
|
+
port: this.options.port ?? 9223,
|
|
133
|
+
headless: this.options.headless ?? false,
|
|
134
|
+
}, timeoutController.signal);
|
|
135
|
+
browser = launched.browser;
|
|
136
|
+
page = launched.page;
|
|
137
|
+
const fixtures = {
|
|
138
|
+
page: launched.page,
|
|
139
|
+
browser: launched.browser,
|
|
140
|
+
env: createEnvFixture(settings.environment, settings.variables),
|
|
141
|
+
secrets: createSecretsFixture(settings.environment, secrets),
|
|
142
|
+
};
|
|
143
|
+
await test.function(fixtures);
|
|
144
|
+
// Already reported as timed out
|
|
145
|
+
if (timeoutController.signal.aborted)
|
|
146
|
+
return resolve();
|
|
147
|
+
const unawaited = takePendingAssertions();
|
|
148
|
+
if (unawaited.length > 0) {
|
|
149
|
+
reporter.onTestEnd(test, {
|
|
150
|
+
message: unawaited
|
|
151
|
+
.map((matcher) => `expect(locator).${matcher}() was not awaited`)
|
|
152
|
+
.join("\n"),
|
|
153
|
+
type: "assertion",
|
|
154
|
+
}, await judgeLogs(page, true));
|
|
155
|
+
return resolve();
|
|
156
|
+
}
|
|
157
|
+
const verdict = await judgeLogs(page, false);
|
|
158
|
+
reporter.onTestEnd(test, verdict.error, verdict);
|
|
159
|
+
resolve();
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
if (timeoutController.signal.aborted)
|
|
163
|
+
return resolve();
|
|
164
|
+
reporter.onTestEnd(test, toTestError(err), await judgeLogs(page, true));
|
|
165
|
+
resolve();
|
|
166
|
+
}
|
|
167
|
+
}),
|
|
168
|
+
]);
|
|
169
|
+
clearTimeout(timeoutTimer);
|
|
170
|
+
if (onRunAborted)
|
|
171
|
+
this.options.signal?.removeEventListener("abort", onRunAborted);
|
|
172
|
+
takePendingAssertions();
|
|
173
|
+
logger.verbose("Closing browser for test: %s", test.name);
|
|
174
|
+
await browser?.close();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Reads the page's log once the test body is done and applies config `logs` (see judgePageLogs) */
|
|
178
|
+
async function judgeLogs(page, testFailed) {
|
|
179
|
+
if (!page)
|
|
180
|
+
return {};
|
|
181
|
+
try {
|
|
182
|
+
// Events and command replies share one ordered connection: after this, everything logged so far is in
|
|
183
|
+
await page.syncLogs();
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
logger.debug("Could not sync the page's logs", { err });
|
|
187
|
+
}
|
|
188
|
+
let config;
|
|
189
|
+
try {
|
|
190
|
+
config = await readConfig("logs");
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
logger.debug("Could not read logs config, using defaults", { err });
|
|
194
|
+
}
|
|
195
|
+
return judgePageLogs({
|
|
196
|
+
config,
|
|
197
|
+
entries: page.getLogs(),
|
|
198
|
+
errors: page.pageErrors(),
|
|
199
|
+
dropped: page.logsDropped,
|
|
200
|
+
testFailed,
|
|
201
|
+
allowPageErrors: page.pageErrorsAllowed,
|
|
202
|
+
routeErrors: page.routeErrors(),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
export { describe, test } from "./registry.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
import type { StackTrace } from "../types/bidi-modules/script.js";
|
|
3
|
+
import { type Arg } from "./serialize.js";
|
|
4
|
+
export interface CallOptions {
|
|
5
|
+
/** @default true. The auto-wait probes pass false */
|
|
6
|
+
awaitPromise?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* "root" keeps a handle alive in the browser; the result then comes back as a `RemoteObject`
|
|
9
|
+
* carrying it, and the caller must `disown` it. @default "none"
|
|
10
|
+
*/
|
|
11
|
+
ownership?: "none" | "root";
|
|
12
|
+
/** Named sandbox realm to run in (see the preload-scripts spec) */
|
|
13
|
+
sandbox?: string;
|
|
14
|
+
thisArg?: Arg;
|
|
15
|
+
}
|
|
16
|
+
/** The page function threw */
|
|
17
|
+
export declare class ScriptError extends Error {
|
|
18
|
+
/** The browser's description, e.g. "TypeError: el is null" */
|
|
19
|
+
readonly text: string;
|
|
20
|
+
readonly stackTrace: StackTrace | undefined;
|
|
21
|
+
/** Start of the function that threw */
|
|
22
|
+
readonly functionSource: string;
|
|
23
|
+
constructor(text: string, functionSource: string, stackTrace?: StackTrace);
|
|
24
|
+
}
|
|
25
|
+
/** A function cannot be sent as source */
|
|
26
|
+
export declare class ScriptSerializationError extends Error {
|
|
27
|
+
constructor(message: string);
|
|
28
|
+
}
|
|
29
|
+
/** The source to send for `fn`; a function object must not depend on transpiler helpers */
|
|
30
|
+
export declare function toFunctionDeclaration(fn: string | ((...args: any[]) => unknown)): string;
|
|
31
|
+
/** Calls `fn` in the page with serialized arguments and returns its deserialized result */
|
|
32
|
+
export declare function callFunction<T = unknown>(connector: BiDiConnector, context: string, fn: string | ((...args: any[]) => unknown), args?: Arg[], options?: CallOptions): Promise<T>;
|
|
33
|
+
/** Releases handles obtained with `ownership: "root"`; a handle that is already gone is fine */
|
|
34
|
+
export declare function disown(connector: BiDiConnector, context: string, handles: string[]): Promise<void>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BiDiError } from "../transport/bidi-error.js";
|
|
2
|
+
import { fromRemoteValue, toLocalValue } from "./serialize.js";
|
|
3
|
+
/** How much of a function's source `ScriptError` keeps */
|
|
4
|
+
const SOURCE_PREVIEW = 200;
|
|
5
|
+
/** The page function threw */
|
|
6
|
+
export class ScriptError extends Error {
|
|
7
|
+
constructor(text, functionSource, stackTrace) {
|
|
8
|
+
super(`Script threw: ${text}`);
|
|
9
|
+
this.name = "ScriptError";
|
|
10
|
+
this.text = text;
|
|
11
|
+
this.stackTrace = stackTrace;
|
|
12
|
+
this.functionSource =
|
|
13
|
+
functionSource.length > SOURCE_PREVIEW
|
|
14
|
+
? functionSource.slice(0, SOURCE_PREVIEW) + "…"
|
|
15
|
+
: functionSource;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** A function cannot be sent as source */
|
|
19
|
+
export class ScriptSerializationError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "ScriptSerializationError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** The source to send for `fn`; a function object must not depend on transpiler helpers */
|
|
26
|
+
export function toFunctionDeclaration(fn) {
|
|
27
|
+
if (typeof fn === "string")
|
|
28
|
+
return fn;
|
|
29
|
+
const source = fn.toString();
|
|
30
|
+
// tsx/esbuild wrap named functions in a __name() helper that does not exist in the page
|
|
31
|
+
if (source.includes("__name(")) {
|
|
32
|
+
throw new ScriptSerializationError("The function's source uses the transpiler's __name helper, which does not exist in the page. " +
|
|
33
|
+
"Use an arrow function without inner named functions, or pass the function as a string.");
|
|
34
|
+
}
|
|
35
|
+
return source;
|
|
36
|
+
}
|
|
37
|
+
/** Calls `fn` in the page with serialized arguments and returns its deserialized result */
|
|
38
|
+
export async function callFunction(connector, context, fn, args = [], options = {}) {
|
|
39
|
+
const functionDeclaration = toFunctionDeclaration(fn);
|
|
40
|
+
const ownership = options.ownership ?? "none";
|
|
41
|
+
// Serialize first: a bad argument must fail before anything is sent
|
|
42
|
+
const serialized = args.map(toLocalValue);
|
|
43
|
+
const thisValue = "thisArg" in options ? toLocalValue(options.thisArg) : undefined;
|
|
44
|
+
const result = await connector.send("script.callFunction", {
|
|
45
|
+
functionDeclaration,
|
|
46
|
+
awaitPromise: options.awaitPromise ?? true,
|
|
47
|
+
target: options.sandbox === undefined
|
|
48
|
+
? { context }
|
|
49
|
+
: { context, sandbox: options.sandbox },
|
|
50
|
+
...(serialized.length > 0 && { arguments: serialized }),
|
|
51
|
+
resultOwnership: ownership,
|
|
52
|
+
...(thisValue !== undefined && { this: thisValue }),
|
|
53
|
+
});
|
|
54
|
+
if (result.type === "exception") {
|
|
55
|
+
throw new ScriptError(result.exceptionDetails.text, functionDeclaration, result.exceptionDetails.stackTrace);
|
|
56
|
+
}
|
|
57
|
+
return fromRemoteValue(result.result, { keepHandles: ownership === "root" });
|
|
58
|
+
}
|
|
59
|
+
/** Releases handles obtained with `ownership: "root"`; a handle that is already gone is fine */
|
|
60
|
+
export async function disown(connector, context, handles) {
|
|
61
|
+
if (handles.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
await connector.send("script.disown", { handles, target: { context } });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err instanceof BiDiError && err.code === "no such handle")
|
|
68
|
+
return;
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reference to a DOM node in the browser. The `sharedId` stays valid across scripts in the
|
|
3
|
+
* same document; after navigation or removal the browser answers `no such node`.
|
|
4
|
+
* Internal: user code keeps using `Locator`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ElementHandle {
|
|
7
|
+
readonly sharedId: string;
|
|
8
|
+
readonly handle?: string | undefined;
|
|
9
|
+
constructor(sharedId: string, handle?: string | undefined);
|
|
10
|
+
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Internal: user code keeps using `Locator`.
|
|
5
5
|
*/
|
|
6
6
|
export class ElementHandle {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
constructor(sharedId, handle) {
|
|
8
|
+
this.sharedId = sharedId;
|
|
9
|
+
this.handle = handle;
|
|
10
|
+
}
|
|
11
11
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
/** Sandbox realm holding the framework's helpers: same DOM as the page, separate JS globals */
|
|
3
|
+
export declare const HELPER_SANDBOX = "samurai";
|
|
4
|
+
/** What `CALL_PROBE` answers with when the helpers are not installed in the document */
|
|
5
|
+
export declare const HELPERS_MISSING = "samurai:helpers-missing";
|
|
6
|
+
/** Installs `globalThis.__samurai` in the sandbox realm of a document */
|
|
7
|
+
export declare const HELPERS_SOURCE: string;
|
|
8
|
+
/** Calls the installed probe, or reports that the helpers are missing instead of throwing */
|
|
9
|
+
export declare const CALL_PROBE: string;
|
|
10
|
+
/** What the locator needs from the helper realm */
|
|
11
|
+
export interface HelperInstaller {
|
|
12
|
+
/** Resolves once the helpers are registered for the page; installs on first use */
|
|
13
|
+
ensureInstalled(): Promise<void>;
|
|
14
|
+
/** Runs the helpers again in the loaded documents; used when a call found them missing */
|
|
15
|
+
reinstall(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** The framework's helper library for one page: registered once, applied to every document */
|
|
18
|
+
export declare class HelperRealm implements HelperInstaller {
|
|
19
|
+
private connector;
|
|
20
|
+
private contextId;
|
|
21
|
+
private installed;
|
|
22
|
+
constructor(connector: BiDiConnector, contextId: string);
|
|
23
|
+
ensureInstalled(): Promise<void>;
|
|
24
|
+
reinstall(): Promise<void>;
|
|
25
|
+
/** Removes the registration; nothing is sent when the helpers were never installed */
|
|
26
|
+
dispose(): Promise<void>;
|
|
27
|
+
private options;
|
|
28
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PROBE_ELEMENT } from "../locator/element-state.js";
|
|
2
|
+
import { addPreload, runInLoadedContexts } from "./preload.js";
|
|
3
|
+
/** Sandbox realm holding the framework's helpers: same DOM as the page, separate JS globals */
|
|
4
|
+
export const HELPER_SANDBOX = "samurai";
|
|
5
|
+
/** What `CALL_PROBE` answers with when the helpers are not installed in the document */
|
|
6
|
+
export const HELPERS_MISSING = "samurai:helpers-missing";
|
|
7
|
+
/** Installs `globalThis.__samurai` in the sandbox realm of a document */
|
|
8
|
+
export const HELPERS_SOURCE = `() => {
|
|
9
|
+
globalThis.__samurai = { probeElement: ${PROBE_ELEMENT} };
|
|
10
|
+
}`;
|
|
11
|
+
/** Calls the installed probe, or reports that the helpers are missing instead of throwing */
|
|
12
|
+
export const CALL_PROBE = `(...args) => {
|
|
13
|
+
const helpers = globalThis.__samurai;
|
|
14
|
+
return helpers ? helpers.probeElement(...args) : ${JSON.stringify(HELPERS_MISSING)};
|
|
15
|
+
}`;
|
|
16
|
+
/** The framework's helper library for one page: registered once, applied to every document */
|
|
17
|
+
export class HelperRealm {
|
|
18
|
+
constructor(connector, contextId) {
|
|
19
|
+
this.connector = connector;
|
|
20
|
+
this.contextId = contextId;
|
|
21
|
+
}
|
|
22
|
+
ensureInstalled() {
|
|
23
|
+
this.installed ??= addPreload(this.connector, this.options()).catch((err) => {
|
|
24
|
+
this.installed = undefined;
|
|
25
|
+
throw err;
|
|
26
|
+
});
|
|
27
|
+
return this.installed.then(() => undefined);
|
|
28
|
+
}
|
|
29
|
+
async reinstall() {
|
|
30
|
+
if (!this.installed)
|
|
31
|
+
return this.ensureInstalled();
|
|
32
|
+
await this.installed;
|
|
33
|
+
await runInLoadedContexts(this.connector, this.options());
|
|
34
|
+
}
|
|
35
|
+
/** Removes the registration; nothing is sent when the helpers were never installed */
|
|
36
|
+
async dispose() {
|
|
37
|
+
const installed = this.installed;
|
|
38
|
+
this.installed = undefined;
|
|
39
|
+
if (!installed)
|
|
40
|
+
return;
|
|
41
|
+
await (await installed).dispose();
|
|
42
|
+
}
|
|
43
|
+
options() {
|
|
44
|
+
return {
|
|
45
|
+
source: HELPERS_SOURCE,
|
|
46
|
+
contexts: [this.contextId],
|
|
47
|
+
sandbox: HELPER_SANDBOX,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
import { type ChannelArg } from "./serialize.js";
|
|
3
|
+
export interface PreloadHandle {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
/** Idempotent. Documents that are already loaded keep the effect until they navigate */
|
|
6
|
+
dispose(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface RunOptions {
|
|
9
|
+
/** A function declaration, e.g. `() => { … }` */
|
|
10
|
+
source: string;
|
|
11
|
+
/** Top-level contexts to cover, including their iframes. Default: every top-level context */
|
|
12
|
+
contexts?: string[];
|
|
13
|
+
/** Named sandbox realm; without it the script runs in the page's own realm */
|
|
14
|
+
sandbox?: string;
|
|
15
|
+
/** Channels the function receives as arguments, in the preload registration and in the immediate run */
|
|
16
|
+
arguments?: ChannelArg[];
|
|
17
|
+
/**
|
|
18
|
+
* What to do when the immediate run in an already-loaded document throws.
|
|
19
|
+
* "log" suits user init scripts (the browser reports such errors for later documents itself)
|
|
20
|
+
* @default "throw"
|
|
21
|
+
*/
|
|
22
|
+
onRunError?: "throw" | "log";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Registers `source` to run before any page script in every future document of `contexts`, and
|
|
26
|
+
* also runs it once in the documents that are loaded now: preload scripts only affect documents
|
|
27
|
+
* created after registration, and callers should not have to care about that ordering.
|
|
28
|
+
*/
|
|
29
|
+
export declare function addPreload(connector: BiDiConnector, options: RunOptions): Promise<PreloadHandle>;
|
|
30
|
+
/** Runs `source` once in every loaded context (iframes included) under `contexts` */
|
|
31
|
+
export declare function runInLoadedContexts(connector: BiDiConnector, { source, contexts, sandbox, onRunError, arguments: args, }: RunOptions): Promise<void>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logger from "../logger/index.js";
|
|
2
|
+
import { BiDiError } from "../transport/bidi-error.js";
|
|
3
|
+
import { callFunction, ScriptError } from "./call-function.js";
|
|
4
|
+
import { toLocalValue } from "./serialize.js";
|
|
5
|
+
/**
|
|
6
|
+
* Registers `source` to run before any page script in every future document of `contexts`, and
|
|
7
|
+
* also runs it once in the documents that are loaded now: preload scripts only affect documents
|
|
8
|
+
* created after registration, and callers should not have to care about that ordering.
|
|
9
|
+
*/
|
|
10
|
+
export async function addPreload(connector, options) {
|
|
11
|
+
const { script } = await connector.send("script.addPreloadScript", {
|
|
12
|
+
functionDeclaration: options.source,
|
|
13
|
+
...(options.contexts && { contexts: options.contexts }),
|
|
14
|
+
...(options.sandbox !== undefined && { sandbox: options.sandbox }),
|
|
15
|
+
...(options.arguments && {
|
|
16
|
+
arguments: options.arguments.map((arg) => toLocalValue(arg)),
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
let removed = false;
|
|
20
|
+
const dispose = async () => {
|
|
21
|
+
if (removed)
|
|
22
|
+
return;
|
|
23
|
+
removed = true;
|
|
24
|
+
try {
|
|
25
|
+
await connector.send("script.removePreloadScript", { script });
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err instanceof BiDiError && err.code === "no such script")
|
|
29
|
+
return;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
// Registered first, so a document created while this runs is covered by the registration
|
|
35
|
+
await runInLoadedContexts(connector, options);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
await dispose().catch((disposeErr) => logger.debug("Could not remove preload script after a failed run", {
|
|
39
|
+
disposeErr,
|
|
40
|
+
}));
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
return { id: script, dispose };
|
|
44
|
+
}
|
|
45
|
+
/** Runs `source` once in every loaded context (iframes included) under `contexts` */
|
|
46
|
+
export async function runInLoadedContexts(connector, { source, contexts, sandbox, onRunError = "throw", arguments: args = [], }) {
|
|
47
|
+
const trees = contexts
|
|
48
|
+
? await Promise.all(contexts.map((root) => connector.send("browsingContext.getTree", { root })))
|
|
49
|
+
: [await connector.send("browsingContext.getTree", {})];
|
|
50
|
+
const loaded = trees.flatMap((tree) => tree.contexts.flatMap(flatten));
|
|
51
|
+
const outcomes = await Promise.allSettled(loaded.map((context) => callFunction(connector, context, source, args, {
|
|
52
|
+
awaitPromise: false,
|
|
53
|
+
...(sandbox !== undefined && { sandbox }),
|
|
54
|
+
})));
|
|
55
|
+
for (const outcome of outcomes) {
|
|
56
|
+
if (outcome.status === "fulfilled")
|
|
57
|
+
continue;
|
|
58
|
+
const err = outcome.reason;
|
|
59
|
+
// The context closed between listing and running: nothing left to cover
|
|
60
|
+
if (err instanceof BiDiError && err.code === "no such frame")
|
|
61
|
+
continue;
|
|
62
|
+
if (err instanceof ScriptError && onRunError === "log") {
|
|
63
|
+
logger.warn("Script threw while running in a loaded document: %s", err.text);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function flatten(info) {
|
|
70
|
+
return [info.context, ...(info.children ?? []).flatMap(flatten)];
|
|
71
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ArgumentValue, RemoteValue } from "../types/bidi-modules/script.js";
|
|
2
|
+
/** A value passed to a page function */
|
|
3
|
+
export type Arg = unknown;
|
|
4
|
+
/** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by type and handle */
|
|
5
|
+
export declare class RemoteObject {
|
|
6
|
+
readonly type: string;
|
|
7
|
+
readonly handle?: string | undefined;
|
|
8
|
+
/** The deserialized value, when the browser sent one and handles were kept */
|
|
9
|
+
readonly value?: unknown | undefined;
|
|
10
|
+
constructor(type: string, handle?: string | undefined,
|
|
11
|
+
/** The deserialized value, when the browser sent one and handles were kept */
|
|
12
|
+
value?: unknown | undefined);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A channel the page function can call as `send(value)`; the browser delivers each value as a
|
|
16
|
+
* `script.message` event. Only meaningful as a preload script or function argument.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ChannelArg {
|
|
19
|
+
readonly channel: string;
|
|
20
|
+
constructor(channel: string);
|
|
21
|
+
}
|
|
22
|
+
/** Serializes `value` for `script.callFunction` arguments; throws TypeError for what cannot cross the wire */
|
|
23
|
+
export declare function toLocalValue(value: Arg): ArgumentValue;
|
|
24
|
+
export interface DeserializeOptions {
|
|
25
|
+
/** Return objects that carry a handle as `RemoteObject`s (with `.value`), so the caller can disown them later */
|
|
26
|
+
keepHandles?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Converts a remote value to plain JS; what cannot be copied becomes a `RemoteObject` */
|
|
29
|
+
export declare function fromRemoteValue(value: RemoteValue, options?: DeserializeOptions): unknown;
|