@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
package/dist/flows/run/run.js
CHANGED
|
@@ -1,55 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {} from "../core/index.js";
|
|
2
2
|
import logger from "../../logger/index.js";
|
|
3
3
|
import { STEP_NODES } from "../steps/catalog.js";
|
|
4
4
|
import { HANDLERS } from "./handlers/index.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return reject(new Error("aborted"));
|
|
8
|
-
const onAbort = () => {
|
|
9
|
-
clearTimeout(timer);
|
|
10
|
-
reject(new Error("aborted"));
|
|
11
|
-
};
|
|
12
|
-
const timer = setTimeout(() => {
|
|
13
|
-
signal.removeEventListener("abort", onAbort);
|
|
14
|
-
resolve();
|
|
15
|
-
}, ms);
|
|
16
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
17
|
-
});
|
|
18
|
-
class ShapeError extends Error {
|
|
19
|
-
}
|
|
20
|
-
const dynamicBranches = (def) => def.shape === "branches" && !!def.branches && !Array.isArray(def.branches);
|
|
5
|
+
import { assertRunnable, defaultSleep, definitionOf, ShapeError, shapeError, } from "./prepare-flow.js";
|
|
6
|
+
import { FlowWalker } from "./walker.js";
|
|
21
7
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* fill order; an end stops. Branches meet again at `meetOf`.
|
|
8
|
+
* Runs a flow: sets up what its nodes share (expression context, handler access to the tests), walks it from
|
|
9
|
+
* Start (see `FlowWalker`) and reports how it ended. A flow whose shape can't run ends with an `error`; a flow
|
|
10
|
+
* with a kind that has no handler throws before it starts, unless `unsupported` is `"skip"`.
|
|
26
11
|
*/
|
|
27
12
|
export async function runFlow(options) {
|
|
28
13
|
const { flow, onEvent = () => { } } = options;
|
|
29
14
|
const signal = options.signal ?? new AbortController().signal;
|
|
30
15
|
const handlers = options.handlers ?? HANDLERS;
|
|
31
16
|
const nodes = options.nodes ?? STEP_NODES;
|
|
32
|
-
|
|
33
|
-
(
|
|
34
|
-
try {
|
|
35
|
-
return nodeDef(kind);
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
})();
|
|
41
|
-
if ((options.unsupported ?? "fail") === "fail") {
|
|
42
|
-
const problems = flow.nodes
|
|
43
|
-
.map((n) => ({ n, def: defOf(n.kind) }))
|
|
44
|
-
.filter(({ n, def }) => def &&
|
|
45
|
-
(def.shape === "step" || def.shape === "branches") &&
|
|
46
|
-
!handlers[n.kind])
|
|
47
|
-
.map(({ def }) => `${def.label} can't run yet.`);
|
|
48
|
-
if (problems.length)
|
|
49
|
-
throw new Error(`This flow can't run: ${[...new Set(problems)].join(" ")}`);
|
|
50
|
-
}
|
|
51
|
-
const byId = new Map(flow.nodes.map((n) => [n.id, n]));
|
|
52
|
-
const results = {};
|
|
17
|
+
if ((options.unsupported ?? "fail") === "fail")
|
|
18
|
+
assertRunnable(flow, nodes, handlers);
|
|
53
19
|
const context = {
|
|
54
20
|
env: { ...options.environment.variables },
|
|
55
21
|
vars: {},
|
|
@@ -60,181 +26,51 @@ export async function runFlow(options) {
|
|
|
60
26
|
startedAt: options.startedAt ?? new Date().toISOString(),
|
|
61
27
|
},
|
|
62
28
|
};
|
|
63
|
-
const
|
|
29
|
+
const handlerRunFor = (context, signal) => ({
|
|
64
30
|
flow,
|
|
65
31
|
environment: options.environment,
|
|
66
32
|
context,
|
|
67
33
|
nodeTests: options.nodeTests,
|
|
34
|
+
...(options.nodeBrowsers && { nodeBrowsers: options.nodeBrowsers }),
|
|
68
35
|
signal,
|
|
69
36
|
sleep: options.sleep ?? defaultSleep,
|
|
70
|
-
runTests: (tests, onTestEvent) => options.runTests({
|
|
37
|
+
runTests: (tests, onTestEvent, browsers) => options.runTests({
|
|
38
|
+
tests,
|
|
39
|
+
onEvent: onTestEvent,
|
|
40
|
+
signal,
|
|
41
|
+
...(browsers && { browsers }),
|
|
42
|
+
}),
|
|
71
43
|
onTestEvent: (nodeId, event) => onEvent({ type: "test", nodeId, event }),
|
|
72
44
|
nodes,
|
|
73
45
|
log: (nodeId, level, text) => logger[level](`[${nodeId}] ${text}`),
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
error: result.error,
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
const out = (id) => flow.edges.filter((e) => e.source === id);
|
|
88
|
-
/** Marks every node from `from` up to `stop` as skipped (a branch not taken) */
|
|
89
|
-
const skip = (from, stop) => {
|
|
90
|
-
const seen = new Set();
|
|
91
|
-
const visit = (id) => {
|
|
92
|
-
if (id === stop || seen.has(id))
|
|
93
|
-
return;
|
|
94
|
-
seen.add(id);
|
|
95
|
-
const node = byId.get(id);
|
|
96
|
-
if (node && !results[id]) {
|
|
97
|
-
record(node, { status: "skipped" });
|
|
98
|
-
onEvent({
|
|
99
|
-
type: "node-end",
|
|
100
|
-
nodeId: id,
|
|
101
|
-
result: { status: "skipped" },
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
for (const e of out(id))
|
|
105
|
-
visit(e.target);
|
|
106
|
-
};
|
|
107
|
-
visit(from);
|
|
108
|
-
};
|
|
109
|
-
const shapeError = (message) => new ShapeError(`This flow's shape can't run: ${message}`);
|
|
110
|
-
async function runNode(node, def) {
|
|
111
|
-
if (visited.has(node.id))
|
|
112
|
-
throw shapeError(`${node.key ?? node.id} is reached twice.`);
|
|
113
|
-
visited.add(node.id);
|
|
114
|
-
if (signal.aborted) {
|
|
115
|
-
stopped = "cancelled";
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
onEvent({ type: "node-start", nodeId: node.id });
|
|
119
|
-
const began = Date.now();
|
|
120
|
-
const handler = handlers[node.kind];
|
|
121
|
-
const outcome = handler
|
|
122
|
-
? await handler(node, handlerRun)
|
|
123
|
-
: { status: "skipped", note: "Not supported yet" };
|
|
124
|
-
if (outcome === "cancelled" || signal.aborted) {
|
|
125
|
-
stopped = "cancelled";
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
const result = { ...outcome, durationMs: Date.now() - began };
|
|
129
|
-
record(node, result);
|
|
130
|
-
// A node with a dynamic list of branches ends after them: walk() reports it then
|
|
131
|
-
if (dynamicBranches(def))
|
|
132
|
-
return result;
|
|
133
|
-
onEvent({ type: "node-end", nodeId: node.id, result });
|
|
134
|
-
if (result.status === "failed" && node.onFailure === "stop")
|
|
135
|
-
stopped = "failed";
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
/** Whether `target` is on the chain from `from` before `stop` */
|
|
139
|
-
const reachesBefore = (from, target, stop) => {
|
|
140
|
-
const seen = new Set();
|
|
141
|
-
const visit = (id) => {
|
|
142
|
-
if (id === stop || seen.has(id))
|
|
143
|
-
return false;
|
|
144
|
-
if (id === target)
|
|
145
|
-
return true;
|
|
146
|
-
seen.add(id);
|
|
147
|
-
return out(id).some((e) => visit(e.target));
|
|
148
|
-
};
|
|
149
|
-
return visit(from);
|
|
150
|
-
};
|
|
151
|
-
/** Runs the chain from `from` up to `stop` (undefined: through End) */
|
|
152
|
-
async function walk(from, stop) {
|
|
153
|
-
let at = from;
|
|
154
|
-
while (at !== undefined && at !== stop && !stopped) {
|
|
155
|
-
const node = byId.get(at);
|
|
156
|
-
if (!node)
|
|
157
|
-
throw shapeError("an edge leads to a missing node.");
|
|
158
|
-
const def = defOf(node.kind);
|
|
159
|
-
if (!def)
|
|
160
|
-
throw shapeError(`${node.key ?? node.id} has an unknown kind (${node.kind}).`);
|
|
161
|
-
const result = await runNode(node, def);
|
|
162
|
-
if (!result || stopped)
|
|
163
|
-
return;
|
|
164
|
-
if (def.shape === "end")
|
|
165
|
-
return;
|
|
166
|
-
const edges = out(node.id);
|
|
167
|
-
if (def.shape === "branches") {
|
|
168
|
-
const meet = meetOf(flow, node.id);
|
|
169
|
-
if (!meet)
|
|
170
|
-
throw shapeError(`the branches of ${node.key ?? node.id} never meet.`);
|
|
171
|
-
if (!dynamicBranches(def)) {
|
|
172
|
-
const taken = result.status === "passed"
|
|
173
|
-
? edges.find((e) => e.sourceHandle === result.branch)
|
|
174
|
-
: undefined;
|
|
175
|
-
for (const e of edges)
|
|
176
|
-
if (e !== taken)
|
|
177
|
-
skip(e.target, meet);
|
|
178
|
-
if (taken)
|
|
179
|
-
await walk(taken.target, meet);
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
const opened = Date.now();
|
|
183
|
-
for (const e of inFillOrder(edges)) {
|
|
184
|
-
if (stopped)
|
|
185
|
-
break;
|
|
186
|
-
if (e.target !== meet)
|
|
187
|
-
await walk(e.target, meet);
|
|
188
|
-
}
|
|
189
|
-
if (stopped === "cancelled") {
|
|
190
|
-
// Stopped inside its branches: like any node cut short, it didn't run to the end
|
|
191
|
-
delete results[node.id];
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
// The node fails when anything in its branches failed
|
|
195
|
-
const failed = flow.nodes.some((n) => results[n.id]?.status === "failed" &&
|
|
196
|
-
edges.some((e) => e.target !== meet && reachesBefore(e.target, n.id, meet)));
|
|
197
|
-
const durationMs = (result.durationMs ?? 0) + Date.now() - opened;
|
|
198
|
-
const ended = failed
|
|
199
|
-
? {
|
|
200
|
-
...result,
|
|
201
|
-
durationMs,
|
|
202
|
-
status: "failed",
|
|
203
|
-
error: "A branch failed.",
|
|
204
|
-
}
|
|
205
|
-
: { ...result, durationMs };
|
|
206
|
-
record(node, ended);
|
|
207
|
-
onEvent({ type: "node-end", nodeId: node.id, result: ended });
|
|
208
|
-
if (failed && node.onFailure === "stop")
|
|
209
|
-
stopped = "failed";
|
|
210
|
-
if (stopped)
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
at = meet;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (edges.length !== 1)
|
|
217
|
-
throw shapeError(`${node.key ?? node.id} has ${edges.length} ways out.`);
|
|
218
|
-
at = edges[0].target;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
46
|
+
});
|
|
47
|
+
const walker = new FlowWalker({
|
|
48
|
+
flow,
|
|
49
|
+
signal,
|
|
50
|
+
handlers,
|
|
51
|
+
nodes,
|
|
52
|
+
handlerRunFor,
|
|
53
|
+
context,
|
|
54
|
+
onEvent,
|
|
55
|
+
});
|
|
221
56
|
onEvent({ type: "flow-start", environment: options.environment.name });
|
|
222
57
|
let error;
|
|
223
|
-
const start = flow.nodes.find((n) =>
|
|
58
|
+
const start = flow.nodes.find((n) => definitionOf(nodes, n.kind)?.shape === "start");
|
|
224
59
|
try {
|
|
225
60
|
if (!start)
|
|
226
61
|
throw shapeError("it has no Start.");
|
|
227
|
-
await walk(start.id);
|
|
62
|
+
await walker.walk(start.id);
|
|
228
63
|
}
|
|
229
64
|
catch (e) {
|
|
230
65
|
if (!(e instanceof ShapeError))
|
|
231
66
|
throw e;
|
|
232
67
|
error = e.message;
|
|
233
68
|
}
|
|
69
|
+
const results = walker.results;
|
|
234
70
|
for (const node of flow.nodes)
|
|
235
71
|
if (!results[node.id])
|
|
236
72
|
results[node.id] = { status: "not-run" };
|
|
237
|
-
const status = stopped === "cancelled"
|
|
73
|
+
const status = walker.stopped === "cancelled"
|
|
238
74
|
? "cancelled"
|
|
239
75
|
: error || Object.values(results).some((r) => r.status === "failed")
|
|
240
76
|
? "failed"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type AnyNodeDefinition, type ExpressionContext, type FlowFile } from "../core/index.js";
|
|
2
|
+
import { type Fork } from "./branch-context.js";
|
|
3
|
+
import type { FlowEvent, NodeResult } from "./events.js";
|
|
4
|
+
import type { Handler, HandlerRun } from "./handlers/index.js";
|
|
5
|
+
/** What the walker needs of the run: the flow, who handles each kind, and where results and events go */
|
|
6
|
+
export interface WalkerInput {
|
|
7
|
+
flow: FlowFile;
|
|
8
|
+
signal: AbortSignal;
|
|
9
|
+
handlers: Readonly<Record<string, Handler>>;
|
|
10
|
+
nodes: readonly AnyNodeDefinition[];
|
|
11
|
+
/** What the handlers get for a context and a signal; the walker also writes each result into `context.nodes` */
|
|
12
|
+
handlerRunFor: (context: ExpressionContext, signal: AbortSignal) => HandlerRun;
|
|
13
|
+
context: ExpressionContext;
|
|
14
|
+
onEvent: (event: FlowEvent) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Walks the flow from Start by the shape of each node's kind: a step runs and follows its one way out; a node
|
|
18
|
+
* with a fixed list of branches follows the one its handler names and skips the others; a node with a dynamic
|
|
19
|
+
* list walks every branch at the same time, each with its own copy of the variables (see `BranchWalk`); an end
|
|
20
|
+
* stops. Branches meet again at `meetOf`.
|
|
21
|
+
*/
|
|
22
|
+
export declare class FlowWalker {
|
|
23
|
+
private readonly input;
|
|
24
|
+
/** Why the walk stopped early: a failed node that stops the flow, or an abort */
|
|
25
|
+
stopped: "failed" | "cancelled" | undefined;
|
|
26
|
+
/** The result of every node that ran or was skipped, by node id; shared with the walks of the branches */
|
|
27
|
+
readonly results: Record<string, NodeResult>;
|
|
28
|
+
/** Nodes already entered, so a node reached twice is a shape error; shared with the walks of the branches */
|
|
29
|
+
private readonly visited;
|
|
30
|
+
private readonly byId;
|
|
31
|
+
private readonly handlerRun;
|
|
32
|
+
constructor(input: WalkerInput, shared?: {
|
|
33
|
+
results: Record<string, NodeResult>;
|
|
34
|
+
visited: Set<string>;
|
|
35
|
+
});
|
|
36
|
+
/** The variables and node outputs this walk reads and writes */
|
|
37
|
+
get context(): ExpressionContext;
|
|
38
|
+
private definition;
|
|
39
|
+
private record;
|
|
40
|
+
private out;
|
|
41
|
+
/** Marks every node from `from` up to `stop` as skipped (a branch not taken) */
|
|
42
|
+
private skip;
|
|
43
|
+
private runNode;
|
|
44
|
+
/** Whether `target` is on the chain from `from` before `stop` */
|
|
45
|
+
private reachesBefore;
|
|
46
|
+
/**
|
|
47
|
+
* Walks the branches of a node with a dynamic list at the same time, then reports the node: failed when anything
|
|
48
|
+
* in them failed, or when two branches set a variable differently. A branch that fails and stops the flow cancels
|
|
49
|
+
* the others.
|
|
50
|
+
*/
|
|
51
|
+
private walkBranches;
|
|
52
|
+
/** A walker for one branch: the same flow, results and visited nodes, but its own variables and its own signal */
|
|
53
|
+
forBranch(fork: Fork, signal: AbortSignal): FlowWalker;
|
|
54
|
+
/** Runs the chain from `from` up to `stop` (undefined: through End) */
|
|
55
|
+
walk(from: string, stop?: string): Promise<void>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { inFillOrder, meetOf, } from "../core/index.js";
|
|
2
|
+
import { forkContext, mergeForks } from "./branch-context.js";
|
|
3
|
+
import { definitionOf, dynamicBranches, shapeError } from "./prepare-flow.js";
|
|
4
|
+
/**
|
|
5
|
+
* Walks the flow from Start by the shape of each node's kind: a step runs and follows its one way out; a node
|
|
6
|
+
* with a fixed list of branches follows the one its handler names and skips the others; a node with a dynamic
|
|
7
|
+
* list walks every branch at the same time, each with its own copy of the variables (see `BranchWalk`); an end
|
|
8
|
+
* stops. Branches meet again at `meetOf`.
|
|
9
|
+
*/
|
|
10
|
+
export class FlowWalker {
|
|
11
|
+
constructor(input, shared = { results: {}, visited: new Set() }) {
|
|
12
|
+
this.input = input;
|
|
13
|
+
this.byId = new Map(input.flow.nodes.map((n) => [n.id, n]));
|
|
14
|
+
this.results = shared.results;
|
|
15
|
+
this.visited = shared.visited;
|
|
16
|
+
this.handlerRun = input.handlerRunFor(input.context, input.signal);
|
|
17
|
+
}
|
|
18
|
+
/** The variables and node outputs this walk reads and writes */
|
|
19
|
+
get context() {
|
|
20
|
+
return this.input.context;
|
|
21
|
+
}
|
|
22
|
+
definition(kind) {
|
|
23
|
+
return definitionOf(this.input.nodes, kind);
|
|
24
|
+
}
|
|
25
|
+
record(node, result) {
|
|
26
|
+
this.results[node.id] = result;
|
|
27
|
+
if (node.key)
|
|
28
|
+
this.input.context.nodes[node.key] = {
|
|
29
|
+
...result.outputs,
|
|
30
|
+
status: result.status,
|
|
31
|
+
durationMs: result.durationMs,
|
|
32
|
+
error: result.error,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
out(id) {
|
|
36
|
+
return this.input.flow.edges.filter((e) => e.source === id);
|
|
37
|
+
}
|
|
38
|
+
/** Marks every node from `from` up to `stop` as skipped (a branch not taken) */
|
|
39
|
+
skip(from, stop) {
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
const visit = (id) => {
|
|
42
|
+
if (id === stop || seen.has(id))
|
|
43
|
+
return;
|
|
44
|
+
seen.add(id);
|
|
45
|
+
const node = this.byId.get(id);
|
|
46
|
+
if (node && !this.results[id]) {
|
|
47
|
+
this.record(node, { status: "skipped" });
|
|
48
|
+
this.input.onEvent({
|
|
49
|
+
type: "node-end",
|
|
50
|
+
nodeId: id,
|
|
51
|
+
result: { status: "skipped" },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
for (const e of this.out(id))
|
|
55
|
+
visit(e.target);
|
|
56
|
+
};
|
|
57
|
+
visit(from);
|
|
58
|
+
}
|
|
59
|
+
async runNode(node, def) {
|
|
60
|
+
const { signal, handlers, onEvent } = this.input;
|
|
61
|
+
const { handlerRun } = this;
|
|
62
|
+
if (this.visited.has(node.id))
|
|
63
|
+
throw shapeError(`${node.key ?? node.id} is reached twice.`);
|
|
64
|
+
this.visited.add(node.id);
|
|
65
|
+
if (signal.aborted) {
|
|
66
|
+
this.stopped = "cancelled";
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
onEvent({ type: "node-start", nodeId: node.id });
|
|
70
|
+
const began = Date.now();
|
|
71
|
+
const handler = handlers[node.kind];
|
|
72
|
+
const outcome = handler
|
|
73
|
+
? await handler(node, handlerRun)
|
|
74
|
+
: { status: "skipped", note: "Not supported yet" };
|
|
75
|
+
if (outcome === "cancelled" || signal.aborted) {
|
|
76
|
+
this.stopped = "cancelled";
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const result = { ...outcome, durationMs: Date.now() - began };
|
|
80
|
+
this.record(node, result);
|
|
81
|
+
// A node with a dynamic list of branches ends after them: walk() reports it then
|
|
82
|
+
if (dynamicBranches(def))
|
|
83
|
+
return result;
|
|
84
|
+
onEvent({ type: "node-end", nodeId: node.id, result });
|
|
85
|
+
if (result.status === "failed" && node.onFailure === "stop")
|
|
86
|
+
this.stopped = "failed";
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/** Whether `target` is on the chain from `from` before `stop` */
|
|
90
|
+
reachesBefore(from, target, stop) {
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
const visit = (id) => {
|
|
93
|
+
if (id === stop || seen.has(id))
|
|
94
|
+
return false;
|
|
95
|
+
if (id === target)
|
|
96
|
+
return true;
|
|
97
|
+
seen.add(id);
|
|
98
|
+
return this.out(id).some((e) => visit(e.target));
|
|
99
|
+
};
|
|
100
|
+
return visit(from);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Walks the branches of a node with a dynamic list at the same time, then reports the node: failed when anything
|
|
104
|
+
* in them failed, or when two branches set a variable differently. A branch that fails and stops the flow cancels
|
|
105
|
+
* the others.
|
|
106
|
+
*/
|
|
107
|
+
async walkBranches(node, result, meet) {
|
|
108
|
+
const { flow, onEvent } = this.input;
|
|
109
|
+
const edges = this.out(node.id);
|
|
110
|
+
const opened = Date.now();
|
|
111
|
+
const cancel = new AbortController();
|
|
112
|
+
const onAbort = () => cancel.abort();
|
|
113
|
+
if (this.input.signal.aborted)
|
|
114
|
+
onAbort();
|
|
115
|
+
else
|
|
116
|
+
this.input.signal.addEventListener("abort", onAbort, { once: true });
|
|
117
|
+
const walks = inFillOrder(edges)
|
|
118
|
+
.filter((e) => e.target !== meet)
|
|
119
|
+
.map((e) => new BranchWalk(this, e.target, meet, cancel));
|
|
120
|
+
const settled = await Promise.allSettled(walks.map((w) => w.done));
|
|
121
|
+
this.input.signal.removeEventListener("abort", onAbort);
|
|
122
|
+
const rejected = settled.find((r) => r.status === "rejected");
|
|
123
|
+
if (rejected)
|
|
124
|
+
throw rejected.reason;
|
|
125
|
+
if (walks.some((w) => w.stopped === "failed"))
|
|
126
|
+
this.stopped = "failed";
|
|
127
|
+
if (this.input.signal.aborted)
|
|
128
|
+
this.stopped = "cancelled";
|
|
129
|
+
if (this.stopped === "cancelled") {
|
|
130
|
+
// Stopped inside its branches: like any node cut short, it didn't run to the end
|
|
131
|
+
delete this.results[node.id];
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const { clash } = mergeForks(this.input.context, walks.map((w) => w.fork));
|
|
135
|
+
// The node fails when anything in its branches failed
|
|
136
|
+
const failed = flow.nodes.some((n) => this.results[n.id]?.status === "failed" &&
|
|
137
|
+
edges.some((e) => e.target !== meet && this.reachesBefore(e.target, n.id, meet)));
|
|
138
|
+
const durationMs = (result.durationMs ?? 0) + Date.now() - opened;
|
|
139
|
+
const ended = clash
|
|
140
|
+
? {
|
|
141
|
+
...result,
|
|
142
|
+
durationMs,
|
|
143
|
+
status: "failed",
|
|
144
|
+
error: `branches set ${clash} differently`,
|
|
145
|
+
}
|
|
146
|
+
: failed
|
|
147
|
+
? {
|
|
148
|
+
...result,
|
|
149
|
+
durationMs,
|
|
150
|
+
status: "failed",
|
|
151
|
+
error: "A branch failed.",
|
|
152
|
+
}
|
|
153
|
+
: { ...result, durationMs };
|
|
154
|
+
this.record(node, ended);
|
|
155
|
+
onEvent({ type: "node-end", nodeId: node.id, result: ended });
|
|
156
|
+
if (ended.status === "failed" && node.onFailure === "stop")
|
|
157
|
+
this.stopped = "failed";
|
|
158
|
+
}
|
|
159
|
+
/** A walker for one branch: the same flow, results and visited nodes, but its own variables and its own signal */
|
|
160
|
+
forBranch(fork, signal) {
|
|
161
|
+
return new FlowWalker({ ...this.input, context: fork.context, signal }, { results: this.results, visited: this.visited });
|
|
162
|
+
}
|
|
163
|
+
/** Runs the chain from `from` up to `stop` (undefined: through End) */
|
|
164
|
+
async walk(from, stop) {
|
|
165
|
+
const { flow } = this.input;
|
|
166
|
+
let at = from;
|
|
167
|
+
while (at !== undefined && at !== stop && !this.stopped) {
|
|
168
|
+
const node = this.byId.get(at);
|
|
169
|
+
if (!node)
|
|
170
|
+
throw shapeError("an edge leads to a missing node.");
|
|
171
|
+
const def = this.definition(node.kind);
|
|
172
|
+
if (!def)
|
|
173
|
+
throw shapeError(`${node.key ?? node.id} has an unknown kind (${node.kind}).`);
|
|
174
|
+
const result = await this.runNode(node, def);
|
|
175
|
+
if (!result || this.stopped)
|
|
176
|
+
return;
|
|
177
|
+
if (def.shape === "end")
|
|
178
|
+
return;
|
|
179
|
+
const edges = this.out(node.id);
|
|
180
|
+
if (def.shape === "branches") {
|
|
181
|
+
const meet = meetOf(flow, node.id);
|
|
182
|
+
if (!meet)
|
|
183
|
+
throw shapeError(`the branches of ${node.key ?? node.id} never meet.`);
|
|
184
|
+
if (!dynamicBranches(def)) {
|
|
185
|
+
const taken = result.status === "passed"
|
|
186
|
+
? edges.find((e) => e.sourceHandle === result.branch)
|
|
187
|
+
: undefined;
|
|
188
|
+
for (const e of edges)
|
|
189
|
+
if (e !== taken)
|
|
190
|
+
this.skip(e.target, meet);
|
|
191
|
+
if (taken)
|
|
192
|
+
await this.walk(taken.target, meet);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
await this.walkBranches(node, result, meet);
|
|
196
|
+
if (this.stopped)
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
at = meet;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (edges.length !== 1)
|
|
203
|
+
throw shapeError(`${node.key ?? node.id} has ${edges.length} ways out.`);
|
|
204
|
+
at = edges[0].target;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** One branch of a Parallel node, walking from its first node up to where the branches meet */
|
|
209
|
+
class BranchWalk {
|
|
210
|
+
constructor(parent, from, meet, cancel) {
|
|
211
|
+
this.fork = forkContext(parent.context);
|
|
212
|
+
this.walker = parent.forBranch(this.fork, cancel.signal);
|
|
213
|
+
this.done = this.walker.walk(from, meet).then(() => {
|
|
214
|
+
// A failure that stops the flow cancels the branches still running
|
|
215
|
+
if (this.walker.stopped === "failed")
|
|
216
|
+
cancel.abort();
|
|
217
|
+
}, (error) => {
|
|
218
|
+
cancel.abort();
|
|
219
|
+
throw error;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
get stopped() {
|
|
223
|
+
return this.walker.stopped;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnyNodeDefinition, FlowFile, FlowProblem, StepInfo } from "../core/index.js";
|
|
2
|
-
import type { Handler } from "../run/handlers/
|
|
2
|
+
import type { Handler } from "../run/handlers/types.js";
|
|
3
3
|
import { type StepDefinition } from "./define.js";
|
|
4
4
|
import type { SamuraiTestConfig } from "../../types/config.js";
|
|
5
5
|
/** The built-in steps, in library order */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Handler } from "../run/handlers/types.js";
|
|
2
2
|
import { type StepDefinition } from "./define.js";
|
|
3
3
|
/** The one handler every step runs through */
|
|
4
4
|
export declare function stepHandler(step: StepDefinition<any>): Handler;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SamuraiTestConfig } from "../../types/config.js";
|
|
2
2
|
import type { AnyNodeDefinition, StepInfo } from "../core/index.js";
|
|
3
|
-
import type { Handler } from "../run/handlers/
|
|
3
|
+
import type { Handler } from "../run/handlers/types.js";
|
|
4
4
|
import { type StepDefinition } from "./define.js";
|
|
5
5
|
type AnyStep = StepDefinition<any>;
|
|
6
6
|
/** The built-in steps, in library order */
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { main } from "./cli/main.js";
|
|
2
|
-
// Development entry (`
|
|
2
|
+
// Development entry (`npm run dev`, which runs `tsx src/index.ts`): the same as `samurai run`, straight from the sources
|
|
3
3
|
const interrupt = new AbortController();
|
|
4
4
|
process.on("SIGINT", () => {
|
|
5
5
|
if (interrupt.signal.aborted)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ElementHandle } from "../script/element-handle.js";
|
|
2
|
+
import { type CheckName, type ElementState, type ProbeOptions, type WaitForState } from "./element-state.js";
|
|
3
|
+
import { type HelperInstaller } from "./helper-realm.js";
|
|
4
|
+
export interface ActionOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Time (ms) to wait for the element to become actionable. Defaults to config `expect.timeout`, then 5000.
|
|
7
|
+
* 0 checks once without waiting; the stability check is skipped because it needs two probes
|
|
8
|
+
*/
|
|
9
|
+
timeout?: number;
|
|
10
|
+
/** Skip every actionability check except "attached" */
|
|
11
|
+
force?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** What the actionability waits need of a locator: finding its element, calling into the page, naming itself */
|
|
14
|
+
export interface ActionabilityContext {
|
|
15
|
+
resolveOne(): Promise<ElementHandle | null>;
|
|
16
|
+
call<T>(fn: string, args: unknown[], sandbox?: string): Promise<T>;
|
|
17
|
+
/** The locator's description for error messages */
|
|
18
|
+
readonly selector: string;
|
|
19
|
+
/** The page's helper realm; without it the probe source is sent on every poll */
|
|
20
|
+
readonly helpers: HelperInstaller | undefined;
|
|
21
|
+
}
|
|
22
|
+
/** Reads the element's actionability state: one search, one probe */
|
|
23
|
+
export declare function probeState(ctx: ActionabilityContext, options: ProbeOptions): Promise<ElementState>;
|
|
24
|
+
/**
|
|
25
|
+
* Probes until every required check passes and returns that state.
|
|
26
|
+
* Throws ActionTimeoutError describing the last probe on timeout.
|
|
27
|
+
*/
|
|
28
|
+
export declare function waitForActionable(ctx: ActionabilityContext, action: string, checks: readonly CheckName[], probe: ProbeOptions, options: ActionOptions | undefined): Promise<ElementState>;
|
|
29
|
+
/** Waits until the element reaches `state` (default "visible"); does not scroll */
|
|
30
|
+
export declare function waitForState(ctx: ActionabilityContext, { state, timeout }: {
|
|
31
|
+
state?: WaitForState;
|
|
32
|
+
timeout?: number;
|
|
33
|
+
}): Promise<void>;
|