@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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BiDiError } from "../transport/bidi-error.js";
|
|
2
1
|
import { fromRemoteValue, toLocalValue } from "./serialize.js";
|
|
3
2
|
/** How much of a function's source `ScriptError` keeps */
|
|
4
3
|
const SOURCE_PREVIEW = 200;
|
|
@@ -37,10 +36,8 @@ export function toFunctionDeclaration(fn) {
|
|
|
37
36
|
/** Calls `fn` in the page with serialized arguments and returns its deserialized result */
|
|
38
37
|
export async function callFunction(connector, context, fn, args = [], options = {}) {
|
|
39
38
|
const functionDeclaration = toFunctionDeclaration(fn);
|
|
40
|
-
const ownership = options.ownership ?? "none";
|
|
41
39
|
// Serialize first: a bad argument must fail before anything is sent
|
|
42
40
|
const serialized = args.map(toLocalValue);
|
|
43
|
-
const thisValue = "thisArg" in options ? toLocalValue(options.thisArg) : undefined;
|
|
44
41
|
const result = await connector.send("script.callFunction", {
|
|
45
42
|
functionDeclaration,
|
|
46
43
|
awaitPromise: options.awaitPromise ?? true,
|
|
@@ -48,24 +45,10 @@ export async function callFunction(connector, context, fn, args = [], options =
|
|
|
48
45
|
? { context }
|
|
49
46
|
: { context, sandbox: options.sandbox },
|
|
50
47
|
...(serialized.length > 0 && { arguments: serialized }),
|
|
51
|
-
resultOwnership:
|
|
52
|
-
...(thisValue !== undefined && { this: thisValue }),
|
|
48
|
+
resultOwnership: "none",
|
|
53
49
|
});
|
|
54
50
|
if (result.type === "exception") {
|
|
55
51
|
throw new ScriptError(result.exceptionDetails.text, functionDeclaration, result.exceptionDetails.stackTrace);
|
|
56
52
|
}
|
|
57
|
-
return fromRemoteValue(result.result
|
|
58
|
-
}
|
|
59
|
-
/** Releases handles obtained with `ownership: "root"`; a handle that is already gone is fine */
|
|
60
|
-
export async function disown(connector, context, handles) {
|
|
61
|
-
if (handles.length === 0)
|
|
62
|
-
return;
|
|
63
|
-
try {
|
|
64
|
-
await connector.send("script.disown", { handles, target: { context } });
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
if (err instanceof BiDiError && err.code === "no such handle")
|
|
68
|
-
return;
|
|
69
|
-
throw err;
|
|
70
|
-
}
|
|
53
|
+
return fromRemoteValue(result.result);
|
|
71
54
|
}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import type { ArgumentValue, RemoteValue } from "../types/bidi-modules/script.js";
|
|
2
2
|
/** A value passed to a page function */
|
|
3
3
|
export type Arg = unknown;
|
|
4
|
-
/** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by type
|
|
4
|
+
/** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by its type */
|
|
5
5
|
export declare class RemoteObject {
|
|
6
6
|
readonly type: string;
|
|
7
|
-
|
|
8
|
-
/** The deserialized value, when the browser sent one and handles were kept */
|
|
9
|
-
readonly value?: unknown | undefined;
|
|
10
|
-
constructor(type: string, handle?: string | undefined,
|
|
11
|
-
/** The deserialized value, when the browser sent one and handles were kept */
|
|
12
|
-
value?: unknown | undefined);
|
|
7
|
+
constructor(type: string);
|
|
13
8
|
}
|
|
14
9
|
/**
|
|
15
10
|
* A channel the page function can call as `send(value)`; the browser delivers each value as a
|
|
@@ -21,9 +16,5 @@ export declare class ChannelArg {
|
|
|
21
16
|
}
|
|
22
17
|
/** Serializes `value` for `script.callFunction` arguments; throws TypeError for what cannot cross the wire */
|
|
23
18
|
export declare function toLocalValue(value: Arg): ArgumentValue;
|
|
24
|
-
export interface DeserializeOptions {
|
|
25
|
-
/** Return objects that carry a handle as `RemoteObject`s (with `.value`), so the caller can disown them later */
|
|
26
|
-
keepHandles?: boolean;
|
|
27
|
-
}
|
|
28
19
|
/** Converts a remote value to plain JS; what cannot be copied becomes a `RemoteObject` */
|
|
29
|
-
export declare function fromRemoteValue(value: RemoteValue
|
|
20
|
+
export declare function fromRemoteValue(value: RemoteValue): unknown;
|
package/dist/script/serialize.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { ElementHandle } from "./element-handle.js";
|
|
2
2
|
/** Deepest nesting `toLocalValue` accepts; a second guard next to cycle detection */
|
|
3
3
|
const MAX_DEPTH = 100;
|
|
4
|
-
/** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by type
|
|
4
|
+
/** A remote value that cannot be copied to plain JS (function, window, promise, …), kept by its type */
|
|
5
5
|
export class RemoteObject {
|
|
6
|
-
constructor(type
|
|
7
|
-
/** The deserialized value, when the browser sent one and handles were kept */
|
|
8
|
-
value) {
|
|
6
|
+
constructor(type) {
|
|
9
7
|
this.type = type;
|
|
10
|
-
this.handle = handle;
|
|
11
|
-
this.value = value;
|
|
12
8
|
}
|
|
13
9
|
}
|
|
14
10
|
/**
|
|
@@ -55,11 +51,8 @@ function serialize(value, ancestors, depth) {
|
|
|
55
51
|
},
|
|
56
52
|
};
|
|
57
53
|
}
|
|
58
|
-
if (value instanceof ElementHandle)
|
|
59
|
-
return value.
|
|
60
|
-
? { sharedId: value.sharedId }
|
|
61
|
-
: { sharedId: value.sharedId, handle: value.handle };
|
|
62
|
-
}
|
|
54
|
+
if (value instanceof ElementHandle)
|
|
55
|
+
return { sharedId: value.sharedId };
|
|
63
56
|
if (value instanceof Date)
|
|
64
57
|
return { type: "date", value: value.toISOString() };
|
|
65
58
|
if (value instanceof RegExp) {
|
|
@@ -112,10 +105,10 @@ function numberValue(value) {
|
|
|
112
105
|
return value;
|
|
113
106
|
}
|
|
114
107
|
/** Converts a remote value to plain JS; what cannot be copied becomes a `RemoteObject` */
|
|
115
|
-
export function fromRemoteValue(value
|
|
116
|
-
return deserialize(value, new Map()
|
|
108
|
+
export function fromRemoteValue(value) {
|
|
109
|
+
return deserialize(value, new Map());
|
|
117
110
|
}
|
|
118
|
-
function deserialize(value, known
|
|
111
|
+
function deserialize(value, known) {
|
|
119
112
|
const loose = value;
|
|
120
113
|
switch (value.type) {
|
|
121
114
|
case "undefined":
|
|
@@ -136,60 +129,58 @@ function deserialize(value, known, options) {
|
|
|
136
129
|
return new RegExp(pattern, flags);
|
|
137
130
|
}
|
|
138
131
|
case "node":
|
|
139
|
-
return new ElementHandle(loose.sharedId ?? ""
|
|
140
|
-
}
|
|
141
|
-
const nested = (item) => deserialize(item, known, options);
|
|
142
|
-
const remember = (result) => {
|
|
143
|
-
if (loose.internalId !== undefined)
|
|
144
|
-
known.set(loose.internalId, result);
|
|
145
|
-
return result;
|
|
146
|
-
};
|
|
147
|
-
const backReference = () => loose.internalId !== undefined && known.has(loose.internalId)
|
|
148
|
-
? { found: true, value: known.get(loose.internalId) }
|
|
149
|
-
: undefined;
|
|
150
|
-
let result;
|
|
151
|
-
switch (value.type) {
|
|
132
|
+
return new ElementHandle(loose.sharedId ?? "");
|
|
152
133
|
case "array":
|
|
153
134
|
case "set":
|
|
154
135
|
case "object":
|
|
136
|
+
case "map":
|
|
137
|
+
// A collection cut off by the serialization depth has no copy to make
|
|
138
|
+
return (deserializeCollection(value, known) ?? new RemoteObject(value.type));
|
|
139
|
+
}
|
|
140
|
+
return new RemoteObject(value.type);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* An array, set, map or object. A back reference (a value seen before, by `internalId`) gives the copy made
|
|
144
|
+
* earlier; `undefined` means the browser cut the collection off.
|
|
145
|
+
*/
|
|
146
|
+
function deserializeCollection(value, known) {
|
|
147
|
+
const { value: items, internalId } = value;
|
|
148
|
+
if (items === undefined)
|
|
149
|
+
return internalId !== undefined && known.has(internalId)
|
|
150
|
+
? known.get(internalId)
|
|
151
|
+
: undefined;
|
|
152
|
+
// Registered before the items are read, so an item that points back at its container finds it
|
|
153
|
+
const remember = (copy) => {
|
|
154
|
+
if (internalId !== undefined)
|
|
155
|
+
known.set(internalId, copy);
|
|
156
|
+
return copy;
|
|
157
|
+
};
|
|
158
|
+
const nested = (item) => deserialize(item, known);
|
|
159
|
+
const key = (raw) => typeof raw === "string" ? raw : nested(raw);
|
|
160
|
+
switch (value.type) {
|
|
161
|
+
case "array": {
|
|
162
|
+
const list = remember([]);
|
|
163
|
+
items.forEach((item) => list.push(nested(item)));
|
|
164
|
+
return list;
|
|
165
|
+
}
|
|
166
|
+
case "set": {
|
|
167
|
+
const set = remember(new Set());
|
|
168
|
+
items.forEach((item) => set.add(nested(item)));
|
|
169
|
+
return set;
|
|
170
|
+
}
|
|
155
171
|
case "map": {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
else if (value.type === "set") {
|
|
167
|
-
const set = remember(new Set());
|
|
168
|
-
loose.value.forEach((item) => set.add(nested(item)));
|
|
169
|
-
result = set;
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
const entries = loose.value;
|
|
173
|
-
const key = (raw) => typeof raw === "string" ? raw : nested(raw);
|
|
174
|
-
if (value.type === "map") {
|
|
175
|
-
const map = remember(new Map());
|
|
176
|
-
entries.forEach(([k, item]) => map.set(key(k), nested(item)));
|
|
177
|
-
result = map;
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
const object = remember({});
|
|
181
|
-
entries.forEach(([k, item]) => {
|
|
182
|
-
object[String(key(k))] = nested(item);
|
|
183
|
-
});
|
|
184
|
-
result = object;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return options.keepHandles && loose.handle !== undefined
|
|
188
|
-
? new RemoteObject(value.type, loose.handle, result)
|
|
189
|
-
: result;
|
|
172
|
+
const map = remember(new Map());
|
|
173
|
+
items.forEach(([k, item]) => map.set(key(k), nested(item)));
|
|
174
|
+
return map;
|
|
175
|
+
}
|
|
176
|
+
default: {
|
|
177
|
+
const object = remember({});
|
|
178
|
+
items.forEach(([k, item]) => {
|
|
179
|
+
object[String(key(k))] = nested(item);
|
|
180
|
+
});
|
|
181
|
+
return object;
|
|
190
182
|
}
|
|
191
183
|
}
|
|
192
|
-
return new RemoteObject(value.type, loose.handle);
|
|
193
184
|
}
|
|
194
185
|
function specialNumber(value) {
|
|
195
186
|
switch (value) {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { readObject, stringOf } from "./literals.js";
|
|
3
|
+
function readCall(method, args) {
|
|
4
|
+
const first = stringOf(args[0]);
|
|
5
|
+
if (first === undefined)
|
|
6
|
+
return undefined;
|
|
7
|
+
switch (method) {
|
|
8
|
+
case "locator":
|
|
9
|
+
return args.length === 1 ? { method, xpath: first } : undefined;
|
|
10
|
+
case "getByCss":
|
|
11
|
+
return args.length === 1 ? { method, css: first } : undefined;
|
|
12
|
+
case "getByTestId":
|
|
13
|
+
return args.length === 1 ? { method, testId: first } : undefined;
|
|
14
|
+
case "getByText":
|
|
15
|
+
case "getByLabel": {
|
|
16
|
+
if (args.length > 2)
|
|
17
|
+
return undefined;
|
|
18
|
+
const options = args.length === 2 ? readObject(args[1], ["match", "ignoreCase"]) : {};
|
|
19
|
+
if (!options)
|
|
20
|
+
return undefined;
|
|
21
|
+
const { match, ignoreCase } = options;
|
|
22
|
+
if ((match !== undefined && match !== "full" && match !== "partial") ||
|
|
23
|
+
(ignoreCase !== undefined && typeof ignoreCase !== "boolean")) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
method,
|
|
28
|
+
text: first,
|
|
29
|
+
...(match !== undefined && { match: match }),
|
|
30
|
+
...(ignoreCase !== undefined && { ignoreCase: ignoreCase }),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
case "getByRole": {
|
|
34
|
+
if (args.length > 2)
|
|
35
|
+
return undefined;
|
|
36
|
+
const options = args.length === 2 ? readObject(args[1], ["name"]) : {};
|
|
37
|
+
if (!options ||
|
|
38
|
+
(options.name !== undefined && typeof options.name !== "string"))
|
|
39
|
+
return undefined;
|
|
40
|
+
return {
|
|
41
|
+
method,
|
|
42
|
+
role: first,
|
|
43
|
+
...(options.name !== undefined && { name: options.name }),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
default:
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** `page.a(…).b(…)` as calls in order; undefined when it is anything else */
|
|
51
|
+
function readChain(node) {
|
|
52
|
+
const calls = [];
|
|
53
|
+
let current = node;
|
|
54
|
+
while (ts.isCallExpression(current)) {
|
|
55
|
+
if (!ts.isPropertyAccessExpression(current.expression))
|
|
56
|
+
return undefined;
|
|
57
|
+
const call = readCall(current.expression.name.text, current.arguments);
|
|
58
|
+
if (!call)
|
|
59
|
+
return undefined;
|
|
60
|
+
calls.unshift(call);
|
|
61
|
+
current = current.expression.expression;
|
|
62
|
+
}
|
|
63
|
+
return ts.isIdentifier(current) && current.text === "page" && calls.length > 0
|
|
64
|
+
? calls
|
|
65
|
+
: undefined;
|
|
66
|
+
}
|
|
67
|
+
export function readLocator(node) {
|
|
68
|
+
if (ts.isCallExpression(node) &&
|
|
69
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
70
|
+
node.expression.name.text === "withFallbacks") {
|
|
71
|
+
const chain = readChain(node.expression.expression);
|
|
72
|
+
const fallbacks = node.arguments.map(readChain);
|
|
73
|
+
if (!chain ||
|
|
74
|
+
node.arguments.length === 0 ||
|
|
75
|
+
fallbacks.some((fallback) => !fallback))
|
|
76
|
+
return undefined;
|
|
77
|
+
return { chain, fallbacks: fallbacks };
|
|
78
|
+
}
|
|
79
|
+
const chain = readChain(node);
|
|
80
|
+
return chain && { chain, fallbacks: [] };
|
|
81
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { Expectation, TextValue } from "../model.js";
|
|
3
|
+
/** `env.NAME`, `env["NAME"]`, `secrets.NAME`, `secrets["NAME"]` or a string literal */
|
|
4
|
+
export declare function readValue(node: ts.Expression | undefined): TextValue | undefined;
|
|
5
|
+
export declare function readExpectation(matcher: string, args: readonly ts.Expression[]): Expectation | undefined;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { stringOf } from "./literals.js";
|
|
3
|
+
const TEXT_MATCHERS = ["toHaveText", "toContainText", "toHaveValue"];
|
|
4
|
+
/** `env.NAME`, `env["NAME"]`, `secrets.NAME`, `secrets["NAME"]` or a string literal */
|
|
5
|
+
export function readValue(node) {
|
|
6
|
+
if (!node)
|
|
7
|
+
return undefined;
|
|
8
|
+
const literal = stringOf(node);
|
|
9
|
+
if (literal !== undefined)
|
|
10
|
+
return { kind: "literal", value: literal };
|
|
11
|
+
let object;
|
|
12
|
+
let name;
|
|
13
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
14
|
+
object = node.expression;
|
|
15
|
+
name = node.name.text;
|
|
16
|
+
}
|
|
17
|
+
else if (ts.isElementAccessExpression(node)) {
|
|
18
|
+
object = node.expression;
|
|
19
|
+
name = stringOf(node.argumentExpression);
|
|
20
|
+
}
|
|
21
|
+
else
|
|
22
|
+
return undefined;
|
|
23
|
+
if (name === undefined || !ts.isIdentifier(object))
|
|
24
|
+
return undefined;
|
|
25
|
+
if (object.text === "env")
|
|
26
|
+
return { kind: "env", name };
|
|
27
|
+
if (object.text === "secrets")
|
|
28
|
+
return { kind: "secret", name };
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
function readTextMatcher(node) {
|
|
32
|
+
if (!node)
|
|
33
|
+
return undefined;
|
|
34
|
+
const literal = stringOf(node);
|
|
35
|
+
if (literal !== undefined)
|
|
36
|
+
return literal;
|
|
37
|
+
if (ts.isRegularExpressionLiteral(node)) {
|
|
38
|
+
const end = node.text.lastIndexOf("/");
|
|
39
|
+
return {
|
|
40
|
+
regex: {
|
|
41
|
+
source: node.text.slice(1, end),
|
|
42
|
+
flags: node.text.slice(end + 1),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
export function readExpectation(matcher, args) {
|
|
49
|
+
if (matcher === "toBeVisible")
|
|
50
|
+
return args.length === 0 ? { matcher } : undefined;
|
|
51
|
+
if (matcher === "toHaveCount") {
|
|
52
|
+
const [count] = args;
|
|
53
|
+
return args.length === 1 && count && ts.isNumericLiteral(count)
|
|
54
|
+
? { matcher, expected: Number(count.text) }
|
|
55
|
+
: undefined;
|
|
56
|
+
}
|
|
57
|
+
if (matcher === "toHaveAttribute") {
|
|
58
|
+
const name = stringOf(args[0]);
|
|
59
|
+
const expected = readTextMatcher(args[1]);
|
|
60
|
+
return args.length === 2 && name !== undefined && expected !== undefined
|
|
61
|
+
? { matcher, name, expected }
|
|
62
|
+
: undefined;
|
|
63
|
+
}
|
|
64
|
+
if (TEXT_MATCHERS.includes(matcher)) {
|
|
65
|
+
const expected = readTextMatcher(args[0]);
|
|
66
|
+
return args.length === 1 && expected !== undefined
|
|
67
|
+
? { matcher: matcher, expected }
|
|
68
|
+
: undefined;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { parseSize } from "../files.js";
|
|
3
|
+
import { stringOf } from "./literals.js";
|
|
4
|
+
/** `"path"` or `generatedFile({ name, size, type? })`; undefined for anything else */
|
|
5
|
+
function readFileEntry(node) {
|
|
6
|
+
const path = stringOf(node);
|
|
7
|
+
if (path !== undefined)
|
|
8
|
+
return { path };
|
|
9
|
+
if (!ts.isCallExpression(node) ||
|
|
10
|
+
!ts.isIdentifier(node.expression) ||
|
|
11
|
+
node.expression.text !== "generatedFile" ||
|
|
12
|
+
node.arguments.length !== 1 ||
|
|
13
|
+
!ts.isObjectLiteralExpression(node.arguments[0]))
|
|
14
|
+
return undefined;
|
|
15
|
+
let name;
|
|
16
|
+
let type;
|
|
17
|
+
let size;
|
|
18
|
+
let sizeSource;
|
|
19
|
+
for (const property of node.arguments[0].properties) {
|
|
20
|
+
if (!ts.isPropertyAssignment(property))
|
|
21
|
+
return undefined;
|
|
22
|
+
const value = property.initializer;
|
|
23
|
+
switch (property.name.getText()) {
|
|
24
|
+
case "name":
|
|
25
|
+
name = stringOf(value);
|
|
26
|
+
if (name === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
break;
|
|
29
|
+
case "type":
|
|
30
|
+
type = stringOf(value);
|
|
31
|
+
if (type === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
break;
|
|
34
|
+
case "size": {
|
|
35
|
+
const text = stringOf(value);
|
|
36
|
+
size =
|
|
37
|
+
text !== undefined
|
|
38
|
+
? parseSize(text)
|
|
39
|
+
: ts.isNumericLiteral(value)
|
|
40
|
+
? parseSize(Number(value.text))
|
|
41
|
+
: undefined;
|
|
42
|
+
if (size === undefined)
|
|
43
|
+
return undefined;
|
|
44
|
+
sizeSource = value.getText();
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
default:
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (name === undefined || size === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
return {
|
|
54
|
+
generate: {
|
|
55
|
+
name,
|
|
56
|
+
size,
|
|
57
|
+
...(type !== undefined && { type }),
|
|
58
|
+
...(sizeSource !== undefined && { sizeSource }),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** One file entry, or an array of them */
|
|
63
|
+
export function readFiles(node) {
|
|
64
|
+
if (!node)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
67
|
+
const entries = node.elements.map(readFileEntry);
|
|
68
|
+
return entries.every((entry) => entry !== undefined)
|
|
69
|
+
? entries
|
|
70
|
+
: undefined;
|
|
71
|
+
}
|
|
72
|
+
const entry = readFileEntry(node);
|
|
73
|
+
return entry && [entry];
|
|
74
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
export declare function stringOf(node: ts.Node | undefined): string | undefined;
|
|
3
|
+
/** `{ key: literal, … }` with only the allowed keys, or undefined when it has anything else */
|
|
4
|
+
export declare function readObject(node: ts.Node | undefined, allowed: readonly string[]): Record<string, string | boolean> | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
export function stringOf(node) {
|
|
3
|
+
return node &&
|
|
4
|
+
(ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
5
|
+
? node.text
|
|
6
|
+
: undefined;
|
|
7
|
+
}
|
|
8
|
+
/** `{ key: literal, … }` with only the allowed keys, or undefined when it has anything else */
|
|
9
|
+
export function readObject(node, allowed) {
|
|
10
|
+
if (!node || !ts.isObjectLiteralExpression(node))
|
|
11
|
+
return undefined;
|
|
12
|
+
const result = {};
|
|
13
|
+
for (const property of node.properties) {
|
|
14
|
+
if (!ts.isPropertyAssignment(property) ||
|
|
15
|
+
!allowed.includes(property.name.getText()))
|
|
16
|
+
return undefined;
|
|
17
|
+
const value = stringOf(property.initializer);
|
|
18
|
+
if (value !== undefined)
|
|
19
|
+
result[property.name.getText()] = value;
|
|
20
|
+
else if (property.initializer.kind === ts.SyntaxKind.TrueKeyword)
|
|
21
|
+
result[property.name.getText()] = true;
|
|
22
|
+
else if (property.initializer.kind === ts.SyntaxKind.FalseKeyword)
|
|
23
|
+
result[property.name.getText()] = false;
|
|
24
|
+
else
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { readLocator } from "./call.js";
|
|
3
|
+
import { readExpectation, readValue } from "./expectation.js";
|
|
4
|
+
import { readFiles } from "./file-entry.js";
|
|
5
|
+
import { readObject, stringOf } from "./literals.js";
|
|
6
|
+
/** The step a statement stands for, or undefined when it is outside the recorder's set */
|
|
7
|
+
export function readStep(statement) {
|
|
8
|
+
if (!ts.isExpressionStatement(statement) ||
|
|
9
|
+
!ts.isAwaitExpression(statement.expression))
|
|
10
|
+
return undefined;
|
|
11
|
+
const call = statement.expression.expression;
|
|
12
|
+
if (!ts.isCallExpression(call) ||
|
|
13
|
+
!ts.isPropertyAccessExpression(call.expression))
|
|
14
|
+
return undefined;
|
|
15
|
+
const { expression: target, name } = call.expression;
|
|
16
|
+
const args = call.arguments;
|
|
17
|
+
if (ts.isIdentifier(target) && target.text === "page") {
|
|
18
|
+
if (name.text === "goto" && args.length === 1) {
|
|
19
|
+
const url = stringOf(args[0]);
|
|
20
|
+
return url === undefined ? undefined : { kind: "goto", url };
|
|
21
|
+
}
|
|
22
|
+
if (name.text === "waitForNetworkIdle" && args.length === 0)
|
|
23
|
+
return { kind: "waitForNetworkIdle" };
|
|
24
|
+
if (name.text === "waitForTimeout" &&
|
|
25
|
+
args.length === 1 &&
|
|
26
|
+
ts.isNumericLiteral(args[0])) {
|
|
27
|
+
const ms = Number(args[0].text);
|
|
28
|
+
return Number.isInteger(ms) ? { kind: "waitForTimeout", ms } : undefined;
|
|
29
|
+
}
|
|
30
|
+
if (name.text === "screenshot" && args.length === 1) {
|
|
31
|
+
const options = readObject(args[0], ["name", "fullPage"]);
|
|
32
|
+
if (!options || typeof options.name !== "string")
|
|
33
|
+
return undefined;
|
|
34
|
+
if (options.fullPage !== undefined &&
|
|
35
|
+
typeof options.fullPage !== "boolean")
|
|
36
|
+
return undefined;
|
|
37
|
+
return {
|
|
38
|
+
kind: "screenshot",
|
|
39
|
+
name: options.name,
|
|
40
|
+
fullPage: options.fullPage === true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
// expect(locator).matcher(…) and expect(locator).not.matcher(…)
|
|
46
|
+
const negated = ts.isPropertyAccessExpression(target) && target.name.text === "not";
|
|
47
|
+
const subject = negated
|
|
48
|
+
? target.expression
|
|
49
|
+
: target;
|
|
50
|
+
if (ts.isCallExpression(subject) &&
|
|
51
|
+
ts.isIdentifier(subject.expression) &&
|
|
52
|
+
subject.expression.text === "expect" &&
|
|
53
|
+
subject.arguments.length === 1) {
|
|
54
|
+
const locator = readLocator(subject.arguments[0]);
|
|
55
|
+
const expectation = readExpectation(name.text, args);
|
|
56
|
+
return locator && expectation
|
|
57
|
+
? { kind: "expect", locator, not: negated, expectation }
|
|
58
|
+
: undefined;
|
|
59
|
+
}
|
|
60
|
+
const locator = readLocator(target);
|
|
61
|
+
if (!locator)
|
|
62
|
+
return undefined;
|
|
63
|
+
if (name.text === "click" && args.length === 0)
|
|
64
|
+
return { kind: "click", locator };
|
|
65
|
+
if (name.text === "press" && args.length === 1) {
|
|
66
|
+
const key = stringOf(args[0]);
|
|
67
|
+
return key === undefined ? undefined : { kind: "press", locator, key };
|
|
68
|
+
}
|
|
69
|
+
if (name.text === "fill" && args.length === 1) {
|
|
70
|
+
const value = readValue(args[0]);
|
|
71
|
+
return value ? { kind: "fill", locator, value } : undefined;
|
|
72
|
+
}
|
|
73
|
+
if ((name.text === "setInputFiles" || name.text === "dropFiles") &&
|
|
74
|
+
args.length === 1) {
|
|
75
|
+
const files = readFiles(args[0]);
|
|
76
|
+
return files
|
|
77
|
+
? { kind: name.text, locator, files }
|
|
78
|
+
: undefined;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|