@itmetsam/samurai-framework 0.9.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 +4 -5
- 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 +14 -1
- package/dist/browser/chrome-launch.js +44 -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 +19 -7
- package/dist/cli/args.js +18 -9
- package/dist/cli/browsers.d.ts +1 -2
- package/dist/cli/browsers.js +2 -2
- package/dist/cli/console-reporter.d.ts +4 -0
- package/dist/cli/console-reporter.js +15 -7
- 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 +3 -8
- 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 +11 -137
- 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 +8 -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 -24
- package/dist/flows/run/handlers/support.js +1 -1
- 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 -49
- package/dist/flows/run/run.d.ts +8 -4
- package/dist/flows/run/run.js +30 -194
- 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 +7 -13
- package/dist/recorder/session.js +22 -117
- 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 +10 -0
- package/dist/runner/browsers.js +12 -0
- package/dist/runner/groups.d.ts +5 -1
- package/dist/runner/groups.js +9 -2
- 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 +40 -11
- package/dist/runner/reporter.js +69 -16
- package/dist/runner/run-test.d.ts +16 -0
- package/dist/runner/run-test.js +178 -0
- package/dist/runner/run.d.ts +5 -2
- package/dist/runner/run.js +28 -28
- 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 +27 -50
- package/dist/runner/test-runner.js +102 -264
- 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 -2
- package/dist/transport/bidi-connection.js +9 -43
- 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 +17 -3
- package/dist/types/test.d.ts +15 -2
- 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
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { expectationFor, secretNameFor } from "./expectation.js";
|
|
2
2
|
/** A picker that opens within this many ms of a click was opened by it */
|
|
3
|
-
|
|
4
|
-
/** The name a secret gets in `secrets.<NAME>`: the field's name or id, upper snake case */
|
|
5
|
-
export function secretNameFor(hint) {
|
|
6
|
-
const name = (hint ?? "")
|
|
7
|
-
.replace(/([a-z\d])([A-Z])/g, "$1_$2")
|
|
8
|
-
.replace(/[^A-Za-z0-9]+/g, "_")
|
|
9
|
-
.replace(/^_+|_+$/g, "")
|
|
10
|
-
.toUpperCase();
|
|
11
|
-
return name === "" || /^\d/.test(name) ? "PASSWORD" : name;
|
|
12
|
-
}
|
|
13
|
-
export function expectationFor(text, value) {
|
|
14
|
-
if (value !== undefined)
|
|
15
|
-
return { matcher: "toHaveValue", expected: value };
|
|
16
|
-
if (text === "")
|
|
17
|
-
return { matcher: "toBeVisible" };
|
|
18
|
-
return text.length <= TEXT_LIMIT
|
|
19
|
-
? { matcher: "toHaveText", expected: text }
|
|
20
|
-
: { matcher: "toContainText", expected: text.slice(0, TEXT_LIMIT).trim() };
|
|
21
|
-
}
|
|
3
|
+
const PICKER_WINDOW = 1000;
|
|
22
4
|
/**
|
|
23
5
|
* Turns what the tester did into steps:
|
|
24
6
|
* - typing into a field becomes one `fill`, rewritten as the text grows (a password becomes a secret reference);
|
|
@@ -93,82 +75,105 @@ export class Normaliser {
|
|
|
93
75
|
this.lastFill = undefined;
|
|
94
76
|
return [this.insert(fromToolbar)];
|
|
95
77
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
78
|
+
switch (observation.type) {
|
|
79
|
+
case "picker":
|
|
80
|
+
return this.pickerOpened(at);
|
|
81
|
+
case "picker-cancel":
|
|
82
|
+
return this.pickerCancelled();
|
|
83
|
+
case "files":
|
|
84
|
+
return this.filesSet(observation, at);
|
|
85
|
+
case "click":
|
|
86
|
+
return this.clicked(observation, at);
|
|
87
|
+
case "press":
|
|
88
|
+
return this.pressed(observation, at);
|
|
89
|
+
case "assert":
|
|
90
|
+
return this.asserted(observation);
|
|
91
|
+
case "input":
|
|
92
|
+
return this.typed(observation, at);
|
|
93
|
+
default:
|
|
94
|
+
return [];
|
|
106
95
|
}
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The sandbox reports a picker for any click on a file input, including ones a page script fires: it is only a
|
|
99
|
+
* hint, and only the very last step, recorded just before, may be replaced or removed (a page's own
|
|
100
|
+
* input.click() also counts as the opening click, bounded to the last step within PICKER_WINDOW)
|
|
101
|
+
*/
|
|
102
|
+
pickerOpened(at) {
|
|
103
|
+
const click = this.lastClick;
|
|
104
|
+
this.picker =
|
|
105
|
+
click && click.index === this.next - 1 && at - click.at <= PICKER_WINDOW
|
|
106
|
+
? click.index
|
|
107
|
+
: undefined;
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
/** The picker closed without a choice: the click that opened it is taken out again */
|
|
111
|
+
pickerCancelled() {
|
|
112
|
+
const index = this.picker;
|
|
113
|
+
this.picker = undefined;
|
|
114
|
+
if (index === undefined || index !== this.next - 1)
|
|
115
|
+
return [];
|
|
116
|
+
this.next--;
|
|
117
|
+
this.lastClick = undefined;
|
|
118
|
+
this.lastStep = undefined;
|
|
119
|
+
return [{ op: "remove", index }];
|
|
120
|
+
}
|
|
121
|
+
filesSet(observation, at) {
|
|
122
|
+
this.lastFill = undefined;
|
|
123
|
+
this.lastAction = at;
|
|
124
|
+
const step = {
|
|
125
|
+
kind: observation.via === "drop" ? "dropFiles" : "setInputFiles",
|
|
126
|
+
locator: observation.locator,
|
|
127
|
+
files: observation.files,
|
|
128
|
+
};
|
|
129
|
+
const index = this.picker;
|
|
130
|
+
if (observation.via === "input" &&
|
|
131
|
+
index !== undefined &&
|
|
132
|
+
index === this.next - 1) {
|
|
109
133
|
this.picker = undefined;
|
|
110
|
-
if (index === undefined || index !== this.next - 1)
|
|
111
|
-
return [];
|
|
112
|
-
this.next--;
|
|
113
134
|
this.lastClick = undefined;
|
|
114
|
-
this.lastStep =
|
|
115
|
-
return [{ op: "
|
|
135
|
+
this.lastStep = step;
|
|
136
|
+
return [{ op: "replace", index, step }];
|
|
116
137
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
138
|
+
return [this.insert(step)];
|
|
139
|
+
}
|
|
140
|
+
clicked(observation, at) {
|
|
141
|
+
if (observation.textEntry)
|
|
142
|
+
return []; // focusing a field: the fill that follows needs no click
|
|
143
|
+
this.lastFill = undefined;
|
|
144
|
+
this.lastAction = at;
|
|
145
|
+
const events = [
|
|
146
|
+
this.insert({ kind: "click", locator: observation.locator }),
|
|
147
|
+
];
|
|
148
|
+
this.lastClick = { index: this.next - 1, at };
|
|
149
|
+
return events;
|
|
150
|
+
}
|
|
151
|
+
pressed(observation, at) {
|
|
152
|
+
// The page may react by clearing the field: what is typed next is a new fill, not a rewrite of this one
|
|
153
|
+
this.lastFill = undefined;
|
|
154
|
+
this.lastAction = at;
|
|
155
|
+
return [
|
|
156
|
+
this.insert({
|
|
157
|
+
kind: "press",
|
|
122
158
|
locator: observation.locator,
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const events = [
|
|
142
|
-
this.insert({ kind: "click", locator: observation.locator }),
|
|
143
|
-
];
|
|
144
|
-
this.lastClick = { index: this.next - 1, at };
|
|
145
|
-
return events;
|
|
146
|
-
}
|
|
147
|
-
if (observation.type === "press") {
|
|
148
|
-
// The page may react by clearing the field: what is typed next is a new fill, not a rewrite of this one
|
|
149
|
-
this.lastFill = undefined;
|
|
150
|
-
this.lastAction = at;
|
|
151
|
-
return [
|
|
152
|
-
this.insert({
|
|
153
|
-
kind: "press",
|
|
154
|
-
locator: observation.locator,
|
|
155
|
-
key: observation.pressed,
|
|
156
|
-
}),
|
|
157
|
-
];
|
|
158
|
-
}
|
|
159
|
-
if (observation.type === "assert") {
|
|
160
|
-
this.lastFill = undefined;
|
|
161
|
-
return [
|
|
162
|
-
this.insert({
|
|
163
|
-
kind: "expect",
|
|
164
|
-
locator: observation.locator,
|
|
165
|
-
not: false,
|
|
166
|
-
expectation: expectationFor(observation.text, observation.value),
|
|
167
|
-
}),
|
|
168
|
-
];
|
|
169
|
-
}
|
|
170
|
-
if (observation.type !== "input")
|
|
171
|
-
return [];
|
|
159
|
+
key: observation.pressed,
|
|
160
|
+
}),
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
/** An Alt+click */
|
|
164
|
+
asserted(observation) {
|
|
165
|
+
this.lastFill = undefined;
|
|
166
|
+
return [
|
|
167
|
+
this.insert({
|
|
168
|
+
kind: "expect",
|
|
169
|
+
locator: observation.locator,
|
|
170
|
+
not: false,
|
|
171
|
+
expectation: expectationFor(observation.text, observation.value),
|
|
172
|
+
}),
|
|
173
|
+
];
|
|
174
|
+
}
|
|
175
|
+
/** Typing: one `fill` per field, rewritten as the text grows */
|
|
176
|
+
typed(observation, at) {
|
|
172
177
|
this.lastAction = at;
|
|
173
178
|
const step = {
|
|
174
179
|
kind: "fill",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { RecorderEvent } from "./normalise.js";
|
|
2
|
+
import type { ControllerDeps, ControllerOptions } from "./toolbar/types.js";
|
|
3
|
+
/** The toolbar's settings: the recording's environment, and where its variables and secrets are written */
|
|
4
|
+
export type ToolbarOptions = ControllerOptions & {
|
|
5
|
+
/** The project folder: `samurai.config.ts` and `.env.<environment>` */
|
|
6
|
+
projectDir: string;
|
|
7
|
+
/**
|
|
8
|
+
* Called when the recording is paused or resumed, or the toolbar fails (or comes back) on a page.
|
|
9
|
+
* There is no initial call: until the first one the recording is `{ state: "recording", toolbar: true }`
|
|
10
|
+
*/
|
|
11
|
+
onStatus?: ControllerDeps["onStatus"];
|
|
12
|
+
/** A secret was saved to `.env.<environment>` from the panel (before its value is typed) */
|
|
13
|
+
onSecretAdded?: (name: string) => void;
|
|
14
|
+
/**
|
|
15
|
+
* The toolbar's Stop; the caller then stops the recording (`recorder.stop()`), as `recordSpec` does.
|
|
16
|
+
* Without it, Stop runs `recorder.stop()` itself: the toolbar goes away and nothing more is recorded, but
|
|
17
|
+
* nothing tells the `page.record()` caller, so pass `onStop` to learn of it.
|
|
18
|
+
*/
|
|
19
|
+
onStop?: () => void;
|
|
20
|
+
};
|
|
21
|
+
/** Where uploads made while recording are stored */
|
|
22
|
+
export interface FileRecording {
|
|
23
|
+
/** The project folder */
|
|
24
|
+
projectDir: string;
|
|
25
|
+
/** Relative to the project folder. @default "files" */
|
|
26
|
+
filesDir?: string;
|
|
27
|
+
/** A file that isn't stored (over 50 MB, or unreadable): its step points at `path`, which doesn't exist */
|
|
28
|
+
onMissing?: (file: MissingFile) => void;
|
|
29
|
+
/** @internal Tests only: also record events no person caused (`isTrusted` false) */
|
|
30
|
+
trustSynthetic?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface MissingFile {
|
|
33
|
+
/** Project-relative, as the step has it */
|
|
34
|
+
path: string;
|
|
35
|
+
name: string;
|
|
36
|
+
size: number;
|
|
37
|
+
reason: "too-big" | "unreadable";
|
|
38
|
+
}
|
|
39
|
+
export interface RecorderOptions {
|
|
40
|
+
/** Called for every step the tester produces, in order. Steps get indexes from `at` on */
|
|
41
|
+
onEvent: (event: RecorderEvent) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Index the first recorded step gets: record from the middle of a test by passing the number of steps
|
|
44
|
+
* before it. The page must already be in the state those steps lead to; replaying them is up to the caller.
|
|
45
|
+
* @default 0
|
|
46
|
+
*/
|
|
47
|
+
at?: number;
|
|
48
|
+
/** Write the page the recording starts on as a `goto` step. Pass false when `at` > 0. @default true */
|
|
49
|
+
initialGoto?: boolean;
|
|
50
|
+
/** URLs inside it are written relative to it, as `page.goto` resolves them */
|
|
51
|
+
baseURL?: string;
|
|
52
|
+
/** Shows the recorder toolbar in the page (pause, assert, insert, wait, screenshot, stop). Without it, no toolbar */
|
|
53
|
+
toolbar?: ToolbarOptions;
|
|
54
|
+
/** Records uploads (file inputs and drops) into the project's files folder. Without it, uploads aren't recorded */
|
|
55
|
+
files?: FileRecording;
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ElementHandle } from "../script/element-handle.js";
|
|
2
|
+
import type { LocatorCall, LocatorSpec } from "../steps/model.js";
|
|
3
|
+
import type { Observation } from "./normalise.js";
|
|
4
|
+
/** What the files capture script sends */
|
|
5
|
+
export type FilesMessage = {
|
|
6
|
+
type: "picker";
|
|
7
|
+
} | {
|
|
8
|
+
type: "picker-cancel";
|
|
9
|
+
} | {
|
|
10
|
+
type: "files";
|
|
11
|
+
via: "input" | "drop";
|
|
12
|
+
target: ElementHandle;
|
|
13
|
+
candidates: LocatorCall[];
|
|
14
|
+
files: {
|
|
15
|
+
id: number;
|
|
16
|
+
name: string;
|
|
17
|
+
type: string;
|
|
18
|
+
size: number;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
/** What the capture script sends */
|
|
22
|
+
export interface PageMessage {
|
|
23
|
+
type: "click" | "input" | "press" | "assert";
|
|
24
|
+
target: ElementHandle;
|
|
25
|
+
candidates: LocatorCall[];
|
|
26
|
+
textEntry?: boolean;
|
|
27
|
+
/** The key of a `press` message */
|
|
28
|
+
pressed?: string;
|
|
29
|
+
value?: string;
|
|
30
|
+
secret?: boolean;
|
|
31
|
+
secretName?: string;
|
|
32
|
+
text?: string;
|
|
33
|
+
}
|
|
34
|
+
/** What a page message says, as the normaliser takes it once its element has a locator */
|
|
35
|
+
export declare function observationFor(message: PageMessage, locator: LocatorSpec): Observation;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** What a page message says, as the normaliser takes it once its element has a locator */
|
|
2
|
+
export function observationFor(message, locator) {
|
|
3
|
+
const key = message.target.sharedId;
|
|
4
|
+
return message.type === "click"
|
|
5
|
+
? { type: "click", key, locator, textEntry: message.textEntry === true }
|
|
6
|
+
: message.type === "press"
|
|
7
|
+
? { type: "press", key, locator, pressed: message.pressed ?? "" }
|
|
8
|
+
: message.type === "assert"
|
|
9
|
+
? {
|
|
10
|
+
type: "assert",
|
|
11
|
+
key,
|
|
12
|
+
locator,
|
|
13
|
+
text: message.text ?? "",
|
|
14
|
+
...(message.value !== undefined && { value: message.value }),
|
|
15
|
+
}
|
|
16
|
+
: {
|
|
17
|
+
type: "input",
|
|
18
|
+
key,
|
|
19
|
+
locator,
|
|
20
|
+
...(message.value !== undefined && { value: message.value }),
|
|
21
|
+
...(message.secret && { secret: true }),
|
|
22
|
+
...(message.secretName !== undefined && {
|
|
23
|
+
secretName: message.secretName,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
}
|
package/dist/recorder/ranking.js
CHANGED
|
@@ -7,6 +7,7 @@ import { locatorFromSpec } from "../steps/build.js";
|
|
|
7
7
|
export async function rankLocator(page, target, candidates, maxFallbacks = 2) {
|
|
8
8
|
const verified = [];
|
|
9
9
|
for (const candidate of candidates) {
|
|
10
|
+
// The primary plus `maxFallbacks` fallbacks: stop once one more than that is verified
|
|
10
11
|
if (verified.length > maxFallbacks)
|
|
11
12
|
break;
|
|
12
13
|
const found = await locatorFromSpec(page, {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
/**
|
|
3
|
+
* The window realms of `sandbox` in the context, as the browser lists them now. A script message names the realm it
|
|
4
|
+
* came from; only messages from a sandbox the page cannot reach are taken.
|
|
5
|
+
*/
|
|
6
|
+
export declare function listSandboxRealms(connector: BiDiConnector, context: string, sandbox: string): Promise<Set<string>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The window realms of `sandbox` in the context, as the browser lists them now. A script message names the realm it
|
|
3
|
+
* came from; only messages from a sandbox the page cannot reach are taken.
|
|
4
|
+
*/
|
|
5
|
+
export async function listSandboxRealms(connector, context, sandbox) {
|
|
6
|
+
const { realms } = await connector.send("script.getRealms", {
|
|
7
|
+
context,
|
|
8
|
+
type: "window",
|
|
9
|
+
});
|
|
10
|
+
return new Set(realms
|
|
11
|
+
.filter((info) => info.type === "window" && info.sandbox === sandbox)
|
|
12
|
+
.map((info) => info.realm));
|
|
13
|
+
}
|
|
@@ -1,62 +1,10 @@
|
|
|
1
1
|
import type Page from "../browser/page.js";
|
|
2
2
|
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { missingFileText } from "./files-recorder.js";
|
|
4
|
+
import type { MissingFile, RecorderOptions } from "./options.js";
|
|
5
5
|
export type { RecorderEvent } from "./normalise.js";
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
/** The project folder: `samurai.config.ts` and `.env.<environment>` */
|
|
9
|
-
projectDir: string;
|
|
10
|
-
/**
|
|
11
|
-
* Called when the recording is paused or resumed, or the toolbar fails (or comes back) on a page.
|
|
12
|
-
* There is no initial call: until the first one the recording is `{ state: "recording", toolbar: true }`
|
|
13
|
-
*/
|
|
14
|
-
onStatus?: ControllerDeps["onStatus"];
|
|
15
|
-
/** A secret was saved to `.env.<environment>` from the panel (before its value is typed) */
|
|
16
|
-
onSecretAdded?: (name: string) => void;
|
|
17
|
-
/**
|
|
18
|
-
* The toolbar's Stop; the caller then stops the recording (`recorder.stop()`), as `recordSpec` does.
|
|
19
|
-
* Without it, Stop runs `recorder.stop()` itself: the toolbar goes away and nothing more is recorded, but
|
|
20
|
-
* nothing tells the `page.record()` caller, so pass `onStop` to learn of it.
|
|
21
|
-
*/
|
|
22
|
-
onStop?: () => void;
|
|
23
|
-
};
|
|
24
|
-
/** Where uploads made while recording are stored */
|
|
25
|
-
export interface FileRecording {
|
|
26
|
-
/** The project folder */
|
|
27
|
-
projectDir: string;
|
|
28
|
-
/** Relative to the project folder. @default "files" */
|
|
29
|
-
filesDir?: string;
|
|
30
|
-
/** A file that isn't stored (over 50 MB, or unreadable): its step points at `path`, which doesn't exist */
|
|
31
|
-
onMissing?: (file: MissingFile) => void;
|
|
32
|
-
/** @internal Tests only: also record events no person caused (`isTrusted` false) */
|
|
33
|
-
trustSynthetic?: boolean;
|
|
34
|
-
}
|
|
35
|
-
export interface MissingFile {
|
|
36
|
-
/** Project-relative, as the step has it */
|
|
37
|
-
path: string;
|
|
38
|
-
name: string;
|
|
39
|
-
size: number;
|
|
40
|
-
reason: "too-big" | "unreadable";
|
|
41
|
-
}
|
|
42
|
-
export interface RecorderOptions {
|
|
43
|
-
/** Called for every step the tester produces, in order. Steps get indexes from `at` on */
|
|
44
|
-
onEvent: (event: RecorderEvent) => void;
|
|
45
|
-
/**
|
|
46
|
-
* Index the first recorded step gets: record from the middle of a test by passing the number of steps
|
|
47
|
-
* before it. The page must already be in the state those steps lead to; replaying them is up to the caller.
|
|
48
|
-
* @default 0
|
|
49
|
-
*/
|
|
50
|
-
at?: number;
|
|
51
|
-
/** Write the page the recording starts on as a `goto` step. Pass false when `at` > 0. @default true */
|
|
52
|
-
initialGoto?: boolean;
|
|
53
|
-
/** URLs inside it are written relative to it, as `page.goto` resolves them */
|
|
54
|
-
baseURL?: string;
|
|
55
|
-
/** Shows the recorder toolbar in the page (pause, assert, insert, wait, screenshot, stop). Without it, no toolbar */
|
|
56
|
-
toolbar?: ToolbarOptions;
|
|
57
|
-
/** Records uploads (file inputs and drops) into the project's files folder. Without it, uploads aren't recorded */
|
|
58
|
-
files?: FileRecording;
|
|
59
|
-
}
|
|
6
|
+
export type { FileRecording, MissingFile, RecorderOptions, ToolbarOptions, } from "./options.js";
|
|
7
|
+
export { missingFileText };
|
|
60
8
|
/**
|
|
61
9
|
* Watches the tester use the page and reports steps as they happen. A preload script reports clicks,
|
|
62
10
|
* typing and Alt+click assertions over a `script.message` channel; each element is given locators that
|
|
@@ -78,23 +26,19 @@ export declare class Recorder {
|
|
|
78
26
|
/** Set when stopping begins: files messages after it are not taken */
|
|
79
27
|
private ending;
|
|
80
28
|
private stopping;
|
|
81
|
-
private
|
|
29
|
+
private toolbar;
|
|
30
|
+
private files;
|
|
82
31
|
private constructor();
|
|
83
32
|
/** The files capture script's channel, handed only to its sandbox */
|
|
84
33
|
private readonly filesChannel;
|
|
85
34
|
private filesPreload;
|
|
86
35
|
private filesRealms;
|
|
87
|
-
|
|
88
|
-
private
|
|
36
|
+
/** The toolbar's controller, when the recording has a toolbar */
|
|
37
|
+
private get controller();
|
|
89
38
|
/** Files stored or reused in the files folder by this recording, project-relative */
|
|
90
39
|
get storedFiles(): string[];
|
|
91
40
|
/** Files whose steps point at a path that wasn't written */
|
|
92
41
|
get missingFiles(): MissingFile[];
|
|
93
|
-
/** The context's `samurai-toolbar` sandbox realms, as last listed: the only realms toolbar messages are taken from */
|
|
94
|
-
private toolbarRealms;
|
|
95
|
-
/** Toolbar messages waiting for a realm lookup, and those that arrived behind them (kept in order) */
|
|
96
|
-
private toolbarBacklog;
|
|
97
|
-
private toolbarWaiting;
|
|
98
42
|
static start(target: {
|
|
99
43
|
page: Page;
|
|
100
44
|
connector: BiDiConnector;
|
|
@@ -108,25 +52,19 @@ export declare class Recorder {
|
|
|
108
52
|
*/
|
|
109
53
|
private readonly onMessage;
|
|
110
54
|
private listFilesRealms;
|
|
111
|
-
private listToolbarRealms;
|
|
112
|
-
/** A message from the toolbar's own sandbox */
|
|
113
|
-
private fromToolbar;
|
|
114
55
|
private readonly onLoad;
|
|
115
56
|
private readonly onNavigation;
|
|
116
57
|
private subscription;
|
|
117
58
|
private preload;
|
|
118
59
|
private toolbarPreload;
|
|
119
60
|
private begin;
|
|
120
|
-
private
|
|
121
|
-
|
|
122
|
-
private callToolbar;
|
|
61
|
+
private createToolbar;
|
|
62
|
+
private createFiles;
|
|
123
63
|
private enqueue;
|
|
124
64
|
private emit;
|
|
125
65
|
private locatorFor;
|
|
126
66
|
private handle;
|
|
127
67
|
private fromFiles;
|
|
128
|
-
/** Saves one kept file to the files folder; returns the path its step gets */
|
|
129
|
-
private storePageFile;
|
|
130
68
|
private release;
|
|
131
69
|
/**
|
|
132
70
|
* Pauses capture in the loaded document: clicks and typing are not reported until `unmute`.
|
|
@@ -140,5 +78,3 @@ export declare class Recorder {
|
|
|
140
78
|
stop(): Promise<void>;
|
|
141
79
|
private end;
|
|
142
80
|
}
|
|
143
|
-
/** The warning for a file that isn't saved, as the toolbar and the CLI show it */
|
|
144
|
-
export declare function missingFileText(file: MissingFile, folder: string): string;
|