@itmetsam/samurai-framework 0.10.0 → 0.12.1
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 +3 -4
- package/dist/assert/assertion-error.d.ts +1 -1
- package/dist/assert/expect.d.ts +5 -50
- package/dist/assert/expect.js +5 -182
- package/dist/assert/locator-assertions.d.ts +22 -0
- package/dist/assert/locator-assertions.js +61 -0
- package/dist/assert/pending-assertions.d.ts +7 -0
- package/dist/assert/pending-assertions.js +40 -0
- package/dist/assert/text-match.d.ts +4 -0
- package/dist/assert/text-match.js +11 -0
- package/dist/assert/value-assertions.d.ts +23 -0
- package/dist/assert/value-assertions.js +82 -0
- package/dist/browser/browser-context.d.ts +0 -1
- package/dist/browser/browser-context.js +13 -5
- package/dist/browser/browser-finder.js +5 -9
- package/dist/browser/browser.d.ts +3 -31
- package/dist/browser/browser.js +36 -219
- package/dist/browser/chrome-launch.d.ts +2 -1
- package/dist/browser/dialog-handling.d.ts +32 -0
- package/dist/browser/dialog-handling.js +85 -0
- package/dist/browser/document-watcher.d.ts +44 -0
- package/dist/browser/document-watcher.js +160 -0
- package/dist/browser/download-folders.d.ts +0 -1
- package/dist/browser/download-folders.js +7 -15
- package/dist/browser/download.js +5 -10
- package/dist/browser/downloads-dir.js +2 -2
- package/dist/browser/emit.d.ts +7 -0
- package/dist/browser/emit.js +16 -0
- package/dist/browser/emulation.d.ts +1 -1
- package/dist/browser/emulation.js +54 -18
- package/dist/browser/file-chooser.js +4 -11
- package/dist/browser/history.d.ts +6 -0
- package/dist/browser/history.js +89 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/init-script.d.ts +2 -0
- package/dist/browser/init-script.js +16 -0
- package/dist/browser/launch-firefox.d.ts +20 -0
- package/dist/browser/launch-firefox.js +66 -0
- package/dist/browser/launch-flags.d.ts +18 -0
- package/dist/browser/launch-flags.js +100 -0
- package/dist/browser/lazy.d.ts +21 -0
- package/dist/browser/lazy.js +44 -0
- package/dist/browser/page-events.d.ts +44 -0
- package/dist/browser/page-events.js +58 -0
- package/dist/browser/page-logs.js +2 -9
- package/dist/browser/page-navigation.d.ts +58 -0
- package/dist/browser/page-navigation.js +108 -0
- package/dist/browser/page-network.d.ts +69 -0
- package/dist/browser/page-network.js +163 -0
- package/dist/browser/page-settings.d.ts +35 -0
- package/dist/browser/page-settings.js +51 -0
- package/dist/browser/page-transfers.d.ts +36 -0
- package/dist/browser/page-transfers.js +50 -0
- package/dist/browser/page.d.ts +39 -154
- package/dist/browser/page.js +134 -758
- package/dist/browser/pdf.d.ts +1 -1
- package/dist/browser/session.d.ts +16 -0
- package/dist/browser/session.js +32 -0
- package/dist/browser/viewport.d.ts +19 -0
- package/dist/browser/viewport.js +72 -0
- package/dist/browser/warm-up.d.ts +1 -1
- package/dist/browser/warm-up.js +2 -1
- package/dist/browsers/index.d.ts +1 -1
- package/dist/browsers/index.js +1 -1
- package/dist/browsers/pins.d.ts +0 -2
- package/dist/browsers/pins.js +0 -2
- package/dist/browsers/store.d.ts +0 -2
- package/dist/browsers/store.js +1 -2
- package/dist/cli/args.d.ts +13 -6
- package/dist/cli/args.js +13 -10
- package/dist/cli/browsers.d.ts +1 -2
- package/dist/cli/browsers.js +2 -2
- package/dist/cli/console-reporter.js +3 -2
- package/dist/cli/flow-reporter.js +2 -1
- package/dist/cli/flow.d.ts +1 -2
- package/dist/cli/flow.js +25 -22
- package/dist/cli/init.js +2 -7
- package/dist/cli/io.d.ts +10 -0
- package/dist/cli/io.js +1 -0
- package/dist/cli/main.d.ts +3 -10
- package/dist/cli/main.js +10 -143
- package/dist/cli/paint.d.ts +2 -0
- package/dist/cli/paint.js +4 -0
- package/dist/cli/record.d.ts +4 -0
- package/dist/cli/record.js +93 -0
- package/dist/cli/usage.d.ts +1 -0
- package/dist/cli/usage.js +58 -0
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.js +7 -0
- package/dist/config/browsers.d.ts +11 -0
- package/dist/config/browsers.js +32 -0
- package/dist/config/config.js +4 -1
- package/dist/config/mask.d.ts +1 -1
- package/dist/config/mask.js +7 -2
- package/dist/config/run-settings.d.ts +1 -2
- package/dist/config/run-settings.js +1 -6
- package/dist/config/timeouts.d.ts +2 -0
- package/dist/config/timeouts.js +13 -0
- package/dist/config/workers.d.ts +14 -0
- package/dist/config/workers.js +29 -0
- package/dist/flows/run/branch-context.d.ts +14 -0
- package/dist/flows/run/branch-context.js +35 -0
- package/dist/flows/run/handlers/condition.d.ts +1 -1
- package/dist/flows/run/handlers/end.d.ts +1 -1
- package/dist/flows/run/handlers/group.d.ts +1 -1
- package/dist/flows/run/handlers/index.d.ts +2 -2
- package/dist/flows/run/handlers/parallel.d.ts +1 -1
- package/dist/flows/run/handlers/start.d.ts +1 -1
- package/dist/flows/run/handlers/support.d.ts +3 -27
- package/dist/flows/run/handlers/test.d.ts +1 -1
- package/dist/flows/run/handlers/types.d.ts +27 -0
- package/dist/flows/run/handlers/types.js +1 -0
- package/dist/flows/run/index.d.ts +1 -1
- package/dist/flows/run/prepare-flow.d.ts +14 -0
- package/dist/flows/run/prepare-flow.js +44 -0
- package/dist/flows/run/project.d.ts +33 -2
- package/dist/flows/run/project.js +139 -57
- package/dist/flows/run/run.d.ts +3 -4
- package/dist/flows/run/run.js +23 -193
- package/dist/flows/run/walker.d.ts +56 -0
- package/dist/flows/run/walker.js +225 -0
- package/dist/flows/steps/catalog.d.ts +1 -1
- package/dist/flows/steps/handler.d.ts +1 -1
- package/dist/flows/steps/set.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/locator/actionability.d.ts +33 -0
- package/dist/locator/actionability.js +104 -0
- package/dist/locator/chain-resolver.d.ts +20 -0
- package/dist/locator/chain-resolver.js +102 -0
- package/dist/locator/document-swap.d.ts +1 -0
- package/dist/locator/document-swap.js +11 -0
- package/dist/locator/element-state.d.ts +3 -1
- package/dist/locator/element-state.js +11 -3
- package/dist/locator/file-actions.d.ts +35 -0
- package/dist/locator/file-actions.js +55 -0
- package/dist/{script/helpers.js → locator/helper-realm.js} +2 -2
- package/dist/locator/input-actions.d.ts +18 -0
- package/dist/locator/input-actions.js +80 -0
- package/dist/locator/label-locator.d.ts +1 -1
- package/dist/locator/label-locator.js +5 -12
- package/dist/locator/locator.d.ts +14 -37
- package/dist/locator/locator.js +68 -424
- package/dist/locator/page-scripts.d.ts +16 -0
- package/dist/locator/page-scripts.js +54 -0
- package/dist/locator/page-search.d.ts +10 -0
- package/dist/locator/page-search.js +19 -0
- package/dist/locator/selector-errors.d.ts +0 -4
- package/dist/locator/selector-errors.js +0 -7
- package/dist/locator/selector.d.ts +14 -13
- package/dist/locator/selector.js +10 -16
- package/dist/locator/text-locator.d.ts +1 -1
- package/dist/locator/text-locator.js +5 -9
- package/dist/logger/index.d.ts +6 -0
- package/dist/logger/index.js +14 -7
- package/dist/network/bidi-wire.d.ts +10 -0
- package/dist/network/bidi-wire.js +38 -0
- package/dist/network/intercept.d.ts +30 -0
- package/dist/network/intercept.js +130 -0
- package/dist/network/network-tracker.d.ts +10 -47
- package/dist/network/network-tracker.js +34 -68
- package/dist/network/network-types.d.ts +33 -0
- package/dist/network/network-types.js +1 -0
- package/dist/network/network-wait.d.ts +7 -2
- package/dist/network/network-wait.js +14 -4
- package/dist/network/response.d.ts +1 -1
- package/dist/network/response.js +1 -1
- package/dist/network/route.d.ts +92 -0
- package/dist/network/route.js +142 -0
- package/dist/network/router.d.ts +6 -91
- package/dist/network/router.js +25 -258
- package/dist/recorder/expectation.d.ts +5 -0
- package/dist/recorder/expectation.js +20 -0
- package/dist/recorder/files-recorder.d.ts +30 -0
- package/dist/recorder/files-recorder.js +88 -0
- package/dist/recorder/normalise.d.ts +17 -6
- package/dist/recorder/normalise.js +96 -91
- package/dist/recorder/options.d.ts +56 -0
- package/dist/recorder/options.js +1 -0
- package/dist/recorder/page-messages.d.ts +35 -0
- package/dist/recorder/page-messages.js +26 -0
- package/dist/recorder/ranking.js +1 -0
- package/dist/recorder/realms.d.ts +6 -0
- package/dist/recorder/realms.js +13 -0
- package/dist/recorder/recorder.d.ts +10 -74
- package/dist/recorder/recorder.js +39 -199
- package/dist/recorder/session-browser.d.ts +22 -0
- package/dist/recorder/session-browser.js +21 -0
- package/dist/recorder/session-secrets.d.ts +8 -0
- package/dist/recorder/session-secrets.js +45 -0
- package/dist/recorder/session.d.ts +5 -14
- package/dist/recorder/session.js +22 -120
- package/dist/recorder/spec-source.d.ts +39 -0
- package/dist/recorder/spec-source.js +84 -0
- package/dist/recorder/toolbar/action-guard.d.ts +21 -0
- package/dist/recorder/toolbar/action-guard.js +43 -0
- package/dist/recorder/toolbar/actions/assert.d.ts +4 -0
- package/dist/recorder/toolbar/actions/assert.js +52 -0
- package/dist/recorder/toolbar/actions/insert.d.ts +6 -0
- package/dist/recorder/toolbar/actions/insert.js +93 -0
- package/dist/recorder/toolbar/actions/pick.d.ts +8 -0
- package/dist/recorder/toolbar/actions/pick.js +26 -0
- package/dist/recorder/toolbar/actions/reply.d.ts +7 -0
- package/dist/recorder/toolbar/actions/reply.js +10 -0
- package/dist/recorder/toolbar/controller-fixture.d.ts +47 -0
- package/dist/recorder/toolbar/controller-fixture.js +129 -0
- package/dist/recorder/toolbar/controller.d.ts +12 -78
- package/dist/recorder/toolbar/controller.js +35 -247
- package/dist/recorder/toolbar/inspect.d.ts +1 -3
- package/dist/recorder/toolbar/inspect.js +2 -2
- package/dist/recorder/toolbar/page-scope.d.ts +19 -0
- package/dist/recorder/toolbar/page-scope.js +43 -0
- package/dist/recorder/toolbar/protocol.d.ts +2 -1
- package/dist/recorder/toolbar/source/attach.d.ts +2 -0
- package/dist/recorder/toolbar/source/attach.js +41 -0
- package/dist/recorder/toolbar/source/dom.d.ts +2 -0
- package/dist/recorder/toolbar/source/dom.js +47 -0
- package/dist/recorder/toolbar/source/drag.d.ts +2 -0
- package/dist/recorder/toolbar/source/drag.js +32 -0
- package/dist/recorder/toolbar/source/events-page.d.ts +2 -0
- package/dist/recorder/toolbar/source/events-page.js +67 -0
- package/dist/recorder/toolbar/source/events-toolbar.d.ts +2 -0
- package/dist/recorder/toolbar/source/events-toolbar.js +35 -0
- package/dist/recorder/toolbar/source/fields.d.ts +2 -0
- package/dist/recorder/toolbar/source/fields.js +44 -0
- package/dist/recorder/toolbar/source/handlers.d.ts +2 -0
- package/dist/recorder/toolbar/source/handlers.js +134 -0
- package/dist/recorder/toolbar/source/names.d.ts +2 -0
- package/dist/recorder/toolbar/source/names.js +12 -0
- package/dist/recorder/toolbar/source/picking.d.ts +2 -0
- package/dist/recorder/toolbar/source/picking.js +72 -0
- package/dist/recorder/toolbar/source/placement.d.ts +2 -0
- package/dist/recorder/toolbar/source/placement.js +45 -0
- package/dist/recorder/toolbar/source/recorder-side.d.ts +2 -0
- package/dist/recorder/toolbar/source/recorder-side.js +54 -0
- package/dist/recorder/toolbar/source/setup.d.ts +2 -0
- package/dist/recorder/toolbar/source/setup.js +50 -0
- package/dist/recorder/toolbar/source/views-panels.d.ts +2 -0
- package/dist/recorder/toolbar/source/views-panels.js +150 -0
- package/dist/recorder/toolbar/source/views-rail.d.ts +2 -0
- package/dist/recorder/toolbar/source/views-rail.js +40 -0
- package/dist/recorder/toolbar/source/views-render.d.ts +2 -0
- package/dist/recorder/toolbar/source/views-render.js +48 -0
- package/dist/recorder/toolbar/source.js +30 -856
- package/dist/recorder/toolbar/types.d.ts +70 -0
- package/dist/recorder/toolbar/types.js +1 -0
- package/dist/recorder/toolbar-bridge.d.ts +43 -0
- package/dist/recorder/toolbar-bridge.js +106 -0
- package/dist/runner/browsers.d.ts +0 -12
- package/dist/runner/browsers.js +1 -33
- package/dist/runner/options.d.ts +31 -0
- package/dist/runner/options.js +1 -0
- package/dist/runner/pooled-run.d.ts +43 -0
- package/dist/runner/pooled-run.js +75 -0
- package/dist/runner/ports.d.ts +2 -0
- package/dist/runner/ports.js +2 -0
- package/dist/runner/registry.d.ts +1 -1
- package/dist/runner/registry.js +1 -1
- package/dist/runner/reporter.d.ts +31 -10
- package/dist/runner/reporter.js +39 -6
- package/dist/runner/run-test.d.ts +16 -0
- package/dist/runner/run-test.js +178 -0
- package/dist/runner/run.d.ts +5 -3
- package/dist/runner/run.js +28 -29
- package/dist/runner/select.d.ts +24 -0
- package/dist/runner/select.js +77 -0
- package/dist/runner/test-dir.d.ts +13 -0
- package/dist/runner/test-dir.js +27 -0
- package/dist/runner/test-runner.d.ts +25 -56
- package/dist/runner/test-runner.js +81 -270
- package/dist/runner/worker-pool.d.ts +67 -0
- package/dist/runner/worker-pool.js +275 -0
- package/dist/runner/worker-process.d.ts +6 -0
- package/dist/runner/worker-process.js +54 -0
- package/dist/runner/worker-protocol.d.ts +67 -0
- package/dist/runner/worker-protocol.js +1 -0
- package/dist/runner/worker-session.d.ts +7 -0
- package/dist/runner/worker-session.js +60 -0
- package/dist/runner/worker.d.ts +26 -0
- package/dist/runner/worker.js +121 -0
- package/dist/runner/workers-option.d.ts +15 -0
- package/dist/runner/workers-option.js +22 -0
- package/dist/script/call-function.d.ts +1 -9
- package/dist/script/call-function.js +2 -19
- package/dist/script/element-handle.d.ts +1 -2
- package/dist/script/element-handle.js +1 -2
- package/dist/script/serialize.d.ts +3 -12
- package/dist/script/serialize.js +53 -62
- package/dist/steps/parse/call.d.ts +3 -0
- package/dist/steps/parse/call.js +81 -0
- package/dist/steps/parse/expectation.d.ts +5 -0
- package/dist/steps/parse/expectation.js +71 -0
- package/dist/steps/parse/file-entry.d.ts +4 -0
- package/dist/steps/parse/file-entry.js +74 -0
- package/dist/steps/parse/literals.d.ts +4 -0
- package/dist/steps/parse/literals.js +28 -0
- package/dist/steps/parse/step.d.ts +4 -0
- package/dist/steps/parse/step.js +81 -0
- package/dist/steps/parse.js +2 -323
- package/dist/transport/bidi-connection.d.ts +4 -15
- package/dist/transport/bidi-connection.js +9 -63
- package/dist/transport/log-redaction.d.ts +13 -0
- package/dist/transport/log-redaction.js +51 -0
- package/dist/transport/subscriptions.js +18 -13
- package/dist/transport/unsupported.d.ts +7 -0
- package/dist/transport/unsupported.js +13 -0
- package/dist/types/bidi-modules/browser.d.ts +1 -12
- package/dist/types/bidi-modules/browsing-context.d.ts +8 -18
- package/dist/types/bidi-modules/emulation.d.ts +29 -21
- package/dist/types/bidi-modules/input.d.ts +1 -9
- package/dist/types/bidi-modules/network.d.ts +29 -24
- package/dist/types/bidi-modules/script.d.ts +0 -9
- package/dist/types/bidi-modules/session.d.ts +0 -8
- package/dist/types/bidi-modules/storage.d.ts +0 -5
- package/dist/types/bidi.d.ts +7 -37
- package/dist/types/config.d.ts +12 -0
- package/dist/types/test.d.ts +8 -3
- package/dist/wait/wait-until.d.ts +1 -5
- package/dist/wait/wait-until.js +7 -16
- package/package.json +3 -2
- package/dist/config/default.d.ts +0 -2
- package/dist/config/default.js +0 -3
- package/dist/types/bidi-modules/web-extensions.d.ts +0 -9
- package/dist/types/bidi-protocols/command.d.ts +0 -7
- package/dist/types/bidi-protocols/event.d.ts +0 -17
- /package/dist/{script/helpers.d.ts → locator/helper-realm.d.ts} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { callFunction } from "../script/call-function.js";
|
|
2
|
+
import { BiDiError } from "../transport/bidi-error.js";
|
|
3
|
+
import { Lazy } from "./lazy.js";
|
|
4
|
+
import { NavigationError } from "./navigation-error.js";
|
|
5
|
+
/** How many navigations keep their load progress */
|
|
6
|
+
const NAVIGATIONS_REMEMBERED = 20;
|
|
7
|
+
/** Events a connection's browser rejected as unknown, so nothing asks for them again */
|
|
8
|
+
const unsupportedEventsByConnector = new WeakMap();
|
|
9
|
+
function unsupportedEvents(connector) {
|
|
10
|
+
let events = unsupportedEventsByConnector.get(connector);
|
|
11
|
+
if (!events)
|
|
12
|
+
unsupportedEventsByConnector.set(connector, (events = new Set()));
|
|
13
|
+
return events;
|
|
14
|
+
}
|
|
15
|
+
/** A wait ends with the navigation it was waiting for failing or being aborted */
|
|
16
|
+
function navigationFailure(url, what) {
|
|
17
|
+
throw new NavigationError(url, what, undefined, "waitForLoadState");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Follows the document of one page: which navigations reached DOMContentLoaded and load, and waits for a state of
|
|
21
|
+
* the document that is showing (or of the navigation that is on its way).
|
|
22
|
+
*/
|
|
23
|
+
export class DocumentWatcher {
|
|
24
|
+
constructor(connector, contextId) {
|
|
25
|
+
this.connector = connector;
|
|
26
|
+
this.contextId = contextId;
|
|
27
|
+
/** How far navigations that did not wait for the load got, by navigation id (the latest few) */
|
|
28
|
+
this.navigationProgress = new Map();
|
|
29
|
+
this.lazyEvents = new Lazy();
|
|
30
|
+
this.onDocumentEvent = (kind) => (info) => {
|
|
31
|
+
if (info.context !== this.contextId || info.navigation === null)
|
|
32
|
+
return;
|
|
33
|
+
const progress = this.progressOf(info.navigation);
|
|
34
|
+
progress.domContentLoaded = true;
|
|
35
|
+
if (kind === "load")
|
|
36
|
+
progress.load = true;
|
|
37
|
+
};
|
|
38
|
+
this.onDomContentLoaded = this.onDocumentEvent("domContentLoaded");
|
|
39
|
+
this.onLoad = this.onDocumentEvent("load");
|
|
40
|
+
}
|
|
41
|
+
/** The progress of `navigation`, created (and the oldest forgotten) when it is not known yet */
|
|
42
|
+
progressOf(navigation) {
|
|
43
|
+
let progress = this.navigationProgress.get(navigation);
|
|
44
|
+
if (!progress) {
|
|
45
|
+
progress = { domContentLoaded: false, load: false };
|
|
46
|
+
this.navigationProgress.set(navigation, progress);
|
|
47
|
+
// Only the latest few navigations matter
|
|
48
|
+
if (this.navigationProgress.size > NAVIGATIONS_REMEMBERED) {
|
|
49
|
+
this.navigationProgress.delete(this.navigationProgress.keys().next().value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return progress;
|
|
53
|
+
}
|
|
54
|
+
/** Starts recording which navigations reached DOMContentLoaded and load; kept until dispose */
|
|
55
|
+
watch() {
|
|
56
|
+
return this.lazyEvents.get(async () => {
|
|
57
|
+
this.connector.onEvent("browsingContext.domContentLoaded", this.onDomContentLoaded);
|
|
58
|
+
this.connector.onEvent("browsingContext.load", this.onLoad);
|
|
59
|
+
try {
|
|
60
|
+
return await this.connector.subscribe([
|
|
61
|
+
"browsingContext.domContentLoaded",
|
|
62
|
+
"browsingContext.load",
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
this.connector.offEvent("browsingContext.domContentLoaded", this.onDomContentLoaded);
|
|
67
|
+
this.connector.offEvent("browsingContext.load", this.onLoad);
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Stops listening and gives the subscription back */
|
|
73
|
+
async dispose() {
|
|
74
|
+
const subscription = this.lazyEvents.take();
|
|
75
|
+
this.connector.offEvent("browsingContext.domContentLoaded", this.onDomContentLoaded);
|
|
76
|
+
this.connector.offEvent("browsingContext.load", this.onLoad);
|
|
77
|
+
await (await subscription)?.unsubscribe();
|
|
78
|
+
}
|
|
79
|
+
canBeAborted() {
|
|
80
|
+
return !unsupportedEvents(this.connector).has("browsingContext.navigationAborted");
|
|
81
|
+
}
|
|
82
|
+
/** Subscribes to what a wait listens for; a browser that lacks the navigationAborted event (Firefox) is asked only once */
|
|
83
|
+
async subscribe(reachedEvent) {
|
|
84
|
+
const required = [
|
|
85
|
+
reachedEvent,
|
|
86
|
+
"browsingContext.navigationFailed",
|
|
87
|
+
];
|
|
88
|
+
if (!this.canBeAborted())
|
|
89
|
+
return this.connector.subscribe([...required]);
|
|
90
|
+
try {
|
|
91
|
+
return await this.connector.subscribe([
|
|
92
|
+
...required,
|
|
93
|
+
"browsingContext.navigationAborted",
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (!(err instanceof BiDiError) || err.code !== "invalid argument")
|
|
98
|
+
throw err;
|
|
99
|
+
unsupportedEvents(this.connector).add("browsingContext.navigationAborted");
|
|
100
|
+
return this.connector.subscribe([...required]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The first of: the state is reached, the navigation `last` fails, the navigation `last` is aborted. Registered
|
|
105
|
+
* before the state is read, so an event in between is not lost.
|
|
106
|
+
*/
|
|
107
|
+
eventsOf(reachedEvent, last, listen) {
|
|
108
|
+
const connector = this.connector;
|
|
109
|
+
return Promise.race([
|
|
110
|
+
connector.waitForEvent(reachedEvent, (p) => p.context === this.contextId, listen),
|
|
111
|
+
connector
|
|
112
|
+
.waitForEvent("browsingContext.navigationFailed", (p) => p.context === this.contextId &&
|
|
113
|
+
(last === undefined || p.navigation === last), listen)
|
|
114
|
+
.then((p) => navigationFailure(p.url, "navigation failed")),
|
|
115
|
+
...(this.canBeAborted()
|
|
116
|
+
? [
|
|
117
|
+
connector
|
|
118
|
+
.waitForEvent("browsingContext.navigationAborted", (p) => p.context === this.contextId &&
|
|
119
|
+
last !== undefined &&
|
|
120
|
+
p.navigation === last, listen)
|
|
121
|
+
.then((p) => navigationFailure(p.url, "navigation aborted")),
|
|
122
|
+
]
|
|
123
|
+
: []),
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
/** Whether the document is at `target` already: a navigation we saw fall short beats the document that is showing */
|
|
127
|
+
async readyStateReached(target, last) {
|
|
128
|
+
const known = last === undefined ? undefined : this.navigationProgress.get(last);
|
|
129
|
+
if (known && !(target === "load" ? known.load : known.domContentLoaded))
|
|
130
|
+
return false;
|
|
131
|
+
const readyState = await callFunction(this.connector, this.contextId, "() => document.readyState", [], { awaitPromise: false });
|
|
132
|
+
return target === "load"
|
|
133
|
+
? readyState === "complete"
|
|
134
|
+
: readyState === "interactive" || readyState === "complete";
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolves when the document reached `target`; rejects with a NavigationError when the navigation `last` (the
|
|
138
|
+
* latest one navigateTo started) fails or is aborted, and with a WaitTimeoutError after `timeout` ms.
|
|
139
|
+
*/
|
|
140
|
+
async waitForState(target, timeout, last) {
|
|
141
|
+
const reachedEvent = target === "load"
|
|
142
|
+
? "browsingContext.load"
|
|
143
|
+
: "browsingContext.domContentLoaded";
|
|
144
|
+
const subscription = await this.subscribe(reachedEvent);
|
|
145
|
+
const abort = new AbortController();
|
|
146
|
+
try {
|
|
147
|
+
const events = this.eventsOf(reachedEvent, last, {
|
|
148
|
+
timeout,
|
|
149
|
+
signal: abort.signal,
|
|
150
|
+
});
|
|
151
|
+
events.catch(() => { }); // settled below, or aborted in finally
|
|
152
|
+
if (!(await this.readyStateReached(target, last)))
|
|
153
|
+
await events;
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
abort.abort();
|
|
157
|
+
await subscription.unsubscribe();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, mkdirSync, renameSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import logger from "../logger/index.js";
|
|
4
|
+
import { removeIfEmpty } from "./downloads-dir.js";
|
|
4
5
|
/** `report.csv` becomes `report(1).csv`, `report(2).csv`, … (the names Firefox gives) until a name is free in `dir` */
|
|
5
6
|
export function uniqueName(dir, name) {
|
|
6
7
|
if (!existsSync(path.join(dir, name)))
|
|
@@ -24,7 +25,7 @@ export class ChromeDownloadFolders {
|
|
|
24
25
|
this.dir = dir;
|
|
25
26
|
this.counter = 0;
|
|
26
27
|
this.disposed = false;
|
|
27
|
-
this.onBegin = (
|
|
28
|
+
this.onBegin = () => {
|
|
28
29
|
if (this.disposed)
|
|
29
30
|
return;
|
|
30
31
|
this.rotate().catch((err) => logger.error("Could not set a new download folder", { err }));
|
|
@@ -37,7 +38,7 @@ export class ChromeDownloadFolders {
|
|
|
37
38
|
const to = path.join(this.dir, uniqueName(this.dir, path.basename(from)));
|
|
38
39
|
renameSync(from, to);
|
|
39
40
|
params.filepath = to;
|
|
40
|
-
|
|
41
|
+
removeIfEmpty(path.dirname(from));
|
|
41
42
|
}
|
|
42
43
|
catch (err) {
|
|
43
44
|
logger.error("Could not move a finished download", { err });
|
|
@@ -68,8 +69,8 @@ export class ChromeDownloadFolders {
|
|
|
68
69
|
this.connector.offEvent("browsingContext.downloadEnd", this.onEnd);
|
|
69
70
|
await this.subscription?.unsubscribe().catch(() => undefined);
|
|
70
71
|
if (this.staging)
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
removeIfEmpty(this.staging);
|
|
73
|
+
removeIfEmpty(this.stagingRoot());
|
|
73
74
|
}
|
|
74
75
|
stagingRoot() {
|
|
75
76
|
return path.join(this.dir, ".staging");
|
|
@@ -83,15 +84,6 @@ export class ChromeDownloadFolders {
|
|
|
83
84
|
downloadBehavior: { type: "allowed", destinationFolder: this.staging },
|
|
84
85
|
});
|
|
85
86
|
if (previous)
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
removeIfEmpty(dir) {
|
|
89
|
-
try {
|
|
90
|
-
if (readdirSync(dir).length === 0)
|
|
91
|
-
rmSync(dir, { recursive: true });
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// gone already, or not empty: leave it
|
|
95
|
-
}
|
|
87
|
+
removeIfEmpty(previous);
|
|
96
88
|
}
|
|
97
89
|
}
|
package/dist/browser/download.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import EventEmitter from "node:events";
|
|
2
|
-
import
|
|
2
|
+
import { emitSafely } from "./emit.js";
|
|
3
3
|
import { WaitTimeoutError } from "../wait/wait-until.js";
|
|
4
4
|
/** A download did not complete */
|
|
5
5
|
export class DownloadError extends Error {
|
|
@@ -80,21 +80,16 @@ export class DownloadTracker {
|
|
|
80
80
|
return;
|
|
81
81
|
const download = new Download(params.url, params.suggestedFilename, this.timeout);
|
|
82
82
|
this.active.set(params.navigation, download);
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
listener(download);
|
|
86
|
-
}
|
|
87
|
-
catch (err) {
|
|
88
|
-
logger.error("A download listener threw", { err });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
83
|
+
emitSafely(this.events, "download", [download]);
|
|
91
84
|
};
|
|
92
85
|
this.onEnd = (params) => {
|
|
93
86
|
const download = this.active.get(params.navigation);
|
|
94
87
|
if (!download)
|
|
95
88
|
return;
|
|
96
89
|
this.active.delete(params.navigation);
|
|
97
|
-
download.finish(params.status === "complete"
|
|
90
|
+
download.finish(params.status === "complete"
|
|
91
|
+
? { status: "complete", path: params.filepath }
|
|
92
|
+
: { status: "canceled" });
|
|
98
93
|
};
|
|
99
94
|
}
|
|
100
95
|
static async start(connector, tree, contextId, options) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, rmdirSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import logger from "../logger/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isUnsupported } from "../transport/unsupported.js";
|
|
5
5
|
let counter = 0;
|
|
6
6
|
/**
|
|
7
7
|
* Makes downloads land in a folder of their own, so a test knows where to look and runs do not mix: a new folder
|
|
@@ -17,7 +17,7 @@ export async function prepareDownloadsDir(connector, baseDir) {
|
|
|
17
17
|
}
|
|
18
18
|
catch (err) {
|
|
19
19
|
removeIfEmpty(dir);
|
|
20
|
-
if (
|
|
20
|
+
if (isUnsupported(err)) {
|
|
21
21
|
logger.warn("The browser cannot be told where to save downloads; they go to its own folder");
|
|
22
22
|
return undefined;
|
|
23
23
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type EventEmitter from "node:events";
|
|
2
|
+
/**
|
|
3
|
+
* Calls the listeners of `event` in order with `args`. A listener that throws is logged, not rethrown: it must not
|
|
4
|
+
* stop the other listeners or the code that raised the event. `rawListeners` so the wrappers that make a `once`
|
|
5
|
+
* listener remove itself are called.
|
|
6
|
+
*/
|
|
7
|
+
export declare function emitSafely(emitter: EventEmitter, event: string, args: unknown[], label?: string): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import logger from "../logger/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Calls the listeners of `event` in order with `args`. A listener that throws is logged, not rethrown: it must not
|
|
4
|
+
* stop the other listeners or the code that raised the event. `rawListeners` so the wrappers that make a `once`
|
|
5
|
+
* listener remove itself are called.
|
|
6
|
+
*/
|
|
7
|
+
export function emitSafely(emitter, event, args, label = event) {
|
|
8
|
+
for (const listener of emitter.rawListeners(event)) {
|
|
9
|
+
try {
|
|
10
|
+
listener(...args);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
logger.error("A %s listener threw", label, { err });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UnsupportedOperationError } from "../
|
|
1
|
+
import { UnsupportedOperationError } from "../transport/unsupported.js";
|
|
2
2
|
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
3
3
|
declare const ORIENTATIONS: readonly ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"];
|
|
4
4
|
type OrientationType = (typeof ORIENTATIONS)[number];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { UnsupportedOperationError } from "../
|
|
2
|
-
import { BiDiError } from "../transport/bidi-error.js";
|
|
1
|
+
import { isUnsupported, UnsupportedOperationError, } from "../transport/unsupported.js";
|
|
3
2
|
const ORIENTATIONS = [
|
|
4
3
|
"portrait-primary",
|
|
5
4
|
"portrait-secondary",
|
|
@@ -20,7 +19,7 @@ export class EmulationError extends Error {
|
|
|
20
19
|
/** The browser does not implement an emulation command */
|
|
21
20
|
export class EmulationUnsupportedError extends UnsupportedOperationError {
|
|
22
21
|
constructor(option, applied) {
|
|
23
|
-
super(`emulate(
|
|
22
|
+
super(`emulate(): ${option} is not supported by the browser`);
|
|
24
23
|
this.name = "EmulationUnsupportedError";
|
|
25
24
|
this.option = option;
|
|
26
25
|
this.applied = applied;
|
|
@@ -34,14 +33,19 @@ function plan(options) {
|
|
|
34
33
|
if (options.locale !== undefined) {
|
|
35
34
|
if (options.locale !== null) {
|
|
36
35
|
try {
|
|
37
|
-
if (options.locale === "" ||
|
|
36
|
+
if (options.locale === "" ||
|
|
37
|
+
Intl.getCanonicalLocales(options.locale).length !== 1)
|
|
38
38
|
throw new RangeError();
|
|
39
39
|
}
|
|
40
40
|
catch {
|
|
41
41
|
throw new RangeError(`emulate(): invalid locale "${options.locale}"`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
steps.push({
|
|
44
|
+
steps.push({
|
|
45
|
+
option: "locale",
|
|
46
|
+
method: "emulation.setLocaleOverride",
|
|
47
|
+
params: { locale: options.locale },
|
|
48
|
+
});
|
|
45
49
|
}
|
|
46
50
|
if (options.timezone !== undefined) {
|
|
47
51
|
if (options.timezone !== null) {
|
|
@@ -52,23 +56,36 @@ function plan(options) {
|
|
|
52
56
|
throw new RangeError(`emulate(): unknown timezone "${options.timezone}"`);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
|
-
steps.push({
|
|
59
|
+
steps.push({
|
|
60
|
+
option: "timezone",
|
|
61
|
+
method: "emulation.setTimezoneOverride",
|
|
62
|
+
params: { timezone: options.timezone },
|
|
63
|
+
});
|
|
56
64
|
}
|
|
57
65
|
if (options.userAgent !== undefined) {
|
|
58
66
|
if (options.userAgent === "")
|
|
59
67
|
throw new RangeError("emulate(): userAgent must not be empty");
|
|
60
|
-
steps.push({
|
|
68
|
+
steps.push({
|
|
69
|
+
option: "userAgent",
|
|
70
|
+
method: "emulation.setUserAgentOverride",
|
|
71
|
+
params: { userAgent: options.userAgent },
|
|
72
|
+
});
|
|
61
73
|
}
|
|
62
74
|
if (options.geolocation !== undefined) {
|
|
63
75
|
const place = options.geolocation;
|
|
64
76
|
if (place !== null) {
|
|
65
|
-
if (!isNumber(place.latitude) ||
|
|
77
|
+
if (!isNumber(place.latitude) ||
|
|
78
|
+
place.latitude < -90 ||
|
|
79
|
+
place.latitude > 90) {
|
|
66
80
|
throw new RangeError("emulate(): latitude must be between -90 and 90");
|
|
67
81
|
}
|
|
68
|
-
if (!isNumber(place.longitude) ||
|
|
82
|
+
if (!isNumber(place.longitude) ||
|
|
83
|
+
place.longitude < -180 ||
|
|
84
|
+
place.longitude > 180) {
|
|
69
85
|
throw new RangeError("emulate(): longitude must be between -180 and 180");
|
|
70
86
|
}
|
|
71
|
-
if (place.accuracy !== undefined &&
|
|
87
|
+
if (place.accuracy !== undefined &&
|
|
88
|
+
(!isNumber(place.accuracy) || place.accuracy < 0)) {
|
|
72
89
|
throw new RangeError("emulate(): accuracy must be 0 or more");
|
|
73
90
|
}
|
|
74
91
|
}
|
|
@@ -82,33 +99,52 @@ function plan(options) {
|
|
|
82
99
|
steps.push({
|
|
83
100
|
option: "offline",
|
|
84
101
|
method: "emulation.setNetworkConditions",
|
|
85
|
-
params: {
|
|
102
|
+
params: {
|
|
103
|
+
networkConditions: options.offline ? { type: "offline" } : null,
|
|
104
|
+
},
|
|
86
105
|
});
|
|
87
106
|
}
|
|
88
107
|
if (options.orientation !== undefined) {
|
|
89
108
|
let screenOrientation = null;
|
|
90
109
|
if (options.orientation !== null) {
|
|
91
|
-
const type = (options.orientation === "portrait" ||
|
|
110
|
+
const type = (options.orientation === "portrait" ||
|
|
111
|
+
options.orientation === "landscape"
|
|
92
112
|
? `${options.orientation}-primary`
|
|
93
113
|
: options.orientation);
|
|
94
114
|
if (!ORIENTATIONS.includes(type))
|
|
95
115
|
throw new RangeError(`emulate(): unknown orientation "${options.orientation}"`);
|
|
96
|
-
screenOrientation = {
|
|
116
|
+
screenOrientation = {
|
|
117
|
+
natural: type.startsWith("portrait") ? "portrait" : "landscape",
|
|
118
|
+
type,
|
|
119
|
+
};
|
|
97
120
|
}
|
|
98
|
-
steps.push({
|
|
121
|
+
steps.push({
|
|
122
|
+
option: "orientation",
|
|
123
|
+
method: "emulation.setScreenOrientationOverride",
|
|
124
|
+
params: { screenOrientation },
|
|
125
|
+
});
|
|
99
126
|
}
|
|
100
127
|
if (options.screen !== undefined) {
|
|
101
128
|
const area = options.screen;
|
|
102
|
-
if (area !== null &&
|
|
129
|
+
if (area !== null &&
|
|
130
|
+
!(isPositiveInteger(area.width) && isPositiveInteger(area.height))) {
|
|
103
131
|
throw new RangeError("emulate(): screen width and height must be positive integers");
|
|
104
132
|
}
|
|
105
|
-
steps.push({
|
|
133
|
+
steps.push({
|
|
134
|
+
option: "screen",
|
|
135
|
+
method: "emulation.setScreenSettingsOverride",
|
|
136
|
+
params: { screenArea: area },
|
|
137
|
+
});
|
|
106
138
|
}
|
|
107
139
|
if (options.touch !== undefined) {
|
|
108
140
|
if (options.touch !== null && !isPositiveInteger(options.touch)) {
|
|
109
141
|
throw new RangeError("emulate(): touch must be a positive integer");
|
|
110
142
|
}
|
|
111
|
-
steps.push({
|
|
143
|
+
steps.push({
|
|
144
|
+
option: "touch",
|
|
145
|
+
method: "emulation.setTouchOverride",
|
|
146
|
+
params: { maxTouchPoints: options.touch },
|
|
147
|
+
});
|
|
112
148
|
}
|
|
113
149
|
if (options.javaScriptEnabled !== undefined) {
|
|
114
150
|
steps.push({
|
|
@@ -134,7 +170,7 @@ export async function applyEmulation(connector, target, options) {
|
|
|
134
170
|
await connector.send(step.method, { ...step.params, ...target });
|
|
135
171
|
}
|
|
136
172
|
catch (err) {
|
|
137
|
-
if (
|
|
173
|
+
if (isUnsupported(err)) {
|
|
138
174
|
throw new EmulationUnsupportedError(step.option, applied);
|
|
139
175
|
}
|
|
140
176
|
throw new EmulationError(step.option, applied, err);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import EventEmitter from "node:events";
|
|
2
|
-
import
|
|
2
|
+
import { emitSafely } from "./emit.js";
|
|
3
3
|
import { resolveFileInputs } from "./generated-files.js";
|
|
4
4
|
/**
|
|
5
5
|
* A native file picker the page asked for (a click on an `<input type=file>` or its label). Nothing appears on screen:
|
|
@@ -22,10 +22,10 @@ export class FileChooser {
|
|
|
22
22
|
const sharedId = this.info.element
|
|
23
23
|
?.sharedId;
|
|
24
24
|
if (!sharedId)
|
|
25
|
-
throw new Error("file chooser has no element reference");
|
|
25
|
+
throw new Error("setFiles(): the file chooser has no element reference");
|
|
26
26
|
const paths = await resolveFileInputs(files, "setFiles");
|
|
27
27
|
if (paths.length > 1 && !this.info.multiple) {
|
|
28
|
-
throw new Error("file chooser does not accept multiple files");
|
|
28
|
+
throw new Error("setFiles(): the file chooser does not accept multiple files");
|
|
29
29
|
}
|
|
30
30
|
await this.connector.send("input.setFiles", {
|
|
31
31
|
context: this.info.context,
|
|
@@ -46,14 +46,7 @@ export class FileChooserTracker {
|
|
|
46
46
|
if (!this.tree.isWithin(params.context, this.contextId))
|
|
47
47
|
return;
|
|
48
48
|
const chooser = new FileChooser(this.connector, params);
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
listener(chooser);
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
logger.error("A filechooser listener threw", { err });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
49
|
+
emitSafely(this.events, "filechooser", [chooser]);
|
|
57
50
|
};
|
|
58
51
|
}
|
|
59
52
|
static async start(connector, tree, contextId) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
/**
|
|
3
|
+
* Goes one entry back (-1) or forward (1) in the context's history and resolves once the entry is shown. False when
|
|
4
|
+
* there is no such entry.
|
|
5
|
+
*/
|
|
6
|
+
export declare function traverseHistory(connector: BiDiConnector, context: string, delta: -1 | 1): Promise<boolean>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import logger from "../logger/index.js";
|
|
2
|
+
import { callFunction } from "../script/call-function.js";
|
|
3
|
+
import { BiDiError } from "../transport/bidi-error.js";
|
|
4
|
+
/** Time (ms) a history traversal may go without a sign that the entry is shown before it is taken as done */
|
|
5
|
+
const HISTORY_SETTLE_TIMEOUT = 1000;
|
|
6
|
+
/**
|
|
7
|
+
* Goes one entry back (-1) or forward (1) in the context's history and resolves once the entry is shown. False when
|
|
8
|
+
* there is no such entry.
|
|
9
|
+
*/
|
|
10
|
+
export async function traverseHistory(connector, context, delta) {
|
|
11
|
+
if (delta === -1 && (await atFirstEntry(connector, context)))
|
|
12
|
+
return false;
|
|
13
|
+
const settled = await watchHistoryTraversal(connector, context);
|
|
14
|
+
try {
|
|
15
|
+
await connector.send("browsingContext.traverseHistory", {
|
|
16
|
+
context,
|
|
17
|
+
delta,
|
|
18
|
+
});
|
|
19
|
+
// Firefox answers once the entry is shown; Chrome answers before, and a script call in between can hit the
|
|
20
|
+
// document that is going away
|
|
21
|
+
await settled.done;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (err instanceof BiDiError && err.code === "no such history entry")
|
|
26
|
+
return false;
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
await settled.stop();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether going back would only reach the blank page a tab starts with. Firefox does not keep that page in the
|
|
35
|
+
* history, Chrome does; the Navigation API (Chrome only) lets Chrome say the same as Firefox.
|
|
36
|
+
*/
|
|
37
|
+
async function atFirstEntry(connector, context) {
|
|
38
|
+
try {
|
|
39
|
+
return await callFunction(connector, context, `() => {
|
|
40
|
+
const nav = globalThis.navigation;
|
|
41
|
+
if (!nav) return false;
|
|
42
|
+
const entries = nav.entries();
|
|
43
|
+
const index = nav.currentEntry.index;
|
|
44
|
+
return index === 0 || (index === 1 && entries[0].url === "about:blank");
|
|
45
|
+
}`, [], { awaitPromise: false });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
logger.debug("Could not read the history position", { err });
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Starts listening for the end of a history traversal (the new entry committed, or the history or fragment changed
|
|
54
|
+
* within the document). `done` resolves at the first such event, or after a second without one (a page restored from
|
|
55
|
+
* the back-forward cache may say nothing); it never rejects.
|
|
56
|
+
*/
|
|
57
|
+
async function watchHistoryTraversal(connector, context) {
|
|
58
|
+
const events = [
|
|
59
|
+
"browsingContext.navigationCommitted",
|
|
60
|
+
"browsingContext.historyUpdated",
|
|
61
|
+
"browsingContext.fragmentNavigated",
|
|
62
|
+
];
|
|
63
|
+
let subscription;
|
|
64
|
+
try {
|
|
65
|
+
subscription = await connector.subscribe([...events]);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
logger.debug("Could not watch a history traversal", { err });
|
|
69
|
+
return { done: Promise.resolve(), stop: async () => { } };
|
|
70
|
+
}
|
|
71
|
+
let finish;
|
|
72
|
+
const done = new Promise((resolve) => (finish = resolve));
|
|
73
|
+
const timer = setTimeout(finish, HISTORY_SETTLE_TIMEOUT);
|
|
74
|
+
const listener = (info) => {
|
|
75
|
+
if (info.context === context)
|
|
76
|
+
finish();
|
|
77
|
+
};
|
|
78
|
+
for (const event of events)
|
|
79
|
+
connector.onEvent(event, listener);
|
|
80
|
+
return {
|
|
81
|
+
done,
|
|
82
|
+
stop: async () => {
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
for (const event of events)
|
|
85
|
+
connector.offEvent(event, listener);
|
|
86
|
+
await subscription?.unsubscribe().catch(() => undefined);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { Browser, type BrowserLaunchOptions, type ContextOptions, } from "./browser.js";
|
|
2
2
|
export { BrowserContext } from "./browser-context.js";
|
|
3
3
|
export { default as Page } from "./page.js";
|
|
4
|
+
export { DownloadTimeoutError } from "./download.js";
|
|
5
|
+
export { EmulationUnsupportedError } from "./emulation.js";
|
|
4
6
|
export { findBrowser } from "./browser-finder.js";
|
package/dist/browser/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { Browser, } from "./browser.js";
|
|
2
2
|
export { BrowserContext } from "./browser-context.js";
|
|
3
3
|
export { default as Page } from "./page.js";
|
|
4
|
+
export { DownloadTimeoutError } from "./download.js";
|
|
5
|
+
export { EmulationUnsupportedError } from "./emulation.js";
|
|
4
6
|
export { findBrowser } from "./browser-finder.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { toFunctionDeclaration } from "../script/call-function.js";
|
|
2
|
+
/** Wraps an init script as one function declaration; the argument is embedded as JSON because preload scripts take no arguments */
|
|
3
|
+
export function initScriptSource(script, arg) {
|
|
4
|
+
if (typeof script === "string") {
|
|
5
|
+
if (arg !== undefined) {
|
|
6
|
+
throw new TypeError("addInitScript(): a string script does not take an argument; pass a function instead");
|
|
7
|
+
}
|
|
8
|
+
return `() => {\n${script}\n}`;
|
|
9
|
+
}
|
|
10
|
+
const declaration = toFunctionDeclaration(script);
|
|
11
|
+
const argument = arg === undefined ? "undefined" : JSON.stringify(arg);
|
|
12
|
+
if (argument === undefined) {
|
|
13
|
+
throw new TypeError("addInitScript(): the argument must be JSON-serializable");
|
|
14
|
+
}
|
|
15
|
+
return `() => (${declaration})(${argument})`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
import type { BrowserProcess } from "./chrome-launch.js";
|
|
3
|
+
import { type BrowserLaunchOptions } from "./launch-flags.js";
|
|
4
|
+
export interface FirefoxLaunch {
|
|
5
|
+
executablePath: string;
|
|
6
|
+
/** Names the browser in the start timeout error */
|
|
7
|
+
label: string;
|
|
8
|
+
launchOptions: Partial<BrowserLaunchOptions>;
|
|
9
|
+
commandTimeout: number | undefined;
|
|
10
|
+
signal?: AbortSignal | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Starts Firefox, waits for it to announce its BiDi endpoint, connects and opens the BiDi session.
|
|
14
|
+
* Rejects with an Error when the process exits first or the handshake fails, with the abort reason when aborted,
|
|
15
|
+
* and with a message string when the debugging port is taken. A failed launch stops what it started.
|
|
16
|
+
*/
|
|
17
|
+
export declare function launchFirefox(launch: FirefoxLaunch): Promise<{
|
|
18
|
+
proc: BrowserProcess;
|
|
19
|
+
connector: BiDiConnector;
|
|
20
|
+
}>;
|