@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,144 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { after, before, test } from "node:test";
|
|
3
|
-
import { callFunction } from "../script/call-function.js";
|
|
4
|
-
import { setContent } from "../testing/browser-fixture.js";
|
|
5
|
-
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
6
|
-
import { Browser } from "./browser.js";
|
|
7
|
-
import type { Dialog } from "./dialog.js";
|
|
8
|
-
import type Page from "./page.js";
|
|
9
|
-
|
|
10
|
-
let browser: Browser;
|
|
11
|
-
let page: Page;
|
|
12
|
-
|
|
13
|
-
before(async () => {
|
|
14
|
-
({ browser, page } = await Browser.launch("firefox", { port: 9239, headless: true }));
|
|
15
|
-
await page.navigateTo("about:blank");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
after(async () => {
|
|
19
|
-
await page.dispose();
|
|
20
|
-
await browser?.close();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const OPTIONS = { timeout: 30000 };
|
|
24
|
-
const connector = () => (page as unknown as { biDiConnector: BiDiConnector }).biDiConnector;
|
|
25
|
-
const run = <T>(source: string) => callFunction<T>(connector(), page.contextId, source, [], { awaitPromise: false });
|
|
26
|
-
const read = <T>(name: string) => run<T>(`() => window.${name}`);
|
|
27
|
-
|
|
28
|
-
/** Starts `code` in the page without waiting for it: a dialog inside would block the command */
|
|
29
|
-
const start = (code: string) => run(`() => { setTimeout(() => { ${code} }, 0); }`);
|
|
30
|
-
|
|
31
|
-
async function until<T>(read: () => Promise<T | undefined>, what: string, ms = 5000): Promise<T> {
|
|
32
|
-
const deadline = Date.now() + ms;
|
|
33
|
-
for (;;) {
|
|
34
|
-
const value = await read();
|
|
35
|
-
if (value !== undefined) return value;
|
|
36
|
-
if (Date.now() > deadline) throw new Error(`timed out waiting for ${what}`);
|
|
37
|
-
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
test("an alert is reported and holds the page until it is accepted", OPTIONS, async () => {
|
|
42
|
-
const seen: string[] = [];
|
|
43
|
-
const onDialog = async (dialog: Dialog) => {
|
|
44
|
-
seen.push(`${dialog.type()}: ${dialog.message()}`);
|
|
45
|
-
assert.equal(await read("__afterAlert"), undefined, "the page is still stuck in alert()");
|
|
46
|
-
await dialog.accept();
|
|
47
|
-
};
|
|
48
|
-
page.on("dialog", onDialog);
|
|
49
|
-
await start(`alert("Saved!"); window.__afterAlert = true;`);
|
|
50
|
-
assert.equal(await until(() => read<boolean | undefined>("__afterAlert"), "the page to go on"), true);
|
|
51
|
-
assert.deepEqual(seen, ["alert: Saved!"]);
|
|
52
|
-
page.off("dialog", onDialog);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("confirm returns true when accepted and false when dismissed", OPTIONS, async () => {
|
|
56
|
-
const answers = [true, false];
|
|
57
|
-
const onDialog = (dialog: Dialog) => (answers.shift() ? dialog.accept() : dialog.dismiss());
|
|
58
|
-
page.on("dialog", onDialog);
|
|
59
|
-
await start(`window.__yes = confirm("Sure?");`);
|
|
60
|
-
assert.equal(await until(() => read<boolean | undefined>("__yes"), "the first confirm"), true);
|
|
61
|
-
await start(`window.__no = confirm("Sure?");`);
|
|
62
|
-
assert.equal(await until(() => read<boolean | undefined>("__no"), "the second confirm"), false);
|
|
63
|
-
page.off("dialog", onDialog);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("a prompt shows its default value and returns the text that was typed", OPTIONS, async () => {
|
|
67
|
-
let shown = "";
|
|
68
|
-
const onDialog = (dialog: Dialog) => {
|
|
69
|
-
shown = `${dialog.message()} / ${dialog.defaultValue()}`;
|
|
70
|
-
return dialog.accept("Sam");
|
|
71
|
-
};
|
|
72
|
-
page.on("dialog", onDialog);
|
|
73
|
-
await start(`window.__name = prompt("Name?", "Anonymous");`);
|
|
74
|
-
assert.equal(await until(() => read<string | undefined>("__name"), "the prompt"), "Sam");
|
|
75
|
-
assert.equal(shown, "Name? / Anonymous");
|
|
76
|
-
page.off("dialog", onDialog);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("a dismissed prompt returns null", OPTIONS, async () => {
|
|
80
|
-
const onDialog = (dialog: Dialog) => dialog.dismiss();
|
|
81
|
-
page.on("dialog", onDialog);
|
|
82
|
-
await start(`window.__cancelled = prompt("Name?") === null;`);
|
|
83
|
-
assert.equal(await until(() => read<boolean | undefined>("__cancelled"), "the prompt"), true);
|
|
84
|
-
page.off("dialog", onDialog);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("closed resolves with what the browser reports", OPTIONS, async () => {
|
|
88
|
-
let closed: Promise<unknown> | undefined;
|
|
89
|
-
const onDialog = async (dialog: Dialog) => {
|
|
90
|
-
closed = dialog.closed;
|
|
91
|
-
await dialog.accept("typed");
|
|
92
|
-
};
|
|
93
|
-
page.on("dialog", onDialog);
|
|
94
|
-
await start(`window.__r = prompt("x");`);
|
|
95
|
-
await until(() => read<string | undefined>("__r"), "the prompt");
|
|
96
|
-
assert.deepEqual(await closed, { accepted: true, userText: "typed" });
|
|
97
|
-
page.off("dialog", onDialog);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("an alert nobody handles is dismissed, so a click on its button does not hang", OPTIONS, async () => {
|
|
101
|
-
await setContent(
|
|
102
|
-
page,
|
|
103
|
-
`<button id="b">Go</button>`,
|
|
104
|
-
`document.getElementById("b").addEventListener("click", () => { alert("Unhandled"); window.__afterUnhandled = true; });`,
|
|
105
|
-
);
|
|
106
|
-
await page.locator("button[@id='b']").click({ timeout: 10000 });
|
|
107
|
-
assert.equal(await until(() => read<boolean | undefined>("__afterUnhandled"), "the page to go on"), true);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("a dialog opened by an iframe is handled too", OPTIONS, async () => {
|
|
111
|
-
await page.navigateTo("about:blank?frame");
|
|
112
|
-
const seen: string[] = [];
|
|
113
|
-
const onDialog = async (dialog: Dialog) => {
|
|
114
|
-
seen.push(dialog.message());
|
|
115
|
-
await dialog.accept();
|
|
116
|
-
};
|
|
117
|
-
page.on("dialog", onDialog);
|
|
118
|
-
await run(`() => {
|
|
119
|
-
const frame = document.body.appendChild(document.createElement("iframe"));
|
|
120
|
-
frame.src = "about:blank";
|
|
121
|
-
frame.addEventListener("load", () => {
|
|
122
|
-
frame.contentWindow.setTimeout(() => { frame.contentWindow.alert("from the frame"); window.__frameDone = true; }, 0);
|
|
123
|
-
}, { once: true });
|
|
124
|
-
}`);
|
|
125
|
-
await until(() => read<boolean | undefined>("__frameDone"), "the frame's alert");
|
|
126
|
-
assert.deepEqual(seen, ["from the frame"]);
|
|
127
|
-
page.off("dialog", onDialog);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test("a beforeunload prompt does not keep the page from navigating away", OPTIONS, async (t) => {
|
|
131
|
-
await page.navigateTo("about:blank?unload");
|
|
132
|
-
await setContent(page, `<button id="b">x</button>`, `window.addEventListener("beforeunload", (e) => { e.preventDefault(); e.returnValue = "leave?"; });`);
|
|
133
|
-
await page.locator("button[@id='b']").click({ timeout: 10000 }); // user activation, or the browser skips the prompt
|
|
134
|
-
const seen: string[] = [];
|
|
135
|
-
const onDialog = (dialog: Dialog) => {
|
|
136
|
-
seen.push(dialog.type());
|
|
137
|
-
};
|
|
138
|
-
page.on("dialog", onDialog);
|
|
139
|
-
await page.navigateTo("about:blank?after", { timeout: 10000 });
|
|
140
|
-
page.off("dialog", onDialog);
|
|
141
|
-
assert.equal(await run<string>("() => location.search"), "?after");
|
|
142
|
-
assert.deepEqual(seen, ["beforeunload"], "the prompt appeared, and the default policy accepted it");
|
|
143
|
-
void t;
|
|
144
|
-
});
|
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { afterEach, beforeEach, describe, it, mock } from "node:test";
|
|
3
|
-
import logger from "../logger/index.js";
|
|
4
|
-
import { autoReply, FakeWebSocket, tick } from "../testing/fake-websocket.js";
|
|
5
|
-
import { BiDiConnector } from "../transport/bidi-connection.js";
|
|
6
|
-
import { SESSION_CAPABILITIES } from "./browser.js";
|
|
7
|
-
import type { Dialog } from "./dialog.js";
|
|
8
|
-
import Page from "./page.js";
|
|
9
|
-
|
|
10
|
-
const TREE = [
|
|
11
|
-
{ context: "ctx-1", parent: null, children: [{ context: "frame", parent: "ctx-1", children: [] }] },
|
|
12
|
-
{ context: "other", parent: null, children: [] },
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const stops: Array<() => void> = [];
|
|
16
|
-
let warn: ReturnType<typeof mock.method>;
|
|
17
|
-
let error: ReturnType<typeof mock.method>;
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
warn = mock.method(logger, "warn", () => logger);
|
|
20
|
-
error = mock.method(logger, "error", () => logger);
|
|
21
|
-
});
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
mock.restoreAll();
|
|
24
|
-
stops.splice(0).forEach((stop) => stop());
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
function setup(handlers: Record<string, (params: any) => object | Error> = {}) {
|
|
28
|
-
const ws = new FakeWebSocket();
|
|
29
|
-
const connector = new BiDiConnector(ws as unknown as WebSocket);
|
|
30
|
-
stops.push(
|
|
31
|
-
autoReply(ws, {
|
|
32
|
-
"browsingContext.getTree": () => ({ contexts: TREE }),
|
|
33
|
-
"browsingContext.handleUserPrompt": () => ({}),
|
|
34
|
-
...handlers,
|
|
35
|
-
}),
|
|
36
|
-
);
|
|
37
|
-
const page = new Page(connector, "ctx-1");
|
|
38
|
-
const opened = (extra: object = {}) =>
|
|
39
|
-
ws.emitEvent("browsingContext.userPromptOpened", {
|
|
40
|
-
context: "ctx-1",
|
|
41
|
-
handler: "ignore",
|
|
42
|
-
message: "Are you sure?",
|
|
43
|
-
type: "confirm",
|
|
44
|
-
...extra,
|
|
45
|
-
});
|
|
46
|
-
const closed = (extra: object = {}) =>
|
|
47
|
-
ws.emitEvent("browsingContext.userPromptClosed", { context: "ctx-1", accepted: true, type: "confirm", ...extra });
|
|
48
|
-
const handled = () =>
|
|
49
|
-
ws.sent.filter((m) => m.method === "browsingContext.handleUserPrompt").map((m) => m.params as any);
|
|
50
|
-
return { ws, page, opened, closed, handled };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
describe("session capability", () => {
|
|
54
|
-
it("asks the browser to leave prompts open until the framework answers", () => {
|
|
55
|
-
assert.deepEqual(SESSION_CAPABILITIES, {
|
|
56
|
-
alwaysMatch: { unhandledPromptBehavior: { default: "ignore" } },
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe("Dialog", () => {
|
|
62
|
-
it("describes the prompt", async () => {
|
|
63
|
-
const { page, opened } = setup();
|
|
64
|
-
let seen!: Dialog;
|
|
65
|
-
page.on("dialog", (dialog) => {
|
|
66
|
-
seen = dialog;
|
|
67
|
-
return dialog.dismiss();
|
|
68
|
-
});
|
|
69
|
-
await page.startDialogHandling();
|
|
70
|
-
opened({ type: "prompt", message: "Name?", defaultValue: "Sam" });
|
|
71
|
-
await tick(20);
|
|
72
|
-
assert.equal(seen.type(), "prompt");
|
|
73
|
-
assert.equal(seen.message(), "Name?");
|
|
74
|
-
assert.equal(seen.defaultValue(), "Sam");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("defaultValue is empty for other prompts", async () => {
|
|
78
|
-
const { page, opened } = setup();
|
|
79
|
-
let seen!: Dialog;
|
|
80
|
-
page.on("dialog", (dialog) => {
|
|
81
|
-
seen = dialog;
|
|
82
|
-
return dialog.dismiss();
|
|
83
|
-
});
|
|
84
|
-
await page.startDialogHandling();
|
|
85
|
-
opened({ type: "alert" });
|
|
86
|
-
await tick(20);
|
|
87
|
-
assert.equal(seen.defaultValue(), "");
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("accept sends the text, dismiss does not accept", async () => {
|
|
91
|
-
const { page, opened, handled } = setup();
|
|
92
|
-
page.on("dialog", (dialog) => (dialog.type() === "prompt" ? dialog.accept("Sam") : dialog.dismiss()));
|
|
93
|
-
await page.startDialogHandling();
|
|
94
|
-
opened({ type: "prompt" });
|
|
95
|
-
opened({ type: "confirm" });
|
|
96
|
-
await tick(30);
|
|
97
|
-
assert.deepEqual(handled(), [
|
|
98
|
-
{ context: "ctx-1", accept: true, userText: "Sam" },
|
|
99
|
-
{ context: "ctx-1", accept: false },
|
|
100
|
-
]);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("accept without text sends no userText", async () => {
|
|
104
|
-
const { page, opened, handled } = setup();
|
|
105
|
-
page.on("dialog", (dialog) => dialog.accept());
|
|
106
|
-
await page.startDialogHandling();
|
|
107
|
-
opened({ type: "confirm" });
|
|
108
|
-
await tick(20);
|
|
109
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: true }]);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("can be handled once; a second call throws", async () => {
|
|
113
|
-
const { page, opened } = setup();
|
|
114
|
-
let second: unknown;
|
|
115
|
-
page.on("dialog", async (dialog) => {
|
|
116
|
-
await dialog.accept();
|
|
117
|
-
second = await dialog.dismiss().catch((err: Error) => err);
|
|
118
|
-
});
|
|
119
|
-
await page.startDialogHandling();
|
|
120
|
-
opened();
|
|
121
|
-
await tick(30);
|
|
122
|
-
assert.match((second as Error).message, /dialog already handled/);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("swallows no such alert: the dialog is gone, which is what the caller wanted", async () => {
|
|
126
|
-
const gone = Object.assign(new Error("no alert open"), { code: "no such alert" });
|
|
127
|
-
const { page, opened } = setup({ "browsingContext.handleUserPrompt": () => gone });
|
|
128
|
-
let outcome = "not run";
|
|
129
|
-
page.on("dialog", async (dialog) => {
|
|
130
|
-
await dialog.accept();
|
|
131
|
-
outcome = "accepted";
|
|
132
|
-
});
|
|
133
|
-
await page.startDialogHandling();
|
|
134
|
-
opened();
|
|
135
|
-
await tick(30);
|
|
136
|
-
assert.equal(outcome, "accepted");
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("other browser errors reach the caller", async () => {
|
|
140
|
-
const broken = Object.assign(new Error("boom"), { code: "unknown error" });
|
|
141
|
-
const { page, opened } = setup({ "browsingContext.handleUserPrompt": () => broken });
|
|
142
|
-
let outcome: unknown;
|
|
143
|
-
page.on("dialog", async (dialog) => {
|
|
144
|
-
outcome = await dialog.accept().catch((err: Error) => err);
|
|
145
|
-
});
|
|
146
|
-
await page.startDialogHandling();
|
|
147
|
-
opened();
|
|
148
|
-
await tick(30);
|
|
149
|
-
assert.equal((outcome as Error).name, "BiDiError");
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("closed resolves with the answer from userPromptClosed", async () => {
|
|
153
|
-
const { page, opened, closed } = setup();
|
|
154
|
-
let seen!: Dialog;
|
|
155
|
-
page.on("dialog", (dialog) => {
|
|
156
|
-
seen = dialog;
|
|
157
|
-
});
|
|
158
|
-
await page.startDialogHandling();
|
|
159
|
-
opened({ type: "prompt" });
|
|
160
|
-
await tick(20);
|
|
161
|
-
let result: unknown;
|
|
162
|
-
void seen.closed.then((value) => (result = value));
|
|
163
|
-
closed({ type: "prompt", accepted: true, userText: "typed" });
|
|
164
|
-
await tick(10);
|
|
165
|
-
assert.deepEqual(result, { accepted: true, userText: "typed" });
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe("default policy", () => {
|
|
170
|
-
it("dismisses an alert nobody handled and says so", async () => {
|
|
171
|
-
const { page, opened, handled } = setup();
|
|
172
|
-
await page.startDialogHandling();
|
|
173
|
-
opened({ type: "alert", message: "Saved!" });
|
|
174
|
-
await tick(30);
|
|
175
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: false }]);
|
|
176
|
-
assert.equal(warn.mock.callCount(), 1);
|
|
177
|
-
const [template, message, outcome] = warn.mock.calls[0]!.arguments as string[];
|
|
178
|
-
assert.match(template!, /Dialog "%s" was %s automatically; add page\.on\("dialog"\) to handle it/);
|
|
179
|
-
assert.deepEqual([message, outcome], ["Saved!", "dismissed"]);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("dismisses confirm and prompt too", async () => {
|
|
183
|
-
const { page, opened, handled } = setup();
|
|
184
|
-
await page.startDialogHandling();
|
|
185
|
-
opened({ type: "confirm" });
|
|
186
|
-
opened({ type: "prompt" });
|
|
187
|
-
await tick(30);
|
|
188
|
-
assert.deepEqual(handled().map((h) => h.accept), [false, false]);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("accepts beforeunload so navigation can go on", async () => {
|
|
192
|
-
const { page, opened, handled } = setup();
|
|
193
|
-
await page.startDialogHandling();
|
|
194
|
-
opened({ type: "beforeunload", message: "" });
|
|
195
|
-
await tick(30);
|
|
196
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: true }]);
|
|
197
|
-
assert.equal((warn.mock.calls[0]!.arguments as string[])[2], "accepted");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("applies when a listener returns without handling the dialog", async () => {
|
|
201
|
-
const { page, opened, handled } = setup();
|
|
202
|
-
page.on("dialog", () => {});
|
|
203
|
-
await page.startDialogHandling();
|
|
204
|
-
opened({ type: "confirm" });
|
|
205
|
-
await tick(30);
|
|
206
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: false }]);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("waits for an async listener before applying, and not at all if it handled the dialog", async () => {
|
|
210
|
-
const { page, opened, handled } = setup();
|
|
211
|
-
page.on("dialog", async (dialog) => {
|
|
212
|
-
await tick(40);
|
|
213
|
-
await dialog.accept();
|
|
214
|
-
});
|
|
215
|
-
await page.startDialogHandling();
|
|
216
|
-
opened({ type: "confirm" });
|
|
217
|
-
await tick(15);
|
|
218
|
-
assert.deepEqual(handled(), [], "still waiting for the listener");
|
|
219
|
-
await tick(60);
|
|
220
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: true }]);
|
|
221
|
-
assert.equal(warn.mock.callCount(), 0);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("applies after a listener threw, and logs the error", async () => {
|
|
225
|
-
const { page, opened, handled } = setup();
|
|
226
|
-
page.on("dialog", () => {
|
|
227
|
-
throw new Error("listener failed");
|
|
228
|
-
});
|
|
229
|
-
await page.startDialogHandling();
|
|
230
|
-
opened({ type: "confirm" });
|
|
231
|
-
await tick(30);
|
|
232
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: false }]);
|
|
233
|
-
assert.ok(error.mock.callCount() >= 1);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("does not answer a prompt the browser already handled, but still tells the listeners", async () => {
|
|
237
|
-
const { page, opened, handled } = setup();
|
|
238
|
-
let notified: Dialog | undefined;
|
|
239
|
-
page.on("dialog", (dialog) => {
|
|
240
|
-
notified = dialog;
|
|
241
|
-
});
|
|
242
|
-
await page.startDialogHandling();
|
|
243
|
-
opened({ handler: "dismiss" });
|
|
244
|
-
await tick(30);
|
|
245
|
-
assert.deepEqual(handled(), []);
|
|
246
|
-
assert.ok(notified);
|
|
247
|
-
await assert.rejects(notified.accept(), /dialog already handled/);
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe("which prompts belong to the page", () => {
|
|
252
|
-
it("handles a prompt from an iframe, answering through the top-level context", async () => {
|
|
253
|
-
const { page, opened, handled } = setup();
|
|
254
|
-
page.on("dialog", (dialog) => dialog.accept());
|
|
255
|
-
await page.startDialogHandling();
|
|
256
|
-
opened({ context: "frame" });
|
|
257
|
-
await tick(20);
|
|
258
|
-
assert.deepEqual(handled(), [{ context: "ctx-1", accept: true }]);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("ignores prompts of other pages", async () => {
|
|
262
|
-
const { page, opened, handled } = setup();
|
|
263
|
-
let count = 0;
|
|
264
|
-
page.on("dialog", () => count++);
|
|
265
|
-
await page.startDialogHandling();
|
|
266
|
-
opened({ context: "other" });
|
|
267
|
-
await tick(30);
|
|
268
|
-
assert.equal(count, 0);
|
|
269
|
-
assert.deepEqual(handled(), []);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
describe("listener management", () => {
|
|
274
|
-
it("off removes a listener, once fires a single time", async () => {
|
|
275
|
-
const { page, opened } = setup();
|
|
276
|
-
let onceCount = 0;
|
|
277
|
-
let offCount = 0;
|
|
278
|
-
const off = () => {
|
|
279
|
-
offCount++;
|
|
280
|
-
};
|
|
281
|
-
page.on("dialog", off);
|
|
282
|
-
page.off("dialog", off);
|
|
283
|
-
page.once("dialog", (dialog) => {
|
|
284
|
-
onceCount++;
|
|
285
|
-
return dialog.dismiss();
|
|
286
|
-
});
|
|
287
|
-
await page.startDialogHandling();
|
|
288
|
-
opened();
|
|
289
|
-
await tick(20);
|
|
290
|
-
opened();
|
|
291
|
-
await tick(20);
|
|
292
|
-
assert.equal(onceCount, 1);
|
|
293
|
-
assert.equal(offCount, 0);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it("page.on starts dialog handling by itself", async () => {
|
|
297
|
-
const { ws, page } = setup();
|
|
298
|
-
page.on("dialog", () => {});
|
|
299
|
-
await tick(40);
|
|
300
|
-
assert.ok(ws.sent.some((m) => m.method === "session.subscribe" && (m.params as any).events.includes("browsingContext.userPromptOpened")));
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it("dispose stops handling and unsubscribes", async () => {
|
|
304
|
-
const { ws, page, opened, handled } = setup();
|
|
305
|
-
await page.startDialogHandling();
|
|
306
|
-
await page.dispose();
|
|
307
|
-
await tick(20);
|
|
308
|
-
opened();
|
|
309
|
-
await tick(20);
|
|
310
|
-
assert.deepEqual(handled(), []);
|
|
311
|
-
assert.ok(ws.sent.filter((m) => m.method === "session.unsubscribe").length >= 2);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it("startDialogHandling is idempotent", async () => {
|
|
315
|
-
const { ws, page } = setup();
|
|
316
|
-
await Promise.all([page.startDialogHandling(), page.startDialogHandling()]);
|
|
317
|
-
const prompts = ws.sent.filter(
|
|
318
|
-
(m) => m.method === "session.subscribe" && (m.params as any).events[0].startsWith("browsingContext.userPrompt"),
|
|
319
|
-
);
|
|
320
|
-
assert.equal(prompts.length, 2, "opened + closed, once");
|
|
321
|
-
});
|
|
322
|
-
});
|
package/src/browser/dialog.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
-
import { BiDiError } from "../transport/bidi-error.js";
|
|
3
|
-
import type {
|
|
4
|
-
UserPromptOpenedParameters,
|
|
5
|
-
UserPromptType,
|
|
6
|
-
} from "../types/bidi-modules/browsing-context.js";
|
|
7
|
-
|
|
8
|
-
export interface DialogResult {
|
|
9
|
-
accepted: boolean;
|
|
10
|
-
/** What was typed into a `prompt` */
|
|
11
|
-
userText?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A JavaScript dialog (`alert`, `confirm`, `prompt`) or a `beforeunload` prompt that is open in the page.
|
|
16
|
-
* Answer it once with `accept()` or `dismiss()`; `page.on("dialog")` hands these out.
|
|
17
|
-
*/
|
|
18
|
-
export class Dialog {
|
|
19
|
-
/** Resolves when the browser reports the prompt closed */
|
|
20
|
-
public readonly closed: Promise<DialogResult>;
|
|
21
|
-
private resolveClosed!: (result: DialogResult) => void;
|
|
22
|
-
private answered: boolean;
|
|
23
|
-
|
|
24
|
-
constructor(
|
|
25
|
-
private connector: BiDiConnector,
|
|
26
|
-
/** The top-level context of the page; prompts are answered there, also when an iframe opened them */
|
|
27
|
-
private context: string,
|
|
28
|
-
private info: UserPromptOpenedParameters,
|
|
29
|
-
) {
|
|
30
|
-
this.closed = new Promise((resolve) => (this.resolveClosed = resolve));
|
|
31
|
-
// With another handler than "ignore" the browser has answered already
|
|
32
|
-
this.answered = info.handler !== "ignore";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
public type(): UserPromptType {
|
|
36
|
-
return this.info.type;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
public message(): string {
|
|
40
|
-
return this.info.message;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** The text a `prompt` starts with; empty for the other kinds */
|
|
44
|
-
public defaultValue(): string {
|
|
45
|
-
return this.info.defaultValue ?? "";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** True once the dialog is answered, by you or by the browser */
|
|
49
|
-
public get handled(): boolean {
|
|
50
|
-
return this.answered;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Clicks OK; `promptText` is typed into a `prompt` first */
|
|
54
|
-
public accept(promptText?: string): Promise<void> {
|
|
55
|
-
return this.answer({ accept: true, ...(promptText !== undefined && { userText: promptText }) });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Clicks Cancel */
|
|
59
|
-
public dismiss(): Promise<void> {
|
|
60
|
-
return this.answer({ accept: false });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Called by the page when the browser reports the prompt closed */
|
|
64
|
-
public markClosed(result: DialogResult): void {
|
|
65
|
-
this.answered = true;
|
|
66
|
-
this.resolveClosed(result);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private async answer(params: { accept: boolean; userText?: string }): Promise<void> {
|
|
70
|
-
if (this.answered) throw new Error("dialog already handled");
|
|
71
|
-
this.answered = true;
|
|
72
|
-
try {
|
|
73
|
-
await this.connector.send("browsingContext.handleUserPrompt", {
|
|
74
|
-
context: this.context,
|
|
75
|
-
...params,
|
|
76
|
-
});
|
|
77
|
-
} catch (err) {
|
|
78
|
-
// Closed meanwhile (a navigation, another handler): the dialog is gone, which is what was wanted
|
|
79
|
-
if (err instanceof BiDiError && err.code === "no such alert") return;
|
|
80
|
-
throw err;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|