@nyssance/tauri-hasgard 0.1.2 → 0.3.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/index.d.ts +3 -3
- package/dist/index.js +822 -36
- package/dist/types.d.ts +278 -0
- package/dist/window.d.ts +257 -3
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -6,10 +6,17 @@ export type HasgardTarget = {
|
|
|
6
6
|
ref: string;
|
|
7
7
|
} | {
|
|
8
8
|
selector: string;
|
|
9
|
+
index?: number;
|
|
9
10
|
} | {
|
|
10
11
|
x: number;
|
|
11
12
|
y: number;
|
|
12
13
|
};
|
|
14
|
+
export interface BoundingBox {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}
|
|
13
20
|
export interface SnapshotElement {
|
|
14
21
|
ref: string;
|
|
15
22
|
role: string;
|
|
@@ -26,11 +33,18 @@ export interface SnapshotOptions {
|
|
|
26
33
|
interactive?: boolean;
|
|
27
34
|
selector?: string;
|
|
28
35
|
depth?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Take the snapshot inside this chain of same-origin iframes, one CSS
|
|
38
|
+
* selector per nesting level, instead of in the top document.
|
|
39
|
+
*/
|
|
40
|
+
frame?: string[];
|
|
29
41
|
}
|
|
30
42
|
export interface WindowInfo {
|
|
31
43
|
label: string;
|
|
32
44
|
url: string;
|
|
33
45
|
title: string;
|
|
46
|
+
/** Operating-system window id. Present only where native capture exists. */
|
|
47
|
+
nativeId?: number;
|
|
34
48
|
}
|
|
35
49
|
export interface WindowState {
|
|
36
50
|
url: string;
|
|
@@ -54,6 +68,270 @@ export interface WaitOptions {
|
|
|
54
68
|
state: "attached" | "detached";
|
|
55
69
|
timeoutMs: number;
|
|
56
70
|
}
|
|
71
|
+
export interface ScrollOptions {
|
|
72
|
+
direction: "up" | "down" | "left" | "right" | "top" | "bottom";
|
|
73
|
+
amount?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface DragOffset {
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
}
|
|
79
|
+
/** A held keyboard modifier. Named as the platform reports them on the event. */
|
|
80
|
+
export type KeyModifier = "Alt" | "Control" | "Meta" | "Shift";
|
|
81
|
+
export type MouseButton = "left" | "middle" | "right";
|
|
82
|
+
/** A point inside the element, measured from its top-left corner. */
|
|
83
|
+
export interface ElementPosition {
|
|
84
|
+
x: number;
|
|
85
|
+
y: number;
|
|
86
|
+
}
|
|
87
|
+
export interface ClickOptions {
|
|
88
|
+
/**
|
|
89
|
+
* Modifiers held for the whole gesture. `Meta` is Command on macOS and the
|
|
90
|
+
* Windows key elsewhere -- the platform's own accelerator, which is why
|
|
91
|
+
* multi-select lists usually bind it rather than `Control`.
|
|
92
|
+
*/
|
|
93
|
+
modifiers?: KeyModifier[];
|
|
94
|
+
/**
|
|
95
|
+
* Which button. `right` raises `contextmenu` and `middle` raises `auxclick`;
|
|
96
|
+
* neither raises `click`, exactly as a real press does not.
|
|
97
|
+
*/
|
|
98
|
+
button?: MouseButton;
|
|
99
|
+
/** Presses in one gesture. `2` also raises a single `dblclick` at the end. */
|
|
100
|
+
clickCount?: number;
|
|
101
|
+
/** Where inside the element to press. Defaults to its centre. */
|
|
102
|
+
position?: ElementPosition;
|
|
103
|
+
}
|
|
104
|
+
export interface DropFile {
|
|
105
|
+
name: string;
|
|
106
|
+
/** Base64-encoded file contents. */
|
|
107
|
+
data: string;
|
|
108
|
+
type?: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* A file to hand to the page: either a path on the test machine, which is read
|
|
112
|
+
* and encoded for you, or an in-memory payload for content that never existed
|
|
113
|
+
* as a file.
|
|
114
|
+
*/
|
|
115
|
+
export type FileInput = string | DropFile;
|
|
116
|
+
/** Refinement applied to an existing locator. Both may be combined. */
|
|
117
|
+
export interface FilterOptions {
|
|
118
|
+
/** Keep only elements whose subtree text matches. */
|
|
119
|
+
hasText?: string;
|
|
120
|
+
/** Drop elements whose subtree text matches. */
|
|
121
|
+
hasNotText?: string;
|
|
122
|
+
/** Require a whole-string match instead of a substring one. */
|
|
123
|
+
exact?: boolean;
|
|
124
|
+
}
|
|
125
|
+
export type DialogType = "alert" | "confirm" | "prompt";
|
|
126
|
+
/** A modal the page opened while the standing policy decided its outcome. */
|
|
127
|
+
export interface DialogRecord {
|
|
128
|
+
id: number;
|
|
129
|
+
timestamp: number;
|
|
130
|
+
type: DialogType;
|
|
131
|
+
message: string;
|
|
132
|
+
/** Whether the policy accepted it. A one-button `alert` is always accepted. */
|
|
133
|
+
accepted: boolean;
|
|
134
|
+
/** The page's suggested answer, for `prompt` only. */
|
|
135
|
+
defaultValue?: string;
|
|
136
|
+
/** What the page received back, for an accepted `prompt`. */
|
|
137
|
+
returned?: string;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* How the next dialog will be answered.
|
|
141
|
+
*
|
|
142
|
+
* This is a standing policy rather than Playwright's per-event handler: the
|
|
143
|
+
* bridge must answer synchronously inside the page's `confirm()` call, with no
|
|
144
|
+
* chance to await a decision from the test process.
|
|
145
|
+
*/
|
|
146
|
+
export interface DialogPolicy {
|
|
147
|
+
action: "accept" | "dismiss";
|
|
148
|
+
promptText: string | null;
|
|
149
|
+
}
|
|
150
|
+
export interface DialogListing {
|
|
151
|
+
dialogs: DialogRecord[];
|
|
152
|
+
policy: DialogPolicy;
|
|
153
|
+
}
|
|
154
|
+
/** How a matched request is answered. */
|
|
155
|
+
export interface RouteOptions {
|
|
156
|
+
/**
|
|
157
|
+
* Glob against the full URL. `*` stays inside one path segment, `**` crosses
|
|
158
|
+
* separators, and the pattern is anchored at both ends -- so `**\/api` does
|
|
159
|
+
* not also intercept `/v2/api-docs`.
|
|
160
|
+
*/
|
|
161
|
+
pattern: string;
|
|
162
|
+
/** Restrict to one HTTP verb. Any verb matches when omitted. */
|
|
163
|
+
method?: string;
|
|
164
|
+
/** Stop matching after this many requests, letting later ones through. */
|
|
165
|
+
times?: number;
|
|
166
|
+
}
|
|
167
|
+
export interface FulfillOptions extends RouteOptions {
|
|
168
|
+
/** Defaults to 200. */
|
|
169
|
+
status?: number;
|
|
170
|
+
body?: string;
|
|
171
|
+
/** Defaults to `text/plain`. */
|
|
172
|
+
contentType?: string;
|
|
173
|
+
}
|
|
174
|
+
export interface RouteRule {
|
|
175
|
+
id: number;
|
|
176
|
+
pattern: string;
|
|
177
|
+
method: string | null;
|
|
178
|
+
action: "fulfill" | "abort";
|
|
179
|
+
status: number;
|
|
180
|
+
times: number | null;
|
|
181
|
+
/** How many requests this rule has already answered. */
|
|
182
|
+
used: number;
|
|
183
|
+
}
|
|
184
|
+
export interface InterceptedRequest {
|
|
185
|
+
id: number;
|
|
186
|
+
timestamp: number;
|
|
187
|
+
route_id: number;
|
|
188
|
+
method: string;
|
|
189
|
+
url: string;
|
|
190
|
+
action: "fulfill" | "abort";
|
|
191
|
+
status: number;
|
|
192
|
+
}
|
|
193
|
+
export interface RouteListing {
|
|
194
|
+
routes: RouteRule[];
|
|
195
|
+
intercepted: InterceptedRequest[];
|
|
196
|
+
}
|
|
197
|
+
export interface ConsoleLogOptions {
|
|
198
|
+
level?: "log" | "warn" | "error" | "info";
|
|
199
|
+
since?: number;
|
|
200
|
+
sinceId?: number;
|
|
201
|
+
last?: number;
|
|
202
|
+
}
|
|
203
|
+
export interface ConsoleLogEntry {
|
|
204
|
+
id: number;
|
|
205
|
+
timestamp: number;
|
|
206
|
+
level: string;
|
|
207
|
+
args: JsonValue[];
|
|
208
|
+
source: string | null;
|
|
209
|
+
}
|
|
210
|
+
export interface NetworkRequestOptions {
|
|
211
|
+
filter?: string;
|
|
212
|
+
failedOnly?: boolean;
|
|
213
|
+
sinceId?: number;
|
|
214
|
+
last?: number;
|
|
215
|
+
}
|
|
216
|
+
export interface NetworkRequestEntry {
|
|
217
|
+
id: number;
|
|
218
|
+
timestamp: number;
|
|
219
|
+
method: string;
|
|
220
|
+
url: string;
|
|
221
|
+
status: number;
|
|
222
|
+
duration_ms: number;
|
|
223
|
+
error: string | null;
|
|
224
|
+
request_size: number;
|
|
225
|
+
response_size: number;
|
|
226
|
+
}
|
|
227
|
+
export interface StorageOptions {
|
|
228
|
+
/** Read and write `sessionStorage` instead of `localStorage`. */
|
|
229
|
+
session?: boolean;
|
|
230
|
+
}
|
|
231
|
+
export interface StorageEntry {
|
|
232
|
+
key: string;
|
|
233
|
+
value: string;
|
|
234
|
+
}
|
|
235
|
+
export interface StorageListing {
|
|
236
|
+
entries: StorageEntry[];
|
|
237
|
+
truncated: boolean;
|
|
238
|
+
}
|
|
239
|
+
export interface FormField {
|
|
240
|
+
tag: string;
|
|
241
|
+
type: string | null;
|
|
242
|
+
name: string;
|
|
243
|
+
value: string | string[];
|
|
244
|
+
checked?: boolean;
|
|
245
|
+
}
|
|
246
|
+
export interface FormEntry {
|
|
247
|
+
id: string;
|
|
248
|
+
name: string;
|
|
249
|
+
action: string;
|
|
250
|
+
method: string;
|
|
251
|
+
fields: FormField[];
|
|
252
|
+
fieldsTruncated?: boolean;
|
|
253
|
+
}
|
|
254
|
+
export interface FormsDump {
|
|
255
|
+
forms: FormEntry[];
|
|
256
|
+
truncated: boolean;
|
|
257
|
+
}
|
|
258
|
+
export type QueryDimension = "text" | "label" | "placeholder" | "testid" | "alt" | "title";
|
|
259
|
+
export interface TextQuery {
|
|
260
|
+
/** Full, case-sensitive match. Whitespace is normalized either way. */
|
|
261
|
+
exact?: boolean;
|
|
262
|
+
}
|
|
263
|
+
export interface WaitForFunctionOptions {
|
|
264
|
+
timeoutMs?: number;
|
|
265
|
+
/** How often to re-evaluate the expression. Defaults to 50ms in the bridge. */
|
|
266
|
+
pollMs?: number;
|
|
267
|
+
}
|
|
268
|
+
export interface WatchOptions {
|
|
269
|
+
selector?: string;
|
|
270
|
+
timeoutMs?: number;
|
|
271
|
+
stableMs?: number;
|
|
272
|
+
requireMutation?: boolean;
|
|
273
|
+
}
|
|
274
|
+
export interface WatchNode {
|
|
275
|
+
tag: string;
|
|
276
|
+
id?: string;
|
|
277
|
+
class?: string;
|
|
278
|
+
text?: string;
|
|
279
|
+
attr?: string;
|
|
280
|
+
value?: string;
|
|
281
|
+
}
|
|
282
|
+
export interface WatchChanges {
|
|
283
|
+
added: WatchNode[];
|
|
284
|
+
removed: WatchNode[];
|
|
285
|
+
modified: WatchNode[];
|
|
286
|
+
truncated: boolean;
|
|
287
|
+
}
|
|
288
|
+
export interface DiffOptions extends SnapshotOptions {
|
|
289
|
+
/** Compare against this snapshot instead of the plugin's last stored one. */
|
|
290
|
+
reference?: Snapshot;
|
|
291
|
+
}
|
|
292
|
+
export interface DiffChange {
|
|
293
|
+
old: SnapshotElement;
|
|
294
|
+
new: SnapshotElement;
|
|
295
|
+
changes: string[];
|
|
296
|
+
}
|
|
297
|
+
export interface SnapshotDiff {
|
|
298
|
+
added: SnapshotElement[];
|
|
299
|
+
removed: SnapshotElement[];
|
|
300
|
+
changed: DiffChange[];
|
|
301
|
+
}
|
|
302
|
+
export interface NativeScreenshotOptions {
|
|
303
|
+
/** Absolute path; the parent directory must already exist. */
|
|
304
|
+
outputPath: string;
|
|
305
|
+
/**
|
|
306
|
+
* Operating-system window id. Resolved from the window's own label when
|
|
307
|
+
* omitted; pass it only to capture a window Hasgard does not own.
|
|
308
|
+
*/
|
|
309
|
+
windowId?: number;
|
|
310
|
+
}
|
|
311
|
+
export interface NativeScreenshot {
|
|
312
|
+
output_path: string;
|
|
313
|
+
window_id: number;
|
|
314
|
+
width: number;
|
|
315
|
+
height: number;
|
|
316
|
+
scale_factor: number;
|
|
317
|
+
byte_size: number;
|
|
318
|
+
backend: string;
|
|
319
|
+
tcc_denied: boolean;
|
|
320
|
+
}
|
|
321
|
+
export interface RecorderEntry {
|
|
322
|
+
action: string;
|
|
323
|
+
timestamp: number;
|
|
324
|
+
[key: string]: JsonValue;
|
|
325
|
+
}
|
|
326
|
+
export interface RecorderStatus {
|
|
327
|
+
active: boolean;
|
|
328
|
+
count: number;
|
|
329
|
+
elapsed_ms: number;
|
|
330
|
+
}
|
|
331
|
+
export interface RecorderResult {
|
|
332
|
+
entries: RecorderEntry[];
|
|
333
|
+
count: number;
|
|
334
|
+
}
|
|
57
335
|
export interface HasgardLaunchConfig {
|
|
58
336
|
command: string;
|
|
59
337
|
args: string[];
|
package/dist/window.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HasgardRpcClient } from "./rpc-client.js";
|
|
2
|
-
import type { HasgardTarget, JsonValue, RoleQuery, Snapshot, SnapshotOptions, WaitOptions, WindowInfo, WindowState } from "./types.js";
|
|
2
|
+
import type { BoundingBox, ClickOptions, ConsoleLogEntry, ConsoleLogOptions, DialogListing, DialogPolicy, DiffOptions, DragOffset, DropFile, FileInput, FilterOptions, FormsDump, FulfillOptions, HasgardTarget, JsonValue, NativeScreenshot, NativeScreenshotOptions, NetworkRequestEntry, NetworkRequestOptions, QueryDimension, RecorderEntry, RecorderResult, RecorderStatus, RoleQuery, RouteListing, RouteOptions, ScrollOptions, Snapshot, SnapshotDiff, SnapshotOptions, StorageListing, StorageOptions, TextQuery, WaitForFunctionOptions, WaitOptions, WatchChanges, WatchOptions, WindowInfo, WindowState } from "./types.js";
|
|
3
3
|
export declare const byRef: (ref: string) => HasgardTarget;
|
|
4
4
|
export declare const bySelector: (selector: string) => HasgardTarget;
|
|
5
5
|
export declare const byPoint: (x: number, y: number) => HasgardTarget;
|
|
@@ -14,6 +14,16 @@ export declare class HasgardApplication {
|
|
|
14
14
|
waitForWindow(label: string, timeoutMs: number): Promise<HasgardWindow>;
|
|
15
15
|
waitForWindowReady(label: string, readySelector: string, timeoutMs: number): Promise<HasgardWindow>;
|
|
16
16
|
window(label: string): HasgardWindow;
|
|
17
|
+
/**
|
|
18
|
+
* Action recorder. Captures the RPC calls this connection makes into a
|
|
19
|
+
* replayable scenario — this is not video capture.
|
|
20
|
+
*/
|
|
21
|
+
readonly recorder: {
|
|
22
|
+
start: () => Promise<void>;
|
|
23
|
+
stop: () => Promise<RecorderResult>;
|
|
24
|
+
status: () => Promise<RecorderStatus>;
|
|
25
|
+
add: (entry: RecorderEntry) => Promise<void>;
|
|
26
|
+
};
|
|
17
27
|
}
|
|
18
28
|
export declare class HasgardWindow {
|
|
19
29
|
readonly label: string;
|
|
@@ -21,41 +31,285 @@ export declare class HasgardWindow {
|
|
|
21
31
|
constructor(rpc: HasgardRpcClient, label: string);
|
|
22
32
|
locator(selector: string): HasgardLocator;
|
|
23
33
|
getByRole(role: string, query?: RoleQuery): HasgardLocator;
|
|
34
|
+
/**
|
|
35
|
+
* Locate by visible text. Matching runs in the page against the live DOM, not
|
|
36
|
+
* against a snapshot's `name`: that field collapses aria-label, alt, `<label>`,
|
|
37
|
+
* placeholder, and text into one 50-character string, so a button with an
|
|
38
|
+
* aria-label would never be findable by the text it actually shows.
|
|
39
|
+
*/
|
|
40
|
+
getByText(text: string, query?: TextQuery): HasgardLocator;
|
|
41
|
+
getByLabel(text: string, query?: TextQuery): HasgardLocator;
|
|
42
|
+
getByPlaceholder(text: string, query?: TextQuery): HasgardLocator;
|
|
43
|
+
getByAltText(text: string, query?: TextQuery): HasgardLocator;
|
|
44
|
+
getByTitle(text: string, query?: TextQuery): HasgardLocator;
|
|
45
|
+
/** Test ids always match exactly, so `save` cannot select `save-draft`. */
|
|
46
|
+
getByTestId(testId: string): HasgardLocator;
|
|
47
|
+
/**
|
|
48
|
+
* Scope locators to a same-origin `<iframe>`.
|
|
49
|
+
*
|
|
50
|
+
* Cross-origin frames are out of reach: the same-origin policy binds injected
|
|
51
|
+
* script exactly as it binds the page's own, so no bridge can see into a
|
|
52
|
+
* third-party payment or OAuth frame. Those raise an error naming the origin
|
|
53
|
+
* problem rather than reporting a missing element.
|
|
54
|
+
*/
|
|
55
|
+
frameLocator(selector: string): HasgardFrameLocator;
|
|
24
56
|
snapshot(options?: SnapshotOptions): Promise<Snapshot>;
|
|
25
57
|
state(): Promise<WindowState>;
|
|
58
|
+
url(): Promise<string>;
|
|
59
|
+
title(): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Navigate this webview by assigning `location.href`. Unlike Playwright's
|
|
62
|
+
* `goto`, this does not wait for the load to finish and returns no response —
|
|
63
|
+
* follow it with `waitFor` or `waitForWindowReady`.
|
|
64
|
+
*/
|
|
65
|
+
navigate(url: string): Promise<void>;
|
|
66
|
+
/** Scroll the document by a relative amount, or to `top`/`bottom`. */
|
|
67
|
+
scrollBy(options: ScrollOptions): Promise<void>;
|
|
26
68
|
press(key: string): Promise<void>;
|
|
69
|
+
diff(options?: DiffOptions): Promise<SnapshotDiff>;
|
|
70
|
+
/**
|
|
71
|
+
* Poll a JavaScript expression in the webview until it returns a truthy
|
|
72
|
+
* value, and return that value. The expression is polled rather than driven
|
|
73
|
+
* by DOM mutations, so predicates that flip on a timer or a resolved fetch
|
|
74
|
+
* are caught too. A predicate that throws rejects instead of being retried.
|
|
75
|
+
*/
|
|
76
|
+
waitForFunction<T = JsonValue>(expression: string, options?: WaitForFunctionOptions): Promise<T>;
|
|
77
|
+
/** Observe DOM mutations until they settle, or reject when nothing changes. */
|
|
78
|
+
watch(options?: WatchOptions): Promise<WatchChanges>;
|
|
79
|
+
consoleLogs(options?: ConsoleLogOptions): Promise<ConsoleLogEntry[]>;
|
|
80
|
+
clearConsole(): Promise<void>;
|
|
81
|
+
networkRequests(options?: NetworkRequestOptions): Promise<NetworkRequestEntry[]>;
|
|
82
|
+
clearNetwork(): Promise<void>;
|
|
83
|
+
/** `localStorage` by default; pass `{ session: true }` for `sessionStorage`. */
|
|
84
|
+
readonly storage: {
|
|
85
|
+
get: (key: string, options?: StorageOptions) => Promise<string | null>;
|
|
86
|
+
set: (key: string, value: string, options?: StorageOptions) => Promise<void>;
|
|
87
|
+
list: (options?: StorageOptions) => Promise<StorageListing>;
|
|
88
|
+
clear: (options?: StorageOptions) => Promise<void>;
|
|
89
|
+
};
|
|
90
|
+
forms(selector?: string): Promise<FormsDump>;
|
|
27
91
|
evaluate<T>(script: string): Promise<T>;
|
|
28
92
|
invoke<T>(command: string, args: Record<string, JsonValue>): Promise<T>;
|
|
29
93
|
screenshot(selector?: string): Promise<Buffer>;
|
|
94
|
+
/**
|
|
95
|
+
* Capture this window through the native compositor, writing a PNG to
|
|
96
|
+
* `outputPath`. macOS only.
|
|
97
|
+
*
|
|
98
|
+
* Unlike `screenshot`, which renders the DOM inside the page, this records
|
|
99
|
+
* what the window server actually shows — so it sees native chrome, layered
|
|
100
|
+
* content, and anything the DOM renderer cannot reproduce.
|
|
101
|
+
*
|
|
102
|
+
* The operating-system window id is resolved from this window's label, so
|
|
103
|
+
* callers do not have to discover it themselves.
|
|
104
|
+
*/
|
|
105
|
+
screenshotNative(options: NativeScreenshotOptions): Promise<NativeScreenshot>;
|
|
106
|
+
/**
|
|
107
|
+
* Scroll the page by wheel.
|
|
108
|
+
*
|
|
109
|
+
* The document sees a real `wheel` event first and may cancel it; the scroll
|
|
110
|
+
* only follows if it does not. Resolves to whether the page cancelled — which
|
|
111
|
+
* is how you tell "the app intercepted my wheel" from "nothing was
|
|
112
|
+
* listening". Use the locator method to scroll a nested scrollport.
|
|
113
|
+
*/
|
|
114
|
+
wheel(deltaX: number, deltaY: number): Promise<boolean>;
|
|
115
|
+
/**
|
|
116
|
+
* The page's modal dialogs (`alert`, `confirm`, `prompt`).
|
|
117
|
+
*
|
|
118
|
+
* These block the webview until answered, so the bridge always answers them —
|
|
119
|
+
* dismissing by default, as Playwright does. A test that never touches this
|
|
120
|
+
* namespace still cannot hang on a `confirm()`, and can read back what the
|
|
121
|
+
* app tried to ask.
|
|
122
|
+
*/
|
|
123
|
+
readonly dialogs: {
|
|
124
|
+
/** Answer every subsequent dialog with OK. `promptText` fills a `prompt`. */
|
|
125
|
+
accept: (promptText?: string) => Promise<DialogPolicy>;
|
|
126
|
+
/** Answer every subsequent dialog with Cancel. This is the default. */
|
|
127
|
+
dismiss: () => Promise<DialogPolicy>;
|
|
128
|
+
/** Every dialog the page has opened so far, oldest first. */
|
|
129
|
+
list: () => Promise<DialogListing>;
|
|
130
|
+
/** Forget the recorded dialogs. Leaves the accept/dismiss policy alone. */
|
|
131
|
+
clear: () => Promise<void>;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Shape what the page's own requests receive, without a real backend.
|
|
135
|
+
*
|
|
136
|
+
* Rules are declarative rather than Playwright's per-request callback: the
|
|
137
|
+
* bridge only ever answers requests, so it cannot call back into this process
|
|
138
|
+
* and await a handler while the page sits inside `fetch`. The same constraint
|
|
139
|
+
* shaped `dialogs`.
|
|
140
|
+
*
|
|
141
|
+
* Rules are checked in registration order and the first match wins, so
|
|
142
|
+
* register the narrow ones before a catch-all. Both `fetch` and
|
|
143
|
+
* `XMLHttpRequest` are covered; resources the webview loads itself -- the
|
|
144
|
+
* document, `<img src>`, stylesheets -- are not, since no page script sees
|
|
145
|
+
* them.
|
|
146
|
+
*/
|
|
147
|
+
readonly routes: {
|
|
148
|
+
/** Answer matching requests with this response instead of the network. */
|
|
149
|
+
fulfill: (options: FulfillOptions) => Promise<{
|
|
150
|
+
id: number;
|
|
151
|
+
}>;
|
|
152
|
+
/** Fail matching requests the way a dropped connection does. */
|
|
153
|
+
abort: (options: RouteOptions) => Promise<{
|
|
154
|
+
id: number;
|
|
155
|
+
}>;
|
|
156
|
+
/** The active rules, their use counts, and every request they answered. */
|
|
157
|
+
list: () => Promise<RouteListing>;
|
|
158
|
+
/** Remove every rule and its interception log. */
|
|
159
|
+
clear: () => Promise<{
|
|
160
|
+
removed: number;
|
|
161
|
+
}>;
|
|
162
|
+
};
|
|
163
|
+
/** This window's operating-system window id, where the platform exposes one. */
|
|
164
|
+
nativeId(): Promise<number>;
|
|
30
165
|
call<T>(method: string, params?: Record<string, JsonValue>): Promise<T>;
|
|
31
166
|
runExclusive<T>(operation: () => Promise<T>): Promise<T>;
|
|
32
167
|
}
|
|
33
|
-
type LocatorQuery = {
|
|
168
|
+
type LocatorQuery = ({
|
|
34
169
|
kind: "selector";
|
|
35
170
|
selector: string;
|
|
36
171
|
} | {
|
|
37
172
|
kind: "role";
|
|
38
173
|
role: string;
|
|
39
174
|
query: RoleQuery;
|
|
175
|
+
} | {
|
|
176
|
+
kind: "query";
|
|
177
|
+
by: QueryDimension;
|
|
178
|
+
value: string;
|
|
179
|
+
query: TextQuery;
|
|
180
|
+
}) & {
|
|
181
|
+
index?: number;
|
|
182
|
+
filters?: FilterOptions[];
|
|
183
|
+
frame?: string[];
|
|
40
184
|
};
|
|
185
|
+
/**
|
|
186
|
+
* The locator factories of a window, scoped to a chain of same-origin iframes.
|
|
187
|
+
*
|
|
188
|
+
* Mirrors the window's own factories rather than wrapping a locator, because a
|
|
189
|
+
* frame is a document to search in, not an element to search under: `#pay`
|
|
190
|
+
* inside the frame is not a descendant of `#pay` outside it.
|
|
191
|
+
*/
|
|
192
|
+
export declare class HasgardFrameLocator {
|
|
193
|
+
private readonly windowRef;
|
|
194
|
+
private readonly frame;
|
|
195
|
+
constructor(window: HasgardWindow, frame: string[]);
|
|
196
|
+
/** Descend into a frame nested inside this one. */
|
|
197
|
+
frameLocator(selector: string): HasgardFrameLocator;
|
|
198
|
+
locator(selector: string): HasgardLocator;
|
|
199
|
+
getByRole(role: string, query?: RoleQuery): HasgardLocator;
|
|
200
|
+
getByText(text: string, query?: TextQuery): HasgardLocator;
|
|
201
|
+
getByLabel(text: string, query?: TextQuery): HasgardLocator;
|
|
202
|
+
getByPlaceholder(text: string, query?: TextQuery): HasgardLocator;
|
|
203
|
+
getByAltText(text: string, query?: TextQuery): HasgardLocator;
|
|
204
|
+
getByTitle(text: string, query?: TextQuery): HasgardLocator;
|
|
205
|
+
getByTestId(testId: string): HasgardLocator;
|
|
206
|
+
/** Snapshot the frame's own document rather than the top one. */
|
|
207
|
+
snapshot(options?: SnapshotOptions): Promise<Snapshot>;
|
|
208
|
+
private byDimension;
|
|
209
|
+
}
|
|
41
210
|
export declare class HasgardLocator {
|
|
42
211
|
readonly window: HasgardWindow;
|
|
43
212
|
private readonly query;
|
|
44
213
|
constructor(window: HasgardWindow, query: LocatorQuery);
|
|
45
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Click this element.
|
|
216
|
+
*
|
|
217
|
+
* A right or middle click raises `contextmenu`/`auxclick` and no `click`, and
|
|
218
|
+
* `clickCount: 2` escalates `detail` across the presses before a single
|
|
219
|
+
* `dblclick` -- both matching what the platform does, so a listener bound to
|
|
220
|
+
* the wrong event fails here rather than in front of a user.
|
|
221
|
+
*/
|
|
222
|
+
click(options?: ClickOptions): Promise<void>;
|
|
46
223
|
fill(value: string): Promise<void>;
|
|
47
224
|
type(text: string): Promise<void>;
|
|
225
|
+
/** Empty the control, firing `input` and `change` as `fill` does. */
|
|
226
|
+
clear(): Promise<void>;
|
|
227
|
+
/**
|
|
228
|
+
* Select files on an `<input type="file">` without a native file chooser,
|
|
229
|
+
* which an embedded webview gives no way to drive.
|
|
230
|
+
*
|
|
231
|
+
* Strings are read from the test machine's disk; `DropFile` payloads let you
|
|
232
|
+
* supply content that never existed as a file. An empty array deselects.
|
|
233
|
+
*/
|
|
234
|
+
setInputFiles(files: FileInput | FileInput[]): Promise<void>;
|
|
235
|
+
/**
|
|
236
|
+
* Scroll this element by wheel.
|
|
237
|
+
*
|
|
238
|
+
* The page sees a real `wheel` event first and may cancel it; the scroll only
|
|
239
|
+
* follows if it does not — the same two steps a physical wheel produces.
|
|
240
|
+
* Resolves to whether the page cancelled.
|
|
241
|
+
*/
|
|
242
|
+
wheel(deltaX: number, deltaY: number): Promise<boolean>;
|
|
48
243
|
selectOption(value: string): Promise<void>;
|
|
244
|
+
/** Drive the checkbox to checked. A no-op when it is already checked. */
|
|
49
245
|
check(): Promise<void>;
|
|
246
|
+
/** Drive the checkbox to unchecked. A no-op when it is already unchecked. */
|
|
247
|
+
uncheck(): Promise<void>;
|
|
248
|
+
/** Flip the checkbox, whatever its current state. */
|
|
249
|
+
toggle(): Promise<void>;
|
|
250
|
+
hover(): Promise<void>;
|
|
251
|
+
/** Shorthand for `click({ clickCount: 2 })`; other options still apply. */
|
|
252
|
+
dblclick(options?: Omit<ClickOptions, "clickCount">): Promise<void>;
|
|
253
|
+
focus(): Promise<void>;
|
|
254
|
+
blur(): Promise<void>;
|
|
255
|
+
/** Scroll this element's own scrollport. */
|
|
256
|
+
scrollBy(options: ScrollOptions): Promise<void>;
|
|
257
|
+
boundingBox(): Promise<BoundingBox>;
|
|
258
|
+
isDisabled(): Promise<boolean>;
|
|
259
|
+
isEnabled(): Promise<boolean>;
|
|
50
260
|
textContent(): Promise<string>;
|
|
51
261
|
inputValue(): Promise<string>;
|
|
262
|
+
innerHTML(): Promise<string>;
|
|
263
|
+
getAttribute(name: string): Promise<string | null>;
|
|
264
|
+
/** Drag this element onto another one in the same window. */
|
|
265
|
+
dragTo(target: HasgardLocator): Promise<void>;
|
|
266
|
+
/** Drag this element by a pixel offset from its centre. */
|
|
267
|
+
dragBy(offset: DragOffset): Promise<void>;
|
|
268
|
+
/** Dispatch a file drop onto this element. `data` is base64-encoded. */
|
|
269
|
+
dropFiles(files: DropFile[]): Promise<void>;
|
|
52
270
|
attributes(): Promise<Record<string, string>>;
|
|
53
271
|
isVisible(): Promise<boolean>;
|
|
54
272
|
isChecked(): Promise<boolean>;
|
|
55
273
|
count(): Promise<number>;
|
|
56
274
|
waitFor(options: WaitOptions): Promise<void>;
|
|
275
|
+
/**
|
|
276
|
+
* Narrow this locator to a single match by position. A negative index counts
|
|
277
|
+
* back from the end, so `last()` stays one round trip.
|
|
278
|
+
*/
|
|
279
|
+
nth(index: number): HasgardLocator;
|
|
280
|
+
/**
|
|
281
|
+
* Narrow this locator to the elements whose text matches.
|
|
282
|
+
*
|
|
283
|
+
* Filters accumulate, so `filter({hasText}).filter({hasNotText})` applies
|
|
284
|
+
* both. Ordering matters against `nth`: filtering happens first, so
|
|
285
|
+
* `filter({hasText: "x"}).first()` is the first *matching* element, which is
|
|
286
|
+
* the reading that makes `first()` mean what it says.
|
|
287
|
+
*/
|
|
288
|
+
filter(options: FilterOptions): HasgardLocator;
|
|
289
|
+
first(): HasgardLocator;
|
|
290
|
+
last(): HasgardLocator;
|
|
57
291
|
private resolveUnique;
|
|
292
|
+
/**
|
|
293
|
+
* Serialize a target together with the frame chain this locator lives in.
|
|
294
|
+
*
|
|
295
|
+
* A ref carries its own document, so the bridge ignores `frame` for one; the
|
|
296
|
+
* chain still travels so that a selector or coordinate target -- which the
|
|
297
|
+
* bridge must resolve itself -- lands in the right document.
|
|
298
|
+
*/
|
|
299
|
+
private scoped;
|
|
300
|
+
private frameParams;
|
|
58
301
|
private withTarget;
|
|
59
302
|
private resolveAll;
|
|
303
|
+
private resolveBase;
|
|
304
|
+
/**
|
|
305
|
+
* Apply each accumulated filter, one round trip per filter.
|
|
306
|
+
*
|
|
307
|
+
* Filters run server-side rather than against a snapshot because the text a
|
|
308
|
+
* filter matches on is not carried in snapshot entries — deliberately, so
|
|
309
|
+
* that every unrelated `snapshot` call stays small.
|
|
310
|
+
*/
|
|
311
|
+
private applyFilters;
|
|
312
|
+
private runQuery;
|
|
313
|
+
private parseElements;
|
|
60
314
|
}
|
|
61
315
|
export {};
|