@itmetsam/samurai-framework 0.10.0 → 0.12.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 +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
package/dist/steps/parse.js
CHANGED
|
@@ -1,327 +1,6 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
function stringOf(node) {
|
|
5
|
-
return node &&
|
|
6
|
-
(ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
7
|
-
? node.text
|
|
8
|
-
: undefined;
|
|
9
|
-
}
|
|
10
|
-
/** `{ key: literal, … }` with only the allowed keys, or undefined when it has anything else */
|
|
11
|
-
function readObject(node, allowed) {
|
|
12
|
-
if (!node || !ts.isObjectLiteralExpression(node))
|
|
13
|
-
return undefined;
|
|
14
|
-
const result = {};
|
|
15
|
-
for (const property of node.properties) {
|
|
16
|
-
if (!ts.isPropertyAssignment(property) ||
|
|
17
|
-
!allowed.includes(property.name.getText()))
|
|
18
|
-
return undefined;
|
|
19
|
-
const value = stringOf(property.initializer);
|
|
20
|
-
if (value !== undefined)
|
|
21
|
-
result[property.name.getText()] = value;
|
|
22
|
-
else if (property.initializer.kind === ts.SyntaxKind.TrueKeyword)
|
|
23
|
-
result[property.name.getText()] = true;
|
|
24
|
-
else if (property.initializer.kind === ts.SyntaxKind.FalseKeyword)
|
|
25
|
-
result[property.name.getText()] = false;
|
|
26
|
-
else
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
function readCall(method, args) {
|
|
32
|
-
const first = stringOf(args[0]);
|
|
33
|
-
if (first === undefined)
|
|
34
|
-
return undefined;
|
|
35
|
-
switch (method) {
|
|
36
|
-
case "locator":
|
|
37
|
-
return args.length === 1 ? { method, xpath: first } : undefined;
|
|
38
|
-
case "getByCss":
|
|
39
|
-
return args.length === 1 ? { method, css: first } : undefined;
|
|
40
|
-
case "getByTestId":
|
|
41
|
-
return args.length === 1 ? { method, testId: first } : undefined;
|
|
42
|
-
case "getByText":
|
|
43
|
-
case "getByLabel": {
|
|
44
|
-
if (args.length > 2)
|
|
45
|
-
return undefined;
|
|
46
|
-
const options = args.length === 2 ? readObject(args[1], ["match", "ignoreCase"]) : {};
|
|
47
|
-
if (!options)
|
|
48
|
-
return undefined;
|
|
49
|
-
const { match, ignoreCase } = options;
|
|
50
|
-
if ((match !== undefined && match !== "full" && match !== "partial") ||
|
|
51
|
-
(ignoreCase !== undefined && typeof ignoreCase !== "boolean")) {
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
method,
|
|
56
|
-
text: first,
|
|
57
|
-
...(match !== undefined && { match: match }),
|
|
58
|
-
...(ignoreCase !== undefined && { ignoreCase: ignoreCase }),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
case "getByRole": {
|
|
62
|
-
if (args.length > 2)
|
|
63
|
-
return undefined;
|
|
64
|
-
const options = args.length === 2 ? readObject(args[1], ["name"]) : {};
|
|
65
|
-
if (!options ||
|
|
66
|
-
(options.name !== undefined && typeof options.name !== "string"))
|
|
67
|
-
return undefined;
|
|
68
|
-
return {
|
|
69
|
-
method,
|
|
70
|
-
role: first,
|
|
71
|
-
...(options.name !== undefined && { name: options.name }),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
default:
|
|
75
|
-
return undefined;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/** `page.a(…).b(…)` as calls in order; undefined when it is anything else */
|
|
79
|
-
function readChain(node) {
|
|
80
|
-
const calls = [];
|
|
81
|
-
let current = node;
|
|
82
|
-
while (ts.isCallExpression(current)) {
|
|
83
|
-
if (!ts.isPropertyAccessExpression(current.expression))
|
|
84
|
-
return undefined;
|
|
85
|
-
const call = readCall(current.expression.name.text, current.arguments);
|
|
86
|
-
if (!call)
|
|
87
|
-
return undefined;
|
|
88
|
-
calls.unshift(call);
|
|
89
|
-
current = current.expression.expression;
|
|
90
|
-
}
|
|
91
|
-
return ts.isIdentifier(current) && current.text === "page" && calls.length > 0
|
|
92
|
-
? calls
|
|
93
|
-
: undefined;
|
|
94
|
-
}
|
|
95
|
-
function readLocator(node) {
|
|
96
|
-
if (ts.isCallExpression(node) &&
|
|
97
|
-
ts.isPropertyAccessExpression(node.expression) &&
|
|
98
|
-
node.expression.name.text === "withFallbacks") {
|
|
99
|
-
const chain = readChain(node.expression.expression);
|
|
100
|
-
const fallbacks = node.arguments.map(readChain);
|
|
101
|
-
if (!chain ||
|
|
102
|
-
node.arguments.length === 0 ||
|
|
103
|
-
fallbacks.some((fallback) => !fallback))
|
|
104
|
-
return undefined;
|
|
105
|
-
return { chain, fallbacks: fallbacks };
|
|
106
|
-
}
|
|
107
|
-
const chain = readChain(node);
|
|
108
|
-
return chain && { chain, fallbacks: [] };
|
|
109
|
-
}
|
|
110
|
-
/** `env.NAME`, `env["NAME"]`, `secrets.NAME`, `secrets["NAME"]` or a string literal */
|
|
111
|
-
function readValue(node) {
|
|
112
|
-
if (!node)
|
|
113
|
-
return undefined;
|
|
114
|
-
const literal = stringOf(node);
|
|
115
|
-
if (literal !== undefined)
|
|
116
|
-
return { kind: "literal", value: literal };
|
|
117
|
-
let object;
|
|
118
|
-
let name;
|
|
119
|
-
if (ts.isPropertyAccessExpression(node)) {
|
|
120
|
-
object = node.expression;
|
|
121
|
-
name = node.name.text;
|
|
122
|
-
}
|
|
123
|
-
else if (ts.isElementAccessExpression(node)) {
|
|
124
|
-
object = node.expression;
|
|
125
|
-
name = stringOf(node.argumentExpression);
|
|
126
|
-
}
|
|
127
|
-
else
|
|
128
|
-
return undefined;
|
|
129
|
-
if (name === undefined || !ts.isIdentifier(object))
|
|
130
|
-
return undefined;
|
|
131
|
-
if (object.text === "env")
|
|
132
|
-
return { kind: "env", name };
|
|
133
|
-
if (object.text === "secrets")
|
|
134
|
-
return { kind: "secret", name };
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
function readTextMatcher(node) {
|
|
138
|
-
if (!node)
|
|
139
|
-
return undefined;
|
|
140
|
-
const literal = stringOf(node);
|
|
141
|
-
if (literal !== undefined)
|
|
142
|
-
return literal;
|
|
143
|
-
if (ts.isRegularExpressionLiteral(node)) {
|
|
144
|
-
const end = node.text.lastIndexOf("/");
|
|
145
|
-
return {
|
|
146
|
-
regex: {
|
|
147
|
-
source: node.text.slice(1, end),
|
|
148
|
-
flags: node.text.slice(end + 1),
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
function readExpectation(matcher, args) {
|
|
155
|
-
if (matcher === "toBeVisible")
|
|
156
|
-
return args.length === 0 ? { matcher } : undefined;
|
|
157
|
-
if (matcher === "toHaveCount") {
|
|
158
|
-
const [count] = args;
|
|
159
|
-
return args.length === 1 && count && ts.isNumericLiteral(count)
|
|
160
|
-
? { matcher, expected: Number(count.text) }
|
|
161
|
-
: undefined;
|
|
162
|
-
}
|
|
163
|
-
if (matcher === "toHaveAttribute") {
|
|
164
|
-
const name = stringOf(args[0]);
|
|
165
|
-
const expected = readTextMatcher(args[1]);
|
|
166
|
-
return args.length === 2 && name !== undefined && expected !== undefined
|
|
167
|
-
? { matcher, name, expected }
|
|
168
|
-
: undefined;
|
|
169
|
-
}
|
|
170
|
-
if (TEXT_MATCHERS.includes(matcher)) {
|
|
171
|
-
const expected = readTextMatcher(args[0]);
|
|
172
|
-
return args.length === 1 && expected !== undefined
|
|
173
|
-
? { matcher: matcher, expected }
|
|
174
|
-
: undefined;
|
|
175
|
-
}
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
|
-
/** `"path"` or `generatedFile({ name, size, type? })`; undefined for anything else */
|
|
179
|
-
function readFileEntry(node) {
|
|
180
|
-
const path = stringOf(node);
|
|
181
|
-
if (path !== undefined)
|
|
182
|
-
return { path };
|
|
183
|
-
if (!ts.isCallExpression(node) ||
|
|
184
|
-
!ts.isIdentifier(node.expression) ||
|
|
185
|
-
node.expression.text !== "generatedFile" ||
|
|
186
|
-
node.arguments.length !== 1 ||
|
|
187
|
-
!ts.isObjectLiteralExpression(node.arguments[0]))
|
|
188
|
-
return undefined;
|
|
189
|
-
let name;
|
|
190
|
-
let type;
|
|
191
|
-
let size;
|
|
192
|
-
let sizeSource;
|
|
193
|
-
for (const property of node.arguments[0].properties) {
|
|
194
|
-
if (!ts.isPropertyAssignment(property))
|
|
195
|
-
return undefined;
|
|
196
|
-
const value = property.initializer;
|
|
197
|
-
switch (property.name.getText()) {
|
|
198
|
-
case "name":
|
|
199
|
-
name = stringOf(value);
|
|
200
|
-
if (name === undefined)
|
|
201
|
-
return undefined;
|
|
202
|
-
break;
|
|
203
|
-
case "type":
|
|
204
|
-
type = stringOf(value);
|
|
205
|
-
if (type === undefined)
|
|
206
|
-
return undefined;
|
|
207
|
-
break;
|
|
208
|
-
case "size": {
|
|
209
|
-
const text = stringOf(value);
|
|
210
|
-
size =
|
|
211
|
-
text !== undefined
|
|
212
|
-
? parseSize(text)
|
|
213
|
-
: ts.isNumericLiteral(value)
|
|
214
|
-
? parseSize(Number(value.text))
|
|
215
|
-
: undefined;
|
|
216
|
-
if (size === undefined)
|
|
217
|
-
return undefined;
|
|
218
|
-
sizeSource = value.getText();
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
default:
|
|
222
|
-
return undefined;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (name === undefined || size === undefined)
|
|
226
|
-
return undefined;
|
|
227
|
-
return {
|
|
228
|
-
generate: {
|
|
229
|
-
name,
|
|
230
|
-
size,
|
|
231
|
-
...(type !== undefined && { type }),
|
|
232
|
-
...(sizeSource !== undefined && { sizeSource }),
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
/** One file entry, or an array of them */
|
|
237
|
-
function readFiles(node) {
|
|
238
|
-
if (!node)
|
|
239
|
-
return undefined;
|
|
240
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
241
|
-
const entries = node.elements.map(readFileEntry);
|
|
242
|
-
return entries.every((entry) => entry !== undefined)
|
|
243
|
-
? entries
|
|
244
|
-
: undefined;
|
|
245
|
-
}
|
|
246
|
-
const entry = readFileEntry(node);
|
|
247
|
-
return entry && [entry];
|
|
248
|
-
}
|
|
249
|
-
/** The step a statement stands for, or undefined when it is outside the recorder's set */
|
|
250
|
-
function readStep(statement) {
|
|
251
|
-
if (!ts.isExpressionStatement(statement) ||
|
|
252
|
-
!ts.isAwaitExpression(statement.expression))
|
|
253
|
-
return undefined;
|
|
254
|
-
const call = statement.expression.expression;
|
|
255
|
-
if (!ts.isCallExpression(call) ||
|
|
256
|
-
!ts.isPropertyAccessExpression(call.expression))
|
|
257
|
-
return undefined;
|
|
258
|
-
const { expression: target, name } = call.expression;
|
|
259
|
-
const args = call.arguments;
|
|
260
|
-
if (ts.isIdentifier(target) && target.text === "page") {
|
|
261
|
-
if (name.text === "goto" && args.length === 1) {
|
|
262
|
-
const url = stringOf(args[0]);
|
|
263
|
-
return url === undefined ? undefined : { kind: "goto", url };
|
|
264
|
-
}
|
|
265
|
-
if (name.text === "waitForNetworkIdle" && args.length === 0)
|
|
266
|
-
return { kind: "waitForNetworkIdle" };
|
|
267
|
-
if (name.text === "waitForTimeout" &&
|
|
268
|
-
args.length === 1 &&
|
|
269
|
-
ts.isNumericLiteral(args[0])) {
|
|
270
|
-
const ms = Number(args[0].text);
|
|
271
|
-
return Number.isInteger(ms) ? { kind: "waitForTimeout", ms } : undefined;
|
|
272
|
-
}
|
|
273
|
-
if (name.text === "screenshot" && args.length === 1) {
|
|
274
|
-
const options = readObject(args[0], ["name", "fullPage"]);
|
|
275
|
-
if (!options || typeof options.name !== "string")
|
|
276
|
-
return undefined;
|
|
277
|
-
if (options.fullPage !== undefined &&
|
|
278
|
-
typeof options.fullPage !== "boolean")
|
|
279
|
-
return undefined;
|
|
280
|
-
return {
|
|
281
|
-
kind: "screenshot",
|
|
282
|
-
name: options.name,
|
|
283
|
-
fullPage: options.fullPage === true,
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
return undefined;
|
|
287
|
-
}
|
|
288
|
-
// expect(locator).matcher(…) and expect(locator).not.matcher(…)
|
|
289
|
-
const negated = ts.isPropertyAccessExpression(target) && target.name.text === "not";
|
|
290
|
-
const subject = negated
|
|
291
|
-
? target.expression
|
|
292
|
-
: target;
|
|
293
|
-
if (ts.isCallExpression(subject) &&
|
|
294
|
-
ts.isIdentifier(subject.expression) &&
|
|
295
|
-
subject.expression.text === "expect" &&
|
|
296
|
-
subject.arguments.length === 1) {
|
|
297
|
-
const locator = readLocator(subject.arguments[0]);
|
|
298
|
-
const expectation = readExpectation(name.text, args);
|
|
299
|
-
return locator && expectation
|
|
300
|
-
? { kind: "expect", locator, not: negated, expectation }
|
|
301
|
-
: undefined;
|
|
302
|
-
}
|
|
303
|
-
const locator = readLocator(target);
|
|
304
|
-
if (!locator)
|
|
305
|
-
return undefined;
|
|
306
|
-
if (name.text === "click" && args.length === 0)
|
|
307
|
-
return { kind: "click", locator };
|
|
308
|
-
if (name.text === "press" && args.length === 1) {
|
|
309
|
-
const key = stringOf(args[0]);
|
|
310
|
-
return key === undefined ? undefined : { kind: "press", locator, key };
|
|
311
|
-
}
|
|
312
|
-
if (name.text === "fill" && args.length === 1) {
|
|
313
|
-
const value = readValue(args[0]);
|
|
314
|
-
return value ? { kind: "fill", locator, value } : undefined;
|
|
315
|
-
}
|
|
316
|
-
if ((name.text === "setInputFiles" || name.text === "dropFiles") &&
|
|
317
|
-
args.length === 1) {
|
|
318
|
-
const files = readFiles(args[0]);
|
|
319
|
-
return files
|
|
320
|
-
? { kind: name.text, locator, files }
|
|
321
|
-
: undefined;
|
|
322
|
-
}
|
|
323
|
-
return undefined;
|
|
324
|
-
}
|
|
2
|
+
import { stringOf } from "./parse/literals.js";
|
|
3
|
+
import { readStep } from "./parse/step.js";
|
|
325
4
|
function parseStatement(source, statement, sourceFile) {
|
|
326
5
|
const start = statement.getStart(sourceFile);
|
|
327
6
|
const end = statement.end;
|
|
@@ -3,7 +3,7 @@ import { type EventName, type Subscription, type SubscribeOptions } from "./subs
|
|
|
3
3
|
/** Time (ms) a command may wait for its reply before it fails with code "timeout" */
|
|
4
4
|
export declare const DEFAULT_COMMAND_TIMEOUT = 30000;
|
|
5
5
|
export interface ConnectorOptions {
|
|
6
|
-
/** @default
|
|
6
|
+
/** @default DEFAULT_COMMAND_TIMEOUT */
|
|
7
7
|
commandTimeout?: number;
|
|
8
8
|
}
|
|
9
9
|
export interface SendOptions {
|
|
@@ -28,6 +28,7 @@ export declare class BiDiConnector {
|
|
|
28
28
|
/** Which browser is on the other end, when the launcher says; code that must treat browsers apart asks this */
|
|
29
29
|
browserName?: "firefox" | "chrome";
|
|
30
30
|
private webSocket;
|
|
31
|
+
private closeListeners;
|
|
31
32
|
private eventEmitter;
|
|
32
33
|
private commandTimeout;
|
|
33
34
|
private subscriptions;
|
|
@@ -35,10 +36,11 @@ export declare class BiDiConnector {
|
|
|
35
36
|
static connect(url: string, options?: ConnectorOptions): Promise<BiDiConnector>;
|
|
36
37
|
/** Calls `listener` once when the socket closes or fails (not when `kill()` ends it) */
|
|
37
38
|
onClosed(listener: () => void): void;
|
|
38
|
-
private closeListeners;
|
|
39
39
|
private notifyClosed;
|
|
40
40
|
private onWebsocketClose;
|
|
41
41
|
private onWebsocketError;
|
|
42
|
+
/** The socket is gone: tell the listeners and fail what is waiting for an answer */
|
|
43
|
+
private lose;
|
|
42
44
|
private get isClosed();
|
|
43
45
|
/** Rejects every pending command; `reason` gets the pending method names for its message */
|
|
44
46
|
private rejectAll;
|
|
@@ -65,16 +67,3 @@ export declare class BiDiConnector {
|
|
|
65
67
|
private messageListener;
|
|
66
68
|
kill(): void;
|
|
67
69
|
}
|
|
68
|
-
/**
|
|
69
|
-
* The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
|
|
70
|
-
* action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
|
|
71
|
-
* (an added secret is only registered for masking after it is saved).
|
|
72
|
-
*/
|
|
73
|
-
export declare function forLog(message: {
|
|
74
|
-
type?: string;
|
|
75
|
-
method?: string;
|
|
76
|
-
params?: {
|
|
77
|
-
channel?: unknown;
|
|
78
|
-
data?: unknown;
|
|
79
|
-
};
|
|
80
|
-
}): unknown;
|
|
@@ -2,6 +2,7 @@ import EventEmitter from "node:events";
|
|
|
2
2
|
import logger from "../logger/index.js";
|
|
3
3
|
import { WaitTimeoutError } from "../wait/wait-until.js";
|
|
4
4
|
import { BiDiError } from "./bidi-error.js";
|
|
5
|
+
import { forLog } from "./log-redaction.js";
|
|
5
6
|
import { SubscriptionManager, } from "./subscriptions.js";
|
|
6
7
|
/** Time (ms) a command may wait for its reply before it fails with code "timeout" */
|
|
7
8
|
export const DEFAULT_COMMAND_TIMEOUT = 30000;
|
|
@@ -23,18 +24,8 @@ export class BiDiConnector {
|
|
|
23
24
|
/** Set once the socket closed or failed: nothing sent after that gets an answer */
|
|
24
25
|
this.closed = false;
|
|
25
26
|
this.closeListeners = new Set();
|
|
26
|
-
this.onWebsocketClose = () =>
|
|
27
|
-
|
|
28
|
-
this.notifyClosed();
|
|
29
|
-
this.rejectAll((pending) => new ConnectionClosedError(`Websocket closed${pendingSuffix(pending)}`));
|
|
30
|
-
};
|
|
31
|
-
this.onWebsocketError = (ev) => {
|
|
32
|
-
this.closed = true;
|
|
33
|
-
this.notifyClosed();
|
|
34
|
-
this.rejectAll((pending) => new ConnectionClosedError(`Websocket error${pendingSuffix(pending)}`, {
|
|
35
|
-
cause: ev,
|
|
36
|
-
}));
|
|
37
|
-
};
|
|
27
|
+
this.onWebsocketClose = () => this.lose("Websocket closed");
|
|
28
|
+
this.onWebsocketError = (ev) => this.lose("Websocket error", ev);
|
|
38
29
|
this.messageListener = (ev) => {
|
|
39
30
|
const message = JSON.parse(ev.data);
|
|
40
31
|
logger.debug("Received message from BiDi Websocket: ", forLog(message));
|
|
@@ -97,6 +88,12 @@ export class BiDiConnector {
|
|
|
97
88
|
for (const listener of listeners)
|
|
98
89
|
listener();
|
|
99
90
|
}
|
|
91
|
+
/** The socket is gone: tell the listeners and fail what is waiting for an answer */
|
|
92
|
+
lose(what, cause) {
|
|
93
|
+
this.closed = true;
|
|
94
|
+
this.notifyClosed();
|
|
95
|
+
this.rejectAll((pending) => new ConnectionClosedError(`${what}${pendingSuffix(pending)}`, cause === undefined ? undefined : { cause }));
|
|
96
|
+
}
|
|
100
97
|
get isClosed() {
|
|
101
98
|
// A socket that is closing or closed (readyState 2 or 3) has already lost the browser
|
|
102
99
|
const state = this.webSocket.readyState;
|
|
@@ -209,57 +206,6 @@ export class BiDiConnector {
|
|
|
209
206
|
this.webSocket.close();
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
|
-
/** Channels whose messages carry what the tester typed (the recorder's) or adds as a secret (its toolbar's) */
|
|
213
|
-
const PRIVATE_CHANNEL = /^samurai-(recorder|toolbar)-/;
|
|
214
|
-
/**
|
|
215
|
-
* The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
|
|
216
|
-
* action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
|
|
217
|
-
* (an added secret is only registered for masking after it is saved).
|
|
218
|
-
*/
|
|
219
|
-
export function forLog(message) {
|
|
220
|
-
return shortened(redacted(message));
|
|
221
|
-
}
|
|
222
|
-
/** A string longer than this is cut in the log: a file's bytes travel in 1 MB chunks, 50 MB makes 70 MB of debug log */
|
|
223
|
-
const LOG_STRING_LIMIT = 1000;
|
|
224
|
-
/** A copy of `value` with every long string cut to its start and its length; short values come back as they are */
|
|
225
|
-
function shortened(value, depth = 0) {
|
|
226
|
-
if (typeof value === "string")
|
|
227
|
-
return value.length > LOG_STRING_LIMIT
|
|
228
|
-
? `${value.slice(0, 200)}… (${value.length} characters)`
|
|
229
|
-
: value;
|
|
230
|
-
if (value === null || typeof value !== "object" || depth > 30)
|
|
231
|
-
return value;
|
|
232
|
-
if (Array.isArray(value))
|
|
233
|
-
return value.map((item) => shortened(item, depth + 1));
|
|
234
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
235
|
-
key,
|
|
236
|
-
shortened(item, depth + 1),
|
|
237
|
-
]));
|
|
238
|
-
}
|
|
239
|
-
function redacted(message) {
|
|
240
|
-
const channel = message.params?.channel;
|
|
241
|
-
if (message.type !== "event" ||
|
|
242
|
-
message.method !== "script.message" ||
|
|
243
|
-
typeof channel !== "string" ||
|
|
244
|
-
!PRIVATE_CHANNEL.test(channel))
|
|
245
|
-
return message;
|
|
246
|
-
const entries = message.params?.data
|
|
247
|
-
?.value;
|
|
248
|
-
const field = (key) => {
|
|
249
|
-
if (!Array.isArray(entries))
|
|
250
|
-
return undefined;
|
|
251
|
-
const entry = entries.find((pair) => Array.isArray(pair) && pair[0] === key);
|
|
252
|
-
const value = entry?.[1]?.value;
|
|
253
|
-
return typeof value === "string" ? value : undefined;
|
|
254
|
-
};
|
|
255
|
-
return {
|
|
256
|
-
...message,
|
|
257
|
-
params: {
|
|
258
|
-
...message.params,
|
|
259
|
-
data: { type: field("type"), action: field("action"), redacted: true },
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
209
|
function pendingSuffix(pending) {
|
|
264
210
|
return pending.length > 0 ? ` (pending: ${pending.join(", ")})` : "";
|
|
265
211
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
|
|
3
|
+
* action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
|
|
4
|
+
* (an added secret is only registered for masking after it is saved).
|
|
5
|
+
*/
|
|
6
|
+
export declare function forLog(message: {
|
|
7
|
+
type?: string;
|
|
8
|
+
method?: string;
|
|
9
|
+
params?: {
|
|
10
|
+
channel?: unknown;
|
|
11
|
+
data?: unknown;
|
|
12
|
+
};
|
|
13
|
+
}): unknown;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Channels whose messages carry what the tester typed (the recorder's) or adds as a secret (its toolbar's) */
|
|
2
|
+
const PRIVATE_CHANNEL = /^samurai-(recorder|toolbar)-/;
|
|
3
|
+
/**
|
|
4
|
+
* The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
|
|
5
|
+
* action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
|
|
6
|
+
* (an added secret is only registered for masking after it is saved).
|
|
7
|
+
*/
|
|
8
|
+
export function forLog(message) {
|
|
9
|
+
return shortened(redacted(message));
|
|
10
|
+
}
|
|
11
|
+
/** A string longer than this is cut in the log: a file's bytes travel in 1 MB chunks, 50 MB makes 70 MB of debug log */
|
|
12
|
+
const LOG_STRING_LIMIT = 1000;
|
|
13
|
+
/** A copy of `value` with every long string cut to its start and its length; short values come back as they are */
|
|
14
|
+
function shortened(value, depth = 0) {
|
|
15
|
+
if (typeof value === "string")
|
|
16
|
+
return value.length > LOG_STRING_LIMIT
|
|
17
|
+
? `${value.slice(0, 200)}… (${value.length} characters)`
|
|
18
|
+
: value;
|
|
19
|
+
if (value === null || typeof value !== "object" || depth > 30)
|
|
20
|
+
return value;
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.map((item) => shortened(item, depth + 1));
|
|
23
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
24
|
+
key,
|
|
25
|
+
shortened(item, depth + 1),
|
|
26
|
+
]));
|
|
27
|
+
}
|
|
28
|
+
function redacted(message) {
|
|
29
|
+
const channel = message.params?.channel;
|
|
30
|
+
if (message.type !== "event" ||
|
|
31
|
+
message.method !== "script.message" ||
|
|
32
|
+
typeof channel !== "string" ||
|
|
33
|
+
!PRIVATE_CHANNEL.test(channel))
|
|
34
|
+
return message;
|
|
35
|
+
const entries = message.params?.data
|
|
36
|
+
?.value;
|
|
37
|
+
const field = (key) => {
|
|
38
|
+
if (!Array.isArray(entries))
|
|
39
|
+
return undefined;
|
|
40
|
+
const entry = entries.find((pair) => Array.isArray(pair) && pair[0] === key);
|
|
41
|
+
const value = entry?.[1]?.value;
|
|
42
|
+
return typeof value === "string" ? value : undefined;
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
...message,
|
|
46
|
+
params: {
|
|
47
|
+
...message.params,
|
|
48
|
+
data: { type: field("type"), action: field("action"), redacted: true },
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -21,26 +21,21 @@ export class SubscriptionManager {
|
|
|
21
21
|
await Promise.allSettled(acquired.map((event) => this.release(event)));
|
|
22
22
|
throw err;
|
|
23
23
|
}
|
|
24
|
-
let released = false;
|
|
25
24
|
return {
|
|
26
|
-
unsubscribe: async () => {
|
|
27
|
-
if (released)
|
|
28
|
-
return;
|
|
29
|
-
released = true;
|
|
25
|
+
unsubscribe: once(async () => {
|
|
30
26
|
await Promise.all(acquired.map((event) => this.release(event)));
|
|
31
|
-
},
|
|
27
|
+
}),
|
|
32
28
|
};
|
|
33
29
|
}
|
|
34
30
|
async subscribeScoped(events, contexts) {
|
|
35
|
-
const { subscription } = await this.commands.subscribe({
|
|
36
|
-
|
|
31
|
+
const { subscription } = await this.commands.subscribe({
|
|
32
|
+
events,
|
|
33
|
+
contexts,
|
|
34
|
+
});
|
|
37
35
|
return {
|
|
38
|
-
unsubscribe: async () => {
|
|
39
|
-
if (released)
|
|
40
|
-
return;
|
|
41
|
-
released = true;
|
|
36
|
+
unsubscribe: once(async () => {
|
|
42
37
|
await this.commands.unsubscribe({ subscriptions: [subscription] });
|
|
43
|
-
},
|
|
38
|
+
}),
|
|
44
39
|
};
|
|
45
40
|
}
|
|
46
41
|
state(event) {
|
|
@@ -79,3 +74,13 @@ export class SubscriptionManager {
|
|
|
79
74
|
return step;
|
|
80
75
|
}
|
|
81
76
|
}
|
|
77
|
+
/** `fn` runs on the first call only; later calls resolve at once */
|
|
78
|
+
function once(fn) {
|
|
79
|
+
let called = false;
|
|
80
|
+
return async () => {
|
|
81
|
+
if (called)
|
|
82
|
+
return;
|
|
83
|
+
called = true;
|
|
84
|
+
await fn();
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BiDiError } from "./bidi-error.js";
|
|
2
|
+
/** The browser does not implement a command, a locator type or an option */
|
|
3
|
+
export declare class UnsupportedOperationError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/** Whether the browser answered that it does not know the command: Firefox says "unknown command", others "unsupported operation" */
|
|
7
|
+
export declare function isUnsupported(err: unknown): err is BiDiError;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BiDiError } from "./bidi-error.js";
|
|
2
|
+
/** The browser does not implement a command, a locator type or an option */
|
|
3
|
+
export class UnsupportedOperationError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "UnsupportedOperationError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Whether the browser answered that it does not know the command: Firefox says "unknown command", others "unsupported operation" */
|
|
10
|
+
export function isUnsupported(err) {
|
|
11
|
+
return (err instanceof BiDiError &&
|
|
12
|
+
(err.code === "unknown command" || err.code === "unsupported operation"));
|
|
13
|
+
}
|
|
@@ -44,18 +44,7 @@ export interface BrowserCommands {
|
|
|
44
44
|
|
|
45
45
|
/** How downloads are handled. */
|
|
46
46
|
export type DownloadBehavior =
|
|
47
|
-
|
|
48
|
-
| { type: "denied" };
|
|
49
|
-
|
|
50
|
-
/** Union of all possible result types returned by browser module commands. */
|
|
51
|
-
export type BrowserResult =
|
|
52
|
-
| CloseResult
|
|
53
|
-
| CreateUserContextResult
|
|
54
|
-
| GetClientWindowsResult
|
|
55
|
-
| GetUserContextsResult
|
|
56
|
-
| RemoveUserContextResult
|
|
57
|
-
| SetClientWindowStateResult
|
|
58
|
-
| SetDownloadBehaviorResult;
|
|
47
|
+
{ type: "allowed"; destinationFolder: string } | { type: "denied" };
|
|
59
48
|
|
|
60
49
|
/** Result of `browser.close`. */
|
|
61
50
|
export type CloseResult = EmptyResult;
|
|
@@ -55,7 +55,11 @@ export interface BrowsingContextModule {
|
|
|
55
55
|
};
|
|
56
56
|
/** Accepts or dismisses the open user prompt. */
|
|
57
57
|
"browsingContext.handleUserPrompt": {
|
|
58
|
-
params: {
|
|
58
|
+
params: {
|
|
59
|
+
context: BrowsingContext;
|
|
60
|
+
accept?: boolean | undefined;
|
|
61
|
+
userText?: string | undefined;
|
|
62
|
+
};
|
|
59
63
|
result: {};
|
|
60
64
|
};
|
|
61
65
|
/** Sets or resets (null) the viewport size and device pixel ratio. */
|
|
@@ -92,7 +96,9 @@ export interface BrowsingContextModule {
|
|
|
92
96
|
params: {
|
|
93
97
|
context: BrowsingContext;
|
|
94
98
|
background?: boolean | undefined;
|
|
95
|
-
margin?:
|
|
99
|
+
margin?:
|
|
100
|
+
| { top?: number; bottom?: number; left?: number; right?: number }
|
|
101
|
+
| undefined;
|
|
96
102
|
orientation?: "portrait" | "landscape" | undefined;
|
|
97
103
|
page?: { width?: number; height?: number } | undefined;
|
|
98
104
|
pageRanges?: (number | string)[] | undefined;
|
|
@@ -360,22 +366,6 @@ export type BrowsingContextEvent =
|
|
|
360
366
|
| UserPromptClosed
|
|
361
367
|
| UserPromptOpened;
|
|
362
368
|
|
|
363
|
-
/** Union of all possible result types returned by browsingContext module commands. */
|
|
364
|
-
export type BrowsingContextResult =
|
|
365
|
-
| ActivateResult
|
|
366
|
-
| CaptureScreenshotResult
|
|
367
|
-
| CloseResult
|
|
368
|
-
| CreateResult
|
|
369
|
-
| GetTreeResult
|
|
370
|
-
| HandleUserPromptResult
|
|
371
|
-
| LocateNodesResult
|
|
372
|
-
| NavigateResult
|
|
373
|
-
| PrintResult
|
|
374
|
-
| ReloadResult
|
|
375
|
-
| SetBypassCSPResult
|
|
376
|
-
| SetViewportResult
|
|
377
|
-
| TraverseHistoryResult;
|
|
378
|
-
|
|
379
369
|
/** Result of `browsingContext.activate`. */
|
|
380
370
|
export type ActivateResult = EmptyResult;
|
|
381
371
|
|