@itmetsam/samurai-framework 0.21.4 → 0.22.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/dist/api.d.ts +8 -1
- package/dist/api.js +1 -0
- package/dist/browser/browser-context.d.ts +18 -6
- package/dist/browser/browser-context.js +25 -7
- package/dist/browser/browser.d.ts +11 -2
- package/dist/browser/browser.js +31 -8
- package/dist/browser/cdp.d.ts +11 -0
- package/dist/browser/cdp.js +28 -0
- package/dist/browser/chrome-launch.d.ts +4 -0
- package/dist/browser/chrome-launch.js +11 -3
- package/dist/browser/context-cookie-store.d.ts +17 -0
- package/dist/browser/context-cookie-store.js +90 -0
- package/dist/browser/index.d.ts +8 -1
- package/dist/browser/index.js +5 -1
- package/dist/browser/keyboard.d.ts +35 -0
- package/dist/browser/keyboard.js +83 -0
- package/dist/browser/launch-firefox.js +1 -1
- package/dist/browser/launch-flags.d.ts +4 -1
- package/dist/browser/launch-flags.js +18 -5
- package/dist/browser/mouse.d.ts +42 -0
- package/dist/browser/mouse.js +66 -0
- package/dist/browser/page-navigation.d.ts +0 -2
- package/dist/browser/page-navigation.js +1 -6
- package/dist/browser/page-settings.d.ts +1 -1
- package/dist/browser/page.d.ts +20 -6
- package/dist/browser/page.js +24 -8
- package/dist/browser/session-capabilities.d.ts +10 -0
- package/dist/browser/session-capabilities.js +11 -0
- package/dist/browser/session.d.ts +2 -1
- package/dist/browser/session.js +3 -3
- package/dist/browser/storage-state.d.ts +4 -0
- package/dist/browser/storage-state.js +17 -0
- package/dist/browser/use-applier.d.ts +38 -0
- package/dist/browser/use-applier.js +121 -0
- package/dist/browser/use-commands.d.ts +36 -0
- package/dist/browser/use-commands.js +120 -0
- package/dist/browser/viewport.d.ts +3 -2
- package/dist/browser/viewport.js +15 -2
- package/dist/cli/args.d.ts +8 -4
- package/dist/cli/args.js +8 -5
- package/dist/cli/console-reporter.js +5 -1
- package/dist/cli/main.js +1 -0
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +4 -1
- package/dist/config/base-url.d.ts +4 -0
- package/dist/config/base-url.js +6 -0
- package/dist/config/config.js +8 -0
- package/dist/config/fully-parallel.d.ts +3 -0
- package/dist/config/fully-parallel.js +6 -0
- package/dist/config/group-use.d.ts +8 -0
- package/dist/config/group-use.js +41 -0
- package/dist/config/resolved-config.d.ts +2 -0
- package/dist/config/resolved-config.js +3 -0
- package/dist/config/storage-state-file.d.ts +3 -0
- package/dist/config/storage-state-file.js +20 -0
- package/dist/config/use-options.d.ts +18 -0
- package/dist/config/use-options.js +184 -0
- package/dist/config/web-server-options.d.ts +7 -0
- package/dist/config/web-server-options.js +111 -0
- package/dist/locator/check-actions.js +1 -1
- package/dist/locator/drag-actions.d.ts +17 -0
- package/dist/locator/drag-actions.js +80 -0
- package/dist/locator/element-actions.d.ts +10 -4
- package/dist/locator/element-actions.js +23 -16
- package/dist/locator/element-state.d.ts +5 -0
- package/dist/locator/element-state.js +2 -1
- package/dist/locator/event-actions.d.ts +10 -0
- package/dist/locator/event-actions.js +81 -0
- package/dist/locator/input-actions.d.ts +2 -11
- package/dist/locator/input-actions.js +3 -56
- package/dist/locator/key-actions.d.ts +11 -0
- package/dist/locator/key-actions.js +52 -0
- package/dist/locator/key-map.d.ts +16 -0
- package/dist/locator/key-map.js +76 -0
- package/dist/locator/locator.d.ts +35 -5
- package/dist/locator/locator.js +70 -16
- package/dist/locator/page-scripts.d.ts +2 -0
- package/dist/locator/page-scripts.js +7 -0
- package/dist/locator/pointer-actions.d.ts +44 -0
- package/dist/locator/pointer-actions.js +117 -0
- package/dist/locator/pointer-element-actions.d.ts +21 -0
- package/dist/locator/pointer-element-actions.js +42 -0
- package/dist/logger/warn-once.d.ts +4 -0
- package/dist/logger/warn-once.js +13 -0
- package/dist/request/api-request-context.d.ts +65 -0
- package/dist/request/api-request-context.js +198 -0
- package/dist/request/api-response.d.ts +34 -0
- package/dist/request/api-response.js +57 -0
- package/dist/request/cookie-jar.d.ts +48 -0
- package/dist/request/cookie-jar.js +164 -0
- package/dist/request/http-transport.d.ts +31 -0
- package/dist/request/http-transport.js +113 -0
- package/dist/request/redirect.d.ts +10 -0
- package/dist/request/redirect.js +21 -0
- package/dist/request/request-body.d.ts +27 -0
- package/dist/request/request-body.js +71 -0
- package/dist/request/request.d.ts +16 -0
- package/dist/request/request.js +20 -0
- package/dist/runner/all-hooks.d.ts +7 -1
- package/dist/runner/all-hooks.js +17 -1
- package/dist/runner/attachments.d.ts +16 -0
- package/dist/runner/attachments.js +60 -0
- package/dist/runner/fixture-args.d.ts +15 -0
- package/dist/runner/fixture-args.js +95 -0
- package/dist/runner/fixture-defs.d.ts +22 -0
- package/dist/runner/fixture-defs.js +104 -0
- package/dist/runner/fixture-scope.d.ts +33 -0
- package/dist/runner/fixture-scope.js +156 -0
- package/dist/runner/fixtures.d.ts +37 -0
- package/dist/runner/fixtures.js +40 -0
- package/dist/runner/global-hooks.d.ts +13 -0
- package/dist/runner/global-hooks.js +66 -0
- package/dist/runner/hooks.d.ts +6 -1
- package/dist/runner/hooks.js +15 -3
- package/dist/runner/options.d.ts +13 -2
- package/dist/runner/output-tail.d.ts +14 -0
- package/dist/runner/output-tail.js +30 -0
- package/dist/runner/pool-schedule.d.ts +22 -0
- package/dist/runner/pool-schedule.js +55 -0
- package/dist/runner/pool-units.d.ts +34 -0
- package/dist/runner/pool-units.js +85 -0
- package/dist/runner/pooled-run.d.ts +7 -5
- package/dist/runner/pooled-run.js +19 -9
- package/dist/runner/registry.d.ts +3 -39
- package/dist/runner/registry.js +99 -21
- package/dist/runner/reporter.d.ts +17 -1
- package/dist/runner/reporter.js +15 -2
- package/dist/runner/request-fixture.d.ts +14 -0
- package/dist/runner/request-fixture.js +25 -0
- package/dist/runner/run-hooks.d.ts +16 -0
- package/dist/runner/run-hooks.js +52 -0
- package/dist/runner/run-test.d.ts +1 -3
- package/dist/runner/run-test.js +98 -35
- package/dist/runner/run.d.ts +1 -0
- package/dist/runner/run.js +1 -0
- package/dist/runner/select.d.ts +15 -4
- package/dist/runner/select.js +38 -12
- package/dist/runner/serial-run.d.ts +23 -0
- package/dist/runner/serial-run.js +88 -0
- package/dist/runner/serial.d.ts +34 -0
- package/dist/runner/serial.js +63 -0
- package/dist/runner/shard.d.ts +22 -0
- package/dist/runner/shard.js +73 -0
- package/dist/runner/steps.d.ts +13 -0
- package/dist/runner/steps.js +67 -0
- package/dist/runner/suite.d.ts +8 -3
- package/dist/runner/suite.js +12 -3
- package/dist/runner/test-annotations.d.ts +16 -0
- package/dist/runner/test-annotations.js +47 -0
- package/dist/runner/test-function.d.ts +115 -0
- package/dist/runner/test-function.js +1 -0
- package/dist/runner/test-info.d.ts +104 -0
- package/dist/runner/test-info.js +170 -0
- package/dist/runner/test-runner.d.ts +1 -0
- package/dist/runner/test-runner.js +64 -19
- package/dist/runner/test-timeout.d.ts +22 -0
- package/dist/runner/test-timeout.js +45 -0
- package/dist/runner/use-options.d.ts +7 -0
- package/dist/runner/use-options.js +25 -0
- package/dist/runner/wait-for-url.d.ts +20 -0
- package/dist/runner/wait-for-url.js +65 -0
- package/dist/runner/web-server.d.ts +16 -0
- package/dist/runner/web-server.js +165 -0
- package/dist/runner/worker-pool.d.ts +13 -5
- package/dist/runner/worker-pool.js +85 -51
- package/dist/runner/worker-protocol.d.ts +8 -0
- package/dist/runner/worker-session.js +5 -2
- package/dist/runner/worker.d.ts +2 -0
- package/dist/runner/worker.js +7 -1
- package/dist/script/element-handle.d.ts +1 -1
- package/dist/script/element-handle.js +1 -1
- package/dist/types/config.d.ts +86 -2
- package/dist/types/extend.d.ts +30 -0
- package/dist/types/test.d.ts +138 -0
- package/dist/types/use.d.ts +85 -0
- package/package.json +1 -1
- package/dist/browser/emulation.d.ts +0 -61
- package/dist/browser/emulation.js +0 -180
|
@@ -1,47 +1,10 @@
|
|
|
1
1
|
/** How long `wait()` pauses when no duration is given (ms) */
|
|
2
2
|
export const DEFAULT_PAUSE_MS = 300;
|
|
3
3
|
// Input source ids: BiDi keeps the pointer position and pressed keys per id, so each kind of input reuses one
|
|
4
|
-
|
|
5
|
-
const
|
|
4
|
+
// (a touch pointer is a source of its own, with its own id)
|
|
5
|
+
export const POINTER_ID = "samurai-pointer";
|
|
6
|
+
export const KEYBOARD_ID = "samurai-keyboard";
|
|
6
7
|
const PAUSE_ID = "samurai-sleep";
|
|
7
|
-
/** WebDriver code points of the named keys `press()` accepts; any other single character is typed as is */
|
|
8
|
-
export const NAMED_KEYS = {
|
|
9
|
-
Backspace: "\uE003",
|
|
10
|
-
Tab: "\uE004",
|
|
11
|
-
Enter: "\uE007",
|
|
12
|
-
Escape: "\uE00C",
|
|
13
|
-
Space: " ",
|
|
14
|
-
ArrowLeft: "\uE012",
|
|
15
|
-
ArrowUp: "\uE013",
|
|
16
|
-
ArrowRight: "\uE014",
|
|
17
|
-
ArrowDown: "\uE015",
|
|
18
|
-
Delete: "\uE017",
|
|
19
|
-
Home: "\uE011",
|
|
20
|
-
End: "\uE010",
|
|
21
|
-
PageUp: "\uE00E",
|
|
22
|
-
PageDown: "\uE00F",
|
|
23
|
-
};
|
|
24
|
-
/** Clicks the centre of `rect` (viewport pixels) with real pointer input */
|
|
25
|
-
export async function clickRect(connector, context, rect) {
|
|
26
|
-
await connector.send("input.performActions", {
|
|
27
|
-
context,
|
|
28
|
-
actions: [
|
|
29
|
-
{
|
|
30
|
-
type: "pointer",
|
|
31
|
-
id: POINTER_ID,
|
|
32
|
-
actions: [
|
|
33
|
-
{
|
|
34
|
-
type: "pointerMove",
|
|
35
|
-
x: rect.x + rect.width / 2,
|
|
36
|
-
y: rect.y + rect.height / 2,
|
|
37
|
-
},
|
|
38
|
-
{ type: "pointerDown", button: 0 },
|
|
39
|
-
{ type: "pointerUp", button: 0 },
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
8
|
/** Pauses the input queue for `duration` ms */
|
|
46
9
|
export async function pause(connector, context, duration) {
|
|
47
10
|
await connector.send("input.performActions", {
|
|
@@ -51,22 +14,6 @@ export async function pause(connector, context, duration) {
|
|
|
51
14
|
],
|
|
52
15
|
});
|
|
53
16
|
}
|
|
54
|
-
/** Presses and releases one key, given as its WebDriver code point or a character */
|
|
55
|
-
export async function pressKey(connector, context, code) {
|
|
56
|
-
await connector.send("input.performActions", {
|
|
57
|
-
context,
|
|
58
|
-
actions: [
|
|
59
|
-
{
|
|
60
|
-
type: "key",
|
|
61
|
-
id: KEYBOARD_ID,
|
|
62
|
-
actions: [
|
|
63
|
-
{ type: "keyDown", value: code },
|
|
64
|
-
{ type: "keyUp", value: code },
|
|
65
|
-
],
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
17
|
/** Types `text` one character at a time (each pressed and released) */
|
|
71
18
|
export async function typeText(connector, context, text) {
|
|
72
19
|
const actions = Array.from(text).flatMap((char) => [
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
import type { KeySourceAction } from "../types/bidi-modules/input.js";
|
|
3
|
+
import { type KeyChord } from "./key-map.js";
|
|
4
|
+
/** Sends `actions` to the page's keyboard; BiDi remembers which keys are down between calls */
|
|
5
|
+
export declare function performKeys(connector: BiDiConnector, context: string, actions: KeySourceAction[]): Promise<void>;
|
|
6
|
+
/** The modifiers down, the key down and up (`delay` ms apart), the modifiers up again in reverse order */
|
|
7
|
+
export declare function chordSteps(chord: KeyChord, delay?: number): KeySourceAction[];
|
|
8
|
+
/** One key press per character (a line break is Enter, a tab is Tab), `delay` ms between characters */
|
|
9
|
+
export declare function textSteps(text: string, delay?: number): KeySourceAction[];
|
|
10
|
+
/** Holds `modifiers` down while `action` runs, and lets go of them afterwards even when it throws */
|
|
11
|
+
export declare function withModifiers<T>(connector: BiDiConnector, context: string, modifiers: readonly string[], action: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { KEYBOARD_ID } from "./input-actions.js";
|
|
2
|
+
import { NAMED_KEYS } from "./key-map.js";
|
|
3
|
+
/** Sends `actions` to the page's keyboard; BiDi remembers which keys are down between calls */
|
|
4
|
+
export async function performKeys(connector, context, actions) {
|
|
5
|
+
if (actions.length === 0)
|
|
6
|
+
return;
|
|
7
|
+
await connector.send("input.performActions", {
|
|
8
|
+
context,
|
|
9
|
+
actions: [{ type: "key", id: KEYBOARD_ID, actions }],
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
const pauseFor = (delay) => delay > 0 ? [{ type: "pause", duration: delay }] : [];
|
|
13
|
+
/** The modifiers down, the key down and up (`delay` ms apart), the modifiers up again in reverse order */
|
|
14
|
+
export function chordSteps(chord, delay = 0) {
|
|
15
|
+
return [
|
|
16
|
+
...chord.modifiers.map((value) => ({ type: "keyDown", value })),
|
|
17
|
+
{ type: "keyDown", value: chord.key },
|
|
18
|
+
...pauseFor(delay),
|
|
19
|
+
{ type: "keyUp", value: chord.key },
|
|
20
|
+
...[...chord.modifiers]
|
|
21
|
+
.reverse()
|
|
22
|
+
.map((value) => ({ type: "keyUp", value })),
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
/** One key press per character (a line break is Enter, a tab is Tab), `delay` ms between characters */
|
|
26
|
+
export function textSteps(text, delay = 0) {
|
|
27
|
+
const chars = Array.from(text);
|
|
28
|
+
return chars.flatMap((char, index) => {
|
|
29
|
+
const value = char === "\n" || char === "\r"
|
|
30
|
+
? NAMED_KEYS.Enter
|
|
31
|
+
: char === "\t"
|
|
32
|
+
? NAMED_KEYS.Tab
|
|
33
|
+
: char;
|
|
34
|
+
return [
|
|
35
|
+
{ type: "keyDown", value },
|
|
36
|
+
{ type: "keyUp", value },
|
|
37
|
+
...(index < chars.length - 1 ? pauseFor(delay) : []),
|
|
38
|
+
];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** Holds `modifiers` down while `action` runs, and lets go of them afterwards even when it throws */
|
|
42
|
+
export async function withModifiers(connector, context, modifiers, action) {
|
|
43
|
+
if (modifiers.length === 0)
|
|
44
|
+
return action();
|
|
45
|
+
await performKeys(connector, context, modifiers.map((value) => ({ type: "keyDown", value })));
|
|
46
|
+
try {
|
|
47
|
+
return await action();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await performKeys(connector, context, [...modifiers].reverse().map((value) => ({ type: "keyUp", value })));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** WebDriver code points of the named keys; any other single character is typed as is */
|
|
2
|
+
export declare const NAMED_KEYS: Readonly<Record<string, string>>;
|
|
3
|
+
/** The modifier names `click`, `hover` and key chords accept; `ControlOrMeta` is Meta on macOS and Control elsewhere */
|
|
4
|
+
export type Modifier = "Alt" | "Control" | "ControlOrMeta" | "Meta" | "Shift";
|
|
5
|
+
/** One key with the modifiers held while it is pressed, as WebDriver code points or characters */
|
|
6
|
+
export interface KeyChord {
|
|
7
|
+
modifiers: string[];
|
|
8
|
+
key: string;
|
|
9
|
+
}
|
|
10
|
+
/** The code points of `modifiers`; `method` names the public method for the error */
|
|
11
|
+
export declare function modifierCodes(modifiers: readonly Modifier[], platform?: string, method?: string): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Reads a key such as `a`, `Enter` or a chord such as `Control+a`, `Shift+Tab`, `ControlOrMeta+Enter`: modifiers
|
|
14
|
+
* joined by `+`, then one key (which may be `+` itself: `Control++`). `method` names the public method for errors.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseKeyChord(spec: string, method: string, platform?: string): KeyChord;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** WebDriver code points of the named keys; any other single character is typed as is */
|
|
2
|
+
export const NAMED_KEYS = {
|
|
3
|
+
Backspace: "",
|
|
4
|
+
Tab: "",
|
|
5
|
+
Enter: "",
|
|
6
|
+
Escape: "",
|
|
7
|
+
Space: " ",
|
|
8
|
+
ArrowLeft: "",
|
|
9
|
+
ArrowUp: "",
|
|
10
|
+
ArrowRight: "",
|
|
11
|
+
ArrowDown: "",
|
|
12
|
+
Delete: "",
|
|
13
|
+
Home: "",
|
|
14
|
+
End: "",
|
|
15
|
+
PageUp: "",
|
|
16
|
+
PageDown: "",
|
|
17
|
+
Insert: "",
|
|
18
|
+
...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [
|
|
19
|
+
`F${i + 1}`,
|
|
20
|
+
String.fromCharCode(0xe031 + i),
|
|
21
|
+
])),
|
|
22
|
+
};
|
|
23
|
+
const MODIFIER_KEYS = {
|
|
24
|
+
Control: "",
|
|
25
|
+
Shift: "",
|
|
26
|
+
Alt: "",
|
|
27
|
+
Meta: "",
|
|
28
|
+
};
|
|
29
|
+
/** The code points of `modifiers`; `method` names the public method for the error */
|
|
30
|
+
export function modifierCodes(modifiers, platform = process.platform, method = "click") {
|
|
31
|
+
return modifiers.map((name) => {
|
|
32
|
+
if (name === "ControlOrMeta") {
|
|
33
|
+
return platform === "darwin"
|
|
34
|
+
? MODIFIER_KEYS.Meta
|
|
35
|
+
: MODIFIER_KEYS.Control;
|
|
36
|
+
}
|
|
37
|
+
const code = MODIFIER_KEYS[name];
|
|
38
|
+
if (code === undefined) {
|
|
39
|
+
throw new Error(`${method}(): unknown modifier "${name}"`);
|
|
40
|
+
}
|
|
41
|
+
return code;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function keyCode(name, method) {
|
|
45
|
+
const code = NAMED_KEYS[name] ??
|
|
46
|
+
MODIFIER_KEYS[name] ??
|
|
47
|
+
(/^Key[A-Za-z]$/.test(name) ? name.slice(3).toLowerCase() : undefined) ??
|
|
48
|
+
(/^Digit\d$/.test(name) ? name.slice(5) : undefined) ??
|
|
49
|
+
(Array.from(name).length === 1 ? name : undefined);
|
|
50
|
+
if (code === undefined) {
|
|
51
|
+
throw new Error(`${method}(): unknown key "${name}". Use a named key such as Enter, Tab or ArrowDown, a single character, or a chord like Control+a`);
|
|
52
|
+
}
|
|
53
|
+
return code;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reads a key such as `a`, `Enter` or a chord such as `Control+a`, `Shift+Tab`, `ControlOrMeta+Enter`: modifiers
|
|
57
|
+
* joined by `+`, then one key (which may be `+` itself: `Control++`). `method` names the public method for errors.
|
|
58
|
+
*/
|
|
59
|
+
export function parseKeyChord(spec, method, platform = process.platform) {
|
|
60
|
+
if (spec === "+")
|
|
61
|
+
return { modifiers: [], key: "+" };
|
|
62
|
+
const parts = spec.split("+");
|
|
63
|
+
// "Control++" splits into a trailing empty pair: the last key is the plus sign
|
|
64
|
+
const key = spec.endsWith("++") ? "+" : parts[parts.length - 1];
|
|
65
|
+
const names = parts.slice(0, spec.endsWith("++") ? -2 : -1);
|
|
66
|
+
const modifiers = names.map((name) => {
|
|
67
|
+
if (!(name in MODIFIER_KEYS) && name !== "ControlOrMeta") {
|
|
68
|
+
throw new Error(`${method}(): "${name}" is not a modifier. A chord is modifiers followed by one key, like Control+a`);
|
|
69
|
+
}
|
|
70
|
+
return name;
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
modifiers: modifierCodes(modifiers, platform, method),
|
|
74
|
+
key: keyCode(key, method),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -6,6 +6,8 @@ import type { ElementScreenshotOptions } from "../browser/screenshot.js";
|
|
|
6
6
|
import type { FileInput } from "../browser/generated-files.js";
|
|
7
7
|
import { type ActionOptions } from "./actionability.js";
|
|
8
8
|
import { type CheckOptions } from "./check-actions.js";
|
|
9
|
+
import { type DragOptions } from "./drag-actions.js";
|
|
10
|
+
import { type ClickOptions, type PointerOptions } from "./pointer-element-actions.js";
|
|
9
11
|
import { type BoundingBox } from "./element-reads.js";
|
|
10
12
|
import type { WaitForState } from "./element-state.js";
|
|
11
13
|
import type { HelperInstaller } from "./helper-realm.js";
|
|
@@ -45,6 +47,8 @@ export default class Locator {
|
|
|
45
47
|
get selector(): string;
|
|
46
48
|
/** Steps that keep the fallbacks: each fallback gets the same step, so "inside A or B" stays meaningful */
|
|
47
49
|
private child;
|
|
50
|
+
/** A locator on the same page and helpers, with the given parts replaced */
|
|
51
|
+
private copy;
|
|
48
52
|
/**
|
|
49
53
|
* A locator that tries this one first and, whenever it matches nothing, each of `fallbacks` in order;
|
|
50
54
|
* the first one with a match wins. For steps with several ways to find the same element (a test id,
|
|
@@ -130,8 +134,30 @@ export default class Locator {
|
|
|
130
134
|
*/
|
|
131
135
|
private resolveOne;
|
|
132
136
|
private waitForActionable;
|
|
133
|
-
/**
|
|
134
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Waits until the element is attached, visible, stable, enabled and not covered, then clicks it with real pointer
|
|
139
|
+
* input: its centre, or `position` (pixels from its top left corner). `button`, `clickCount`, `delay` (ms between press
|
|
140
|
+
* and release) and `modifiers` (keys held down) are as in Playwright
|
|
141
|
+
*/
|
|
142
|
+
click(options?: ClickOptions): Promise<void>;
|
|
143
|
+
/** `click` with `clickCount: 2` */
|
|
144
|
+
dblclick(options?: ClickOptions): Promise<void>;
|
|
145
|
+
/** Waits until the element is attached, visible, stable and not covered, then moves the mouse onto it (`:hover`, `mouseover`, tooltips) */
|
|
146
|
+
hover(options?: PointerOptions): Promise<void>;
|
|
147
|
+
/** Waits like `click`, then touches the element with a touch pointer: `touchstart`, `touchend`, then the mouse events a tap ends in */
|
|
148
|
+
tap(options?: PointerOptions): Promise<void>;
|
|
149
|
+
/** Drags this element onto `target` with real pointer input; `sourcePosition`, `targetPosition` and `steps` shape the gesture. Firefox needs synthetic drop events, see the wiki */
|
|
150
|
+
dragTo(target: Locator, options?: DragOptions): Promise<void>;
|
|
151
|
+
/** Waits until the element is attached, then fires a synthetic event (`click`, `keydown`, ...; the class follows the type) at it, bubbling and composed */
|
|
152
|
+
dispatchEvent(type: string, eventInit?: Record<string, unknown>, options?: ActionOptions): Promise<void>;
|
|
153
|
+
/** Waits until the element is attached, then removes focus from it */
|
|
154
|
+
blur(options?: ActionOptions): Promise<void>;
|
|
155
|
+
/** Empties an input, textarea or editable element, as `fill("")` does */
|
|
156
|
+
clear(options?: ActionOptions): Promise<void>;
|
|
157
|
+
/** Focuses the element and types `text` into it one key press at a time, `delay` ms apart */
|
|
158
|
+
pressSequentially(text: string, options?: ActionOptions & {
|
|
159
|
+
delay?: number;
|
|
160
|
+
}): Promise<void>;
|
|
135
161
|
/**
|
|
136
162
|
* Checks a checkbox, radio or `role=checkbox|radio|switch` element; does nothing when it is already checked.
|
|
137
163
|
* Waits like `click`, but a hidden input counts as reachable through its `<label>`, which is what gets clicked.
|
|
@@ -250,12 +276,16 @@ export default class Locator {
|
|
|
250
276
|
wait(duration?: number): Promise<void>;
|
|
251
277
|
/**
|
|
252
278
|
* Waits until the element is attached, visible and enabled, focuses it and presses `key` with real keyboard input.
|
|
253
|
-
* Named keys are `Enter`, `Escape`, `Tab`, `Backspace`, `Delete`, `Space`, `Home`, `End`, `PageUp`, `PageDown
|
|
254
|
-
* any other key must be a single character.
|
|
279
|
+
* Named keys are `Enter`, `Escape`, `Tab`, `Backspace`, `Delete`, `Space`, `Insert`, `Home`, `End`, `PageUp`, `PageDown`,
|
|
280
|
+
* `F1` to `F12` and the arrows; any other key must be a single character. A chord holds modifiers (`Control`, `Shift`,
|
|
281
|
+
* `Alt`, `Meta`, `ControlOrMeta`) while the last key is pressed.
|
|
255
282
|
* @example
|
|
256
283
|
* await page.getByLabel("New todo").press("Enter");
|
|
284
|
+
* await page.getByLabel("Name").press("Control+a");
|
|
257
285
|
*/
|
|
258
|
-
press(key: string, options?: ActionOptions
|
|
286
|
+
press(key: string, options?: ActionOptions & {
|
|
287
|
+
delay?: number;
|
|
288
|
+
}): Promise<void>;
|
|
259
289
|
/** Waits until the field is editable and not covered, then replaces its value by typing `value`; an empty string clears it */
|
|
260
290
|
fill(value: string, options?: ActionOptions & {
|
|
261
291
|
output?: string;
|
package/dist/locator/locator.js
CHANGED
|
@@ -3,7 +3,10 @@ import { waitForActionable, waitForState, } from "./actionability.js";
|
|
|
3
3
|
import { setChecked } from "./check-actions.js";
|
|
4
4
|
import { ChainResolver } from "./chain-resolver.js";
|
|
5
5
|
import { callInPage, noElementError, requireElement, } from "./element-access.js";
|
|
6
|
-
import {
|
|
6
|
+
import { clearElement, fillElement, focusElement, pressOnElement, pressSequentially, screenshotElement, } from "./element-actions.js";
|
|
7
|
+
import { dragElement } from "./drag-actions.js";
|
|
8
|
+
import { blurElement, dispatchEventOn } from "./event-actions.js";
|
|
9
|
+
import { clickElement, hoverElement, tapElement, } from "./pointer-element-actions.js";
|
|
7
10
|
import { readAccessibleDescription, readAccessibleName, readAllInnerTexts, readAllTextContents, readAttribute, readBoundingBox, readBoundingClientRect, readClassAttribute, readCssValue, readEnabledState, readInnerHTML, readInnerText, readInputValue, readIsChecked, readIsEditable, readIsEnabled, readIsVisible, readTextContent, } from "./element-reads.js";
|
|
8
11
|
import { evaluateOnAll, evaluateOnElement } from "./evaluate.js";
|
|
9
12
|
import { dropFiles, setInputFiles } from "./file-actions.js";
|
|
@@ -65,7 +68,14 @@ export default class Locator {
|
|
|
65
68
|
}
|
|
66
69
|
/** Steps that keep the fallbacks: each fallback gets the same step, so "inside A or B" stays meaningful */
|
|
67
70
|
child(step) {
|
|
68
|
-
return
|
|
71
|
+
return this.copy({
|
|
72
|
+
chain: [...this.chain, step],
|
|
73
|
+
alternatives: this.alternatives.map((alt) => alt.child(step)),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** A locator on the same page and helpers, with the given parts replaced */
|
|
77
|
+
copy(parts = {}) {
|
|
78
|
+
return new Locator(parts.chain ?? this.chain, this.biDiConnector, this.contextId, this.helpers, parts.alternatives ?? this.alternatives, parts.strictness ?? this.strictness);
|
|
69
79
|
}
|
|
70
80
|
/**
|
|
71
81
|
* A locator that tries this one first and, whenever it matches nothing, each of `fallbacks` in order;
|
|
@@ -76,7 +86,7 @@ export default class Locator {
|
|
|
76
86
|
* page.getByTestId("save").withFallbacks(page.getByRole("button", { name: "Save" }), page.getByText("Save"));
|
|
77
87
|
*/
|
|
78
88
|
withFallbacks(...fallbacks) {
|
|
79
|
-
return
|
|
89
|
+
return this.copy({ alternatives: [...this.alternatives, ...fallbacks] });
|
|
80
90
|
}
|
|
81
91
|
/**
|
|
82
92
|
* This locator, strict or not whatever the page and the config say: a strict locator makes actions and reads
|
|
@@ -86,7 +96,7 @@ export default class Locator {
|
|
|
86
96
|
* await page.locator("li").strict().click(); // throws when there is more than one li
|
|
87
97
|
*/
|
|
88
98
|
strict(enabled = true) {
|
|
89
|
-
return
|
|
99
|
+
return this.copy({ strictness: () => Promise.resolve(enabled) });
|
|
90
100
|
}
|
|
91
101
|
/**
|
|
92
102
|
* Which locator found elements the last time this one searched: index 0 is the primary, 1 the first
|
|
@@ -162,13 +172,16 @@ export default class Locator {
|
|
|
162
172
|
if (other.contextId !== this.contextId) {
|
|
163
173
|
throw new TypeError(`${kind}(): the locators belong to different pages`);
|
|
164
174
|
}
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
return this.copy({
|
|
176
|
+
chain: [
|
|
177
|
+
{
|
|
178
|
+
kind,
|
|
179
|
+
left: this.chainFor(`${kind}()`, "an operand"),
|
|
180
|
+
right: other.chainFor(`${kind}()`, "an operand"),
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
alternatives: [],
|
|
184
|
+
});
|
|
172
185
|
}
|
|
173
186
|
/** The chain of a locator used inside another (`has`); a locator with fallbacks cannot be */
|
|
174
187
|
chainFor(method, role) {
|
|
@@ -196,7 +209,10 @@ export default class Locator {
|
|
|
196
209
|
const only = this.chain.length === 1 ? this.chain[0] : undefined;
|
|
197
210
|
return Array.from({ length: count }, (_, index) => only?.kind === "xpath"
|
|
198
211
|
? // A positional xpath keeps working without a second search for the whole chain
|
|
199
|
-
|
|
212
|
+
this.copy({
|
|
213
|
+
chain: `(${describeSelector(only, false)})[${index + 1}]`,
|
|
214
|
+
alternatives: [],
|
|
215
|
+
})
|
|
200
216
|
: this.child({ kind: "nth", index }));
|
|
201
217
|
}
|
|
202
218
|
/**
|
|
@@ -232,7 +248,7 @@ export default class Locator {
|
|
|
232
248
|
return undefined;
|
|
233
249
|
}
|
|
234
250
|
withoutFallbacks() {
|
|
235
|
-
return
|
|
251
|
+
return this.copy({ alternatives: [] });
|
|
236
252
|
}
|
|
237
253
|
/**
|
|
238
254
|
* The element the locator matches first. With a `method` name and strict selectors on, several matches
|
|
@@ -252,10 +268,46 @@ export default class Locator {
|
|
|
252
268
|
waitForActionable(action, checks, probe, options) {
|
|
253
269
|
return waitForActionable(this.actionability, action, checks, probe, options);
|
|
254
270
|
}
|
|
255
|
-
/**
|
|
271
|
+
/**
|
|
272
|
+
* Waits until the element is attached, visible, stable, enabled and not covered, then clicks it with real pointer
|
|
273
|
+
* input: its centre, or `position` (pixels from its top left corner). `button`, `clickCount`, `delay` (ms between press
|
|
274
|
+
* and release) and `modifiers` (keys held down) are as in Playwright
|
|
275
|
+
*/
|
|
256
276
|
async click(options) {
|
|
257
277
|
await clickElement(this.access, options);
|
|
258
278
|
}
|
|
279
|
+
/** `click` with `clickCount: 2` */
|
|
280
|
+
async dblclick(options) {
|
|
281
|
+
await clickElement(this.access, { ...options, clickCount: 2 }, "dblclick");
|
|
282
|
+
}
|
|
283
|
+
/** Waits until the element is attached, visible, stable and not covered, then moves the mouse onto it (`:hover`, `mouseover`, tooltips) */
|
|
284
|
+
async hover(options) {
|
|
285
|
+
await hoverElement(this.access, options);
|
|
286
|
+
}
|
|
287
|
+
/** Waits like `click`, then touches the element with a touch pointer: `touchstart`, `touchend`, then the mouse events a tap ends in */
|
|
288
|
+
async tap(options) {
|
|
289
|
+
await tapElement(this.access, options);
|
|
290
|
+
}
|
|
291
|
+
/** Drags this element onto `target` with real pointer input; `sourcePosition`, `targetPosition` and `steps` shape the gesture. Firefox needs synthetic drop events, see the wiki */
|
|
292
|
+
async dragTo(target, options) {
|
|
293
|
+
await dragElement(this.access, target.access, options);
|
|
294
|
+
}
|
|
295
|
+
/** Waits until the element is attached, then fires a synthetic event (`click`, `keydown`, ...; the class follows the type) at it, bubbling and composed */
|
|
296
|
+
async dispatchEvent(type, eventInit, options) {
|
|
297
|
+
await dispatchEventOn(this.access, type, eventInit, options);
|
|
298
|
+
}
|
|
299
|
+
/** Waits until the element is attached, then removes focus from it */
|
|
300
|
+
async blur(options) {
|
|
301
|
+
await blurElement(this.access, options);
|
|
302
|
+
}
|
|
303
|
+
/** Empties an input, textarea or editable element, as `fill("")` does */
|
|
304
|
+
async clear(options) {
|
|
305
|
+
await clearElement(this.access, options);
|
|
306
|
+
}
|
|
307
|
+
/** Focuses the element and types `text` into it one key press at a time, `delay` ms apart */
|
|
308
|
+
async pressSequentially(text, options) {
|
|
309
|
+
await pressSequentially(this.access, text, options);
|
|
310
|
+
}
|
|
259
311
|
/**
|
|
260
312
|
* Checks a checkbox, radio or `role=checkbox|radio|switch` element; does nothing when it is already checked.
|
|
261
313
|
* Waits like `click`, but a hidden input counts as reachable through its `<label>`, which is what gets clicked.
|
|
@@ -431,10 +483,12 @@ export default class Locator {
|
|
|
431
483
|
}
|
|
432
484
|
/**
|
|
433
485
|
* Waits until the element is attached, visible and enabled, focuses it and presses `key` with real keyboard input.
|
|
434
|
-
* Named keys are `Enter`, `Escape`, `Tab`, `Backspace`, `Delete`, `Space`, `Home`, `End`, `PageUp`, `PageDown
|
|
435
|
-
* any other key must be a single character.
|
|
486
|
+
* Named keys are `Enter`, `Escape`, `Tab`, `Backspace`, `Delete`, `Space`, `Insert`, `Home`, `End`, `PageUp`, `PageDown`,
|
|
487
|
+
* `F1` to `F12` and the arrows; any other key must be a single character. A chord holds modifiers (`Control`, `Shift`,
|
|
488
|
+
* `Alt`, `Meta`, `ControlOrMeta`) while the last key is pressed.
|
|
436
489
|
* @example
|
|
437
490
|
* await page.getByLabel("New todo").press("Enter");
|
|
491
|
+
* await page.getByLabel("Name").press("Control+a");
|
|
438
492
|
*/
|
|
439
493
|
async press(key, options) {
|
|
440
494
|
await pressOnElement(this.access, key, options);
|
|
@@ -11,6 +11,8 @@ export declare const DROP_INPUT = "() => {\n const input = document.createEleme
|
|
|
11
11
|
export declare const DROP_EVENTS = "(el, types) => {\n const input = globalThis[Symbol.for(\"samurai.drop\")];\n delete globalThis[Symbol.for(\"samurai.drop\")];\n const data = new DataTransfer();\n Array.from(input.files).forEach((file, i) => {\n data.items.add(types[i] ? new File([file], file.name, { type: types[i], lastModified: file.lastModified }) : file);\n });\n const box = el.getBoundingClientRect();\n const at = { clientX: box.left + box.width / 2, clientY: box.top + box.height / 2 };\n for (const type of [\"dragenter\", \"dragover\", \"drop\"]) {\n el.dispatchEvent(new DragEvent(type, Object.assign({ bubbles: true, cancelable: true, composed: true, dataTransfer: data }, at)));\n }\n}";
|
|
12
12
|
export declare const SCREENSHOT_CHECKS: readonly CheckName[];
|
|
13
13
|
export declare const CLICK_CHECKS: readonly CheckName[];
|
|
14
|
+
/** `hover`: the pointer only moves, so the element need not be enabled */
|
|
15
|
+
export declare const HOVER_CHECKS: readonly CheckName[];
|
|
14
16
|
/** `selectOption`: a select is not clicked, so it need not be stable or uncovered */
|
|
15
17
|
export declare const SELECT_CHECKS: readonly CheckName[];
|
|
16
18
|
export declare const FILL_CHECKS: readonly CheckName[];
|
|
@@ -39,6 +39,13 @@ export const CLICK_CHECKS = [
|
|
|
39
39
|
"enabled",
|
|
40
40
|
"hit target",
|
|
41
41
|
];
|
|
42
|
+
/** `hover`: the pointer only moves, so the element need not be enabled */
|
|
43
|
+
export const HOVER_CHECKS = [
|
|
44
|
+
"attached",
|
|
45
|
+
"visible",
|
|
46
|
+
"stable",
|
|
47
|
+
"hit target",
|
|
48
|
+
];
|
|
42
49
|
/** `selectOption`: a select is not clicked, so it need not be stable or uncovered */
|
|
43
50
|
export const SELECT_CHECKS = [
|
|
44
51
|
"attached",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
2
|
+
import type { PointerButton, PointerSourceAction } from "../types/bidi-modules/input.js";
|
|
3
|
+
export interface Point {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
type Rect = Point & {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
export type MouseButton = "left" | "middle" | "right";
|
|
12
|
+
/** What a click does beyond a plain left click */
|
|
13
|
+
export interface PointerClickOptions {
|
|
14
|
+
button?: MouseButton;
|
|
15
|
+
/** Presses in a row: 2 is a double click (default 1) */
|
|
16
|
+
clickCount?: number;
|
|
17
|
+
/** Milliseconds between a press and its release (default 0) */
|
|
18
|
+
delay?: number;
|
|
19
|
+
}
|
|
20
|
+
/** The BiDi number of a mouse button; `method` names the public method for the error */
|
|
21
|
+
export declare function buttonCode(button: MouseButton | undefined, method: string): PointerButton;
|
|
22
|
+
/** The viewport point `position` (from the rect's top left corner) names; the centre without one */
|
|
23
|
+
export declare function pointInRect(rect: Rect, position?: Point): Point;
|
|
24
|
+
/** `steps` pointer moves in a straight line from `from` to `to`, the last one on `to` */
|
|
25
|
+
export declare function moveSteps(from: Point, to: Point, steps?: number): PointerSourceAction[];
|
|
26
|
+
/** Sends `actions` to the page's mouse, or to its touch pointer; BiDi remembers the position and the pressed buttons */
|
|
27
|
+
export declare function performPointer(connector: BiDiConnector, context: string, actions: PointerSourceAction[], touch?: boolean): Promise<void>;
|
|
28
|
+
/** Checks a click's `button` and `clickCount` before anything is sent; answers the button's BiDi number */
|
|
29
|
+
export declare function checkClick({ button, clickCount }: PointerClickOptions, method: string): PointerButton;
|
|
30
|
+
/** Press and release `clickCount` times at `point`, `delay` ms between each press and release */
|
|
31
|
+
export declare function clickSteps(point: Point, { button, clickCount, delay }?: PointerClickOptions, method?: string): PointerSourceAction[];
|
|
32
|
+
/** Clicks `point` (viewport pixels) with real pointer input */
|
|
33
|
+
export declare function pointerClick(connector: BiDiConnector, context: string, point: Point, options?: PointerClickOptions, method?: string): Promise<void>;
|
|
34
|
+
/** Clicks the centre of `rect` (viewport pixels) with real pointer input */
|
|
35
|
+
export declare function clickRect(connector: BiDiConnector, context: string, rect: Rect): Promise<void>;
|
|
36
|
+
/** Moves the mouse to `point` (viewport pixels) */
|
|
37
|
+
export declare function pointerMoveTo(connector: BiDiConnector, context: string, point: Point): Promise<void>;
|
|
38
|
+
/** Touches and lifts a finger at `point` (viewport pixels) */
|
|
39
|
+
export declare function pointerTap(connector: BiDiConnector, context: string, point: Point): Promise<void>;
|
|
40
|
+
/** Presses on `from`, moves to `to` in `steps` moves and releases there */
|
|
41
|
+
export declare function pointerDrag(connector: BiDiConnector, context: string, from: Point, to: Point, steps: number): Promise<void>;
|
|
42
|
+
/** Turns the mouse wheel at `point` by `deltaX` and `deltaY` pixels */
|
|
43
|
+
export declare function wheelScroll(connector: BiDiConnector, context: string, point: Point, deltaX: number, deltaY: number): Promise<void>;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { POINTER_ID } from "./input-actions.js";
|
|
2
|
+
const TOUCH_ID = "samurai-touch";
|
|
3
|
+
const WHEEL_ID = "samurai-wheel";
|
|
4
|
+
const BUTTONS = {
|
|
5
|
+
left: 0,
|
|
6
|
+
middle: 1,
|
|
7
|
+
right: 2,
|
|
8
|
+
};
|
|
9
|
+
/** The BiDi number of a mouse button; `method` names the public method for the error */
|
|
10
|
+
export function buttonCode(button = "left", method) {
|
|
11
|
+
const code = BUTTONS[button];
|
|
12
|
+
if (code === undefined) {
|
|
13
|
+
throw new Error(`${method}(): unknown button "${button}". Use left, middle or right`);
|
|
14
|
+
}
|
|
15
|
+
return code;
|
|
16
|
+
}
|
|
17
|
+
/** The viewport point `position` (from the rect's top left corner) names; the centre without one */
|
|
18
|
+
export function pointInRect(rect, position) {
|
|
19
|
+
return position
|
|
20
|
+
? { x: rect.x + position.x, y: rect.y + position.y }
|
|
21
|
+
: { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
|
|
22
|
+
}
|
|
23
|
+
/** `steps` pointer moves in a straight line from `from` to `to`, the last one on `to` */
|
|
24
|
+
export function moveSteps(from, to, steps = 1) {
|
|
25
|
+
const count = Math.max(1, Math.floor(steps));
|
|
26
|
+
return Array.from({ length: count }, (_, index) => ({
|
|
27
|
+
type: "pointerMove",
|
|
28
|
+
x: from.x + ((to.x - from.x) * (index + 1)) / count,
|
|
29
|
+
y: from.y + ((to.y - from.y) * (index + 1)) / count,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
/** Sends `actions` to the page's mouse, or to its touch pointer; BiDi remembers the position and the pressed buttons */
|
|
33
|
+
export async function performPointer(connector, context, actions, touch = false) {
|
|
34
|
+
await connector.send("input.performActions", {
|
|
35
|
+
context,
|
|
36
|
+
actions: [
|
|
37
|
+
{
|
|
38
|
+
type: "pointer",
|
|
39
|
+
id: touch ? TOUCH_ID : POINTER_ID,
|
|
40
|
+
parameters: { pointerType: touch ? "touch" : "mouse" },
|
|
41
|
+
actions,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const pauseFor = (delay) => delay > 0 ? [{ type: "pause", duration: delay }] : [];
|
|
47
|
+
/** Checks a click's `button` and `clickCount` before anything is sent; answers the button's BiDi number */
|
|
48
|
+
export function checkClick({ button, clickCount = 1 }, method) {
|
|
49
|
+
const code = buttonCode(button, method);
|
|
50
|
+
if (!Number.isInteger(clickCount) || clickCount < 1) {
|
|
51
|
+
throw new Error(`${method}(): clickCount must be a whole number, 1 or more`);
|
|
52
|
+
}
|
|
53
|
+
return code;
|
|
54
|
+
}
|
|
55
|
+
/** Press and release `clickCount` times at `point`, `delay` ms between each press and release */
|
|
56
|
+
export function clickSteps(point, { button, clickCount = 1, delay = 0 } = {}, method = "click") {
|
|
57
|
+
const code = checkClick({ button, clickCount }, method);
|
|
58
|
+
return [
|
|
59
|
+
{ type: "pointerMove", x: point.x, y: point.y },
|
|
60
|
+
...Array.from({ length: clickCount }, () => [
|
|
61
|
+
{ type: "pointerDown", button: code },
|
|
62
|
+
...pauseFor(delay),
|
|
63
|
+
{ type: "pointerUp", button: code },
|
|
64
|
+
]).flat(),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
/** Clicks `point` (viewport pixels) with real pointer input */
|
|
68
|
+
export async function pointerClick(connector, context, point, options, method = "click") {
|
|
69
|
+
await performPointer(connector, context, clickSteps(point, options, method));
|
|
70
|
+
}
|
|
71
|
+
/** Clicks the centre of `rect` (viewport pixels) with real pointer input */
|
|
72
|
+
export async function clickRect(connector, context, rect) {
|
|
73
|
+
await pointerClick(connector, context, pointInRect(rect));
|
|
74
|
+
}
|
|
75
|
+
/** Moves the mouse to `point` (viewport pixels) */
|
|
76
|
+
export async function pointerMoveTo(connector, context, point) {
|
|
77
|
+
await performPointer(connector, context, moveSteps(point, point));
|
|
78
|
+
}
|
|
79
|
+
/** Touches and lifts a finger at `point` (viewport pixels) */
|
|
80
|
+
export async function pointerTap(connector, context, point) {
|
|
81
|
+
await performPointer(connector, context, [
|
|
82
|
+
{ type: "pointerMove", x: point.x, y: point.y },
|
|
83
|
+
{ type: "pointerDown", button: 0 },
|
|
84
|
+
{ type: "pointerUp", button: 0 },
|
|
85
|
+
], true);
|
|
86
|
+
}
|
|
87
|
+
/** Presses on `from`, moves to `to` in `steps` moves and releases there */
|
|
88
|
+
export async function pointerDrag(connector, context, from, to, steps) {
|
|
89
|
+
await performPointer(connector, context, [
|
|
90
|
+
{ type: "pointerMove", x: from.x, y: from.y },
|
|
91
|
+
{ type: "pointerDown", button: 0 },
|
|
92
|
+
...moveSteps(from, to, steps),
|
|
93
|
+
{ type: "pointerUp", button: 0 },
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
/** Turns the mouse wheel at `point` by `deltaX` and `deltaY` pixels */
|
|
97
|
+
export async function wheelScroll(connector, context, point, deltaX, deltaY) {
|
|
98
|
+
await connector.send("input.performActions", {
|
|
99
|
+
context,
|
|
100
|
+
actions: [
|
|
101
|
+
{
|
|
102
|
+
type: "wheel",
|
|
103
|
+
id: WHEEL_ID,
|
|
104
|
+
actions: [
|
|
105
|
+
{
|
|
106
|
+
type: "scroll",
|
|
107
|
+
x: Math.round(point.x),
|
|
108
|
+
y: Math.round(point.y),
|
|
109
|
+
deltaX: Math.round(deltaX),
|
|
110
|
+
deltaY: Math.round(deltaY),
|
|
111
|
+
origin: "viewport",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
}
|