wrc-ts 0.1.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/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/browser.d.ts +39 -0
- package/dist/browser.js +43 -0
- package/dist/client.d.ts +840 -0
- package/dist/client.js +1264 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.js +78 -0
- package/dist/cookies.d.ts +21 -0
- package/dist/cookies.js +1 -0
- package/dist/defaults.d.ts +19 -0
- package/dist/defaults.js +22 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +21 -0
- package/dist/gen/google/protobuf/empty_pb.d.ts +15 -0
- package/dist/gen/google/protobuf/empty_pb.js +13 -0
- package/dist/gen/wrc_pb.d.ts +2189 -0
- package/dist/gen/wrc_pb.js +328 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +134 -0
- package/dist/internal/convert.d.ts +44 -0
- package/dist/internal/convert.js +250 -0
- package/dist/locator.d.ts +191 -0
- package/dist/locator.js +255 -0
- package/dist/network.d.ts +37 -0
- package/dist/network.js +2 -0
- package/dist/options.d.ts +96 -0
- package/dist/options.js +7 -0
- package/dist/storage.d.ts +14 -0
- package/dist/storage.js +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.js +3 -0
- package/dist/wrc.browser.js +5627 -0
- package/dist/ws-transport.d.ts +15 -0
- package/dist/ws-transport.js +99 -0
- package/package.json +80 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Internal proto ↔ TS helpers used by client.ts. Anything in here is
|
|
2
|
+
// implementation detail and is NOT exported from the package entrypoints.
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import { HeaderModificationSchema, CookiePartitionKeySchema, CookieParamSchema, HeaderSchema, StorageItemSchema, StorageOriginEntrySchema, } from "../gen/wrc_pb.js";
|
|
5
|
+
import { pickFrame } from "../locator.js";
|
|
6
|
+
export function elementFields(target, optsInFrame) {
|
|
7
|
+
const out = {};
|
|
8
|
+
if (target.selector)
|
|
9
|
+
out.selector = target.selector;
|
|
10
|
+
if (target.jsExpression)
|
|
11
|
+
out.jsExpression = target.jsExpression;
|
|
12
|
+
if (target.backendNodeId)
|
|
13
|
+
out.backendNodeId = target.backendNodeId;
|
|
14
|
+
const fid = pickFrame(optsInFrame, target);
|
|
15
|
+
if (fid)
|
|
16
|
+
out.frameId = fid;
|
|
17
|
+
if (target.x !== undefined)
|
|
18
|
+
out.x = target.x;
|
|
19
|
+
if (target.y !== undefined)
|
|
20
|
+
out.y = target.y;
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
24
|
+
// Rect
|
|
25
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
26
|
+
export function rectFromProto(r) {
|
|
27
|
+
if (!r)
|
|
28
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
29
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
30
|
+
}
|
|
31
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Element / Drag / Wait results
|
|
33
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
34
|
+
export function elementResultFromProto(r) {
|
|
35
|
+
return {
|
|
36
|
+
success: r.success,
|
|
37
|
+
frameId: r.frameId,
|
|
38
|
+
backendNodeId: r.backendNodeId,
|
|
39
|
+
isVisible: r.isVisible,
|
|
40
|
+
bounds: rectFromProto(r.bounds),
|
|
41
|
+
rootX: r.rootX,
|
|
42
|
+
rootY: r.rootY,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function dragResultFromProto(r) {
|
|
46
|
+
return {
|
|
47
|
+
success: r.success,
|
|
48
|
+
frameId: r.frameId,
|
|
49
|
+
backendNodeId: r.backendNodeId,
|
|
50
|
+
startX: r.startX,
|
|
51
|
+
startY: r.startY,
|
|
52
|
+
endX: r.endX,
|
|
53
|
+
endY: r.endY,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function waitResultFromProto(r) {
|
|
57
|
+
return {
|
|
58
|
+
index: r.index,
|
|
59
|
+
frameId: r.frameId,
|
|
60
|
+
backendNodeId: r.backendNodeId,
|
|
61
|
+
isVisible: r.isVisible,
|
|
62
|
+
bounds: rectFromProto(r.bounds),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
66
|
+
// FrameInfo / PageInfo
|
|
67
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
68
|
+
export function frameInfoFromProto(f) {
|
|
69
|
+
return {
|
|
70
|
+
frameId: f.frameId,
|
|
71
|
+
url: f.url,
|
|
72
|
+
isOOPIF: f.isOopif,
|
|
73
|
+
hasJSContext: f.hasJsContext,
|
|
74
|
+
isLoading: f.isLoading,
|
|
75
|
+
isVisible: f.isVisible,
|
|
76
|
+
absoluteRect: rectFromProto(f.absoluteRect),
|
|
77
|
+
relativeRect: rectFromProto(f.relativeRect),
|
|
78
|
+
children: f.children.map(frameInfoFromProto),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function pageInfoFromProto(p) {
|
|
82
|
+
return {
|
|
83
|
+
pageId: p.pageId,
|
|
84
|
+
browserContextId: p.browserContextId,
|
|
85
|
+
url: p.url,
|
|
86
|
+
title: p.title,
|
|
87
|
+
viewport: rectFromProto(p.viewport),
|
|
88
|
+
frameTree: p.frameTree
|
|
89
|
+
? frameInfoFromProto(p.frameTree)
|
|
90
|
+
: {},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
94
|
+
// Headers
|
|
95
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
96
|
+
export function headerFromProto(h) {
|
|
97
|
+
return { name: h.name, value: h.value };
|
|
98
|
+
}
|
|
99
|
+
export function headersToProto(headers) {
|
|
100
|
+
if (!headers)
|
|
101
|
+
return [];
|
|
102
|
+
return headers.map(h => {
|
|
103
|
+
const msg = create(HeaderSchema);
|
|
104
|
+
msg.name = h.name;
|
|
105
|
+
msg.value = h.value;
|
|
106
|
+
return msg;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
110
|
+
// Intercepted request / response
|
|
111
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
112
|
+
export function interceptedRequestFromProto(r) {
|
|
113
|
+
if (!r)
|
|
114
|
+
return null;
|
|
115
|
+
return {
|
|
116
|
+
method: r.method,
|
|
117
|
+
url: r.url,
|
|
118
|
+
headers: r.headers.map(headerFromProto),
|
|
119
|
+
body: r.body,
|
|
120
|
+
resourceType: r.resourceType,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export function interceptedResponseFromProto(r) {
|
|
124
|
+
if (!r)
|
|
125
|
+
return null;
|
|
126
|
+
return {
|
|
127
|
+
url: r.url,
|
|
128
|
+
statusCode: r.statusCode,
|
|
129
|
+
headers: r.headers.map(headerFromProto),
|
|
130
|
+
body: r.body,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
134
|
+
// Cookies
|
|
135
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
136
|
+
export function cookieParamFromProto(c) {
|
|
137
|
+
return {
|
|
138
|
+
name: c.name,
|
|
139
|
+
value: c.value,
|
|
140
|
+
url: c.url,
|
|
141
|
+
domain: c.domain,
|
|
142
|
+
path: c.path,
|
|
143
|
+
secure: c.secure,
|
|
144
|
+
httpOnly: c.httpOnly,
|
|
145
|
+
sameSite: c.sameSite,
|
|
146
|
+
expires: c.expires,
|
|
147
|
+
priority: c.priority,
|
|
148
|
+
sourceScheme: c.sourceScheme,
|
|
149
|
+
sourcePort: c.sourcePort,
|
|
150
|
+
partitionKey: c.partitionKey
|
|
151
|
+
? {
|
|
152
|
+
topLevelSite: c.partitionKey.topLevelSite,
|
|
153
|
+
hasCrossSiteAncestor: c.partitionKey.hasCrossSiteAncestor,
|
|
154
|
+
}
|
|
155
|
+
: undefined,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function cookieParamsToProto(cookies) {
|
|
159
|
+
return cookies.map(c => {
|
|
160
|
+
const msg = create(CookieParamSchema);
|
|
161
|
+
msg.name = c.name;
|
|
162
|
+
msg.value = c.value;
|
|
163
|
+
if (c.url !== undefined)
|
|
164
|
+
msg.url = c.url;
|
|
165
|
+
msg.domain = c.domain;
|
|
166
|
+
msg.path = c.path;
|
|
167
|
+
if (c.secure !== undefined)
|
|
168
|
+
msg.secure = c.secure;
|
|
169
|
+
if (c.httpOnly !== undefined)
|
|
170
|
+
msg.httpOnly = c.httpOnly;
|
|
171
|
+
if (c.sameSite !== undefined)
|
|
172
|
+
msg.sameSite = c.sameSite;
|
|
173
|
+
if (c.expires !== undefined)
|
|
174
|
+
msg.expires = c.expires;
|
|
175
|
+
if (c.priority !== undefined)
|
|
176
|
+
msg.priority = c.priority;
|
|
177
|
+
if (c.sourceScheme !== undefined)
|
|
178
|
+
msg.sourceScheme = c.sourceScheme;
|
|
179
|
+
if (c.sourcePort !== undefined)
|
|
180
|
+
msg.sourcePort = c.sourcePort;
|
|
181
|
+
if (c.partitionKey !== undefined) {
|
|
182
|
+
msg.partitionKey = create(CookiePartitionKeySchema);
|
|
183
|
+
msg.partitionKey.topLevelSite = c.partitionKey.topLevelSite;
|
|
184
|
+
msg.partitionKey.hasCrossSiteAncestor = c.partitionKey.hasCrossSiteAncestor;
|
|
185
|
+
}
|
|
186
|
+
return msg;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
190
|
+
// Storage (localStorage)
|
|
191
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
192
|
+
export function storageEntryFromProto(e) {
|
|
193
|
+
return {
|
|
194
|
+
origin: e.origin,
|
|
195
|
+
items: e.items.map(it => ({ key: it.key, value: it.value })),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function storageEntriesToProto(storage) {
|
|
199
|
+
return storage.map(e => {
|
|
200
|
+
const msg = create(StorageOriginEntrySchema);
|
|
201
|
+
msg.origin = e.origin;
|
|
202
|
+
msg.items = e.items.map(it => {
|
|
203
|
+
const item = create(StorageItemSchema);
|
|
204
|
+
item.key = it.key;
|
|
205
|
+
item.value = it.value;
|
|
206
|
+
return item;
|
|
207
|
+
});
|
|
208
|
+
return msg;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
212
|
+
// Network — patterns + header modifications
|
|
213
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
214
|
+
/**
|
|
215
|
+
* splitRequestPatterns turns a RequestPattern[] into the proto's parallel
|
|
216
|
+
* URL + abort-flag slices. When no pattern has abort set, the aborts
|
|
217
|
+
* slice is returned as an empty array so it stays off the wire.
|
|
218
|
+
*/
|
|
219
|
+
export function splitRequestPatterns(patterns) {
|
|
220
|
+
const urls = new Array(patterns.length);
|
|
221
|
+
let any = false;
|
|
222
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
223
|
+
urls[i] = patterns[i].url;
|
|
224
|
+
if (patterns[i].abort)
|
|
225
|
+
any = true;
|
|
226
|
+
}
|
|
227
|
+
if (!any)
|
|
228
|
+
return { urls, aborts: [] };
|
|
229
|
+
const aborts = new Array(patterns.length);
|
|
230
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
231
|
+
aborts[i] = patterns[i].abort ? 1 : 0;
|
|
232
|
+
}
|
|
233
|
+
return { urls, aborts };
|
|
234
|
+
}
|
|
235
|
+
export function headerModsToProto(mods) {
|
|
236
|
+
if (!mods)
|
|
237
|
+
return [];
|
|
238
|
+
return mods.map(m => {
|
|
239
|
+
const msg = create(HeaderModificationSchema);
|
|
240
|
+
msg.name = m.name;
|
|
241
|
+
if (m.value !== undefined)
|
|
242
|
+
msg.value = m.value;
|
|
243
|
+
msg.action = m.action;
|
|
244
|
+
if (m.before)
|
|
245
|
+
msg.before = m.before;
|
|
246
|
+
if (m.after)
|
|
247
|
+
msg.after = m.after;
|
|
248
|
+
return msg;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AllFrames is the WRC.pdl sentinel for "match in every frame on the page".
|
|
3
|
+
* Use it for any field documented as accepting a frameId — both
|
|
4
|
+
* Locator.inFrame and *Opts.inFrame.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AllFrames = "ALL_FRAMES";
|
|
7
|
+
/**
|
|
8
|
+
* Locator is the universal "what element / what condition" type. It is used
|
|
9
|
+
* both as a wait condition (passed to wait()/waitAny()) and as a target for
|
|
10
|
+
* element actions (passed to click(), fill(), …).
|
|
11
|
+
*
|
|
12
|
+
* Not every field is meaningful in every context:
|
|
13
|
+
* - selector / jsExpression → both wait and actions
|
|
14
|
+
* - backendNodeId → actions only (wait rejects it)
|
|
15
|
+
* - visible / steadyMs → wait only (silently ignored by actions)
|
|
16
|
+
* - x / y → actions only (wait rejects it)
|
|
17
|
+
* - frameId → both, may be overridden by call-level
|
|
18
|
+
* opts.inFrame
|
|
19
|
+
*
|
|
20
|
+
* Use the css() / js() / node() / at() constructors instead of building
|
|
21
|
+
* this class by hand.
|
|
22
|
+
*
|
|
23
|
+
* Modifiers (visible, steady, inFrame, inAllFrames) are immutable — they
|
|
24
|
+
* return a new Locator and leave the original untouched, so it's safe to
|
|
25
|
+
* share a base locator across calls.
|
|
26
|
+
*/
|
|
27
|
+
export declare class Locator {
|
|
28
|
+
readonly selector: string;
|
|
29
|
+
readonly jsExpression: string;
|
|
30
|
+
readonly backendNodeId: number;
|
|
31
|
+
readonly frameId: string;
|
|
32
|
+
readonly visibleFlag?: boolean;
|
|
33
|
+
readonly steadyMs?: number;
|
|
34
|
+
readonly x?: number;
|
|
35
|
+
readonly y?: number;
|
|
36
|
+
private constructor();
|
|
37
|
+
/** Internal — clone the locator with one or more fields overridden. */
|
|
38
|
+
private with;
|
|
39
|
+
/**
|
|
40
|
+
* Enforces or disables the visibility check for this Locator's wait
|
|
41
|
+
* condition.
|
|
42
|
+
*
|
|
43
|
+
* Pass `false` to opt out of the default `DefaultVisible` (true). Has
|
|
44
|
+
* no effect when used as an action target — actions never check
|
|
45
|
+
* visibility before dispatching.
|
|
46
|
+
*
|
|
47
|
+
* @param v - true to require visibility, false to skip the check
|
|
48
|
+
*
|
|
49
|
+
* @returns a new Locator with the override applied
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* await browser.wait(css("#hidden").visible(false));
|
|
53
|
+
*/
|
|
54
|
+
visible(v: boolean): Locator;
|
|
55
|
+
/**
|
|
56
|
+
* Requires the element to keep a stable position and size for at least
|
|
57
|
+
* `ms` milliseconds before the wait matches.
|
|
58
|
+
*
|
|
59
|
+
* Pass 0 to disable the default `DefaultSteadyMs` (500). Has no effect
|
|
60
|
+
* for js() expressions that return a non-Element value, nor when used
|
|
61
|
+
* as an action target.
|
|
62
|
+
*
|
|
63
|
+
* @param ms - steady-state duration in milliseconds; 0 disables
|
|
64
|
+
*
|
|
65
|
+
* @returns a new Locator with the override applied
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* await browser.wait(css(".banner").steady(0));
|
|
69
|
+
*/
|
|
70
|
+
steady(ms: number): Locator;
|
|
71
|
+
/**
|
|
72
|
+
* Scopes this Locator to a specific frameId.
|
|
73
|
+
*
|
|
74
|
+
* Use the frameId from a previous result or {@link CloudBrowser.getPages}
|
|
75
|
+
* to target elements inside a known iframe.
|
|
76
|
+
*
|
|
77
|
+
* @param frameId - id of the frame to scope to
|
|
78
|
+
*
|
|
79
|
+
* @returns a new Locator scoped to that frame
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* const pages = await browser.getPages();
|
|
83
|
+
* const iframeId = pages[0].frameTree.children[0].frameId;
|
|
84
|
+
* await browser.click(css("button").inFrame(iframeId));
|
|
85
|
+
*/
|
|
86
|
+
inFrame(frameId: string): Locator;
|
|
87
|
+
/**
|
|
88
|
+
* Scopes this Locator to every frame.
|
|
89
|
+
*
|
|
90
|
+
* Equivalent to `.inFrame(AllFrames)`. Use this when an element might
|
|
91
|
+
* appear inside any of several frames and you do not want to enumerate
|
|
92
|
+
* them.
|
|
93
|
+
*
|
|
94
|
+
* @returns a new Locator scoped to all frames
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* await browser.wait(css("button.consent").inAllFrames());
|
|
98
|
+
*/
|
|
99
|
+
inAllFrames(): Locator;
|
|
100
|
+
/** @internal — used by action methods to validate at send time. */
|
|
101
|
+
validateTarget(cmd: string, allowCoords: boolean): void;
|
|
102
|
+
/** @internal */
|
|
103
|
+
static _css(selector: string): Locator;
|
|
104
|
+
/** @internal */
|
|
105
|
+
static _js(expression: string): Locator;
|
|
106
|
+
/** @internal */
|
|
107
|
+
static _node(backendNodeId: number): Locator;
|
|
108
|
+
/** @internal */
|
|
109
|
+
static _at(x: number, y: number): Locator;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* css waits for / targets an element matching the given CSS selector.
|
|
113
|
+
*
|
|
114
|
+
* When used in {@link CloudBrowser.wait}/{@link CloudBrowser.waitAny}, the
|
|
115
|
+
* returned Locator carries the SDK defaults `DefaultVisible` (true) and
|
|
116
|
+
* `DefaultSteadyMs` (500). Override per call with `.visible(false)` /
|
|
117
|
+
* `.steady(ms)` (use `.steady(0)` to disable the steady check).
|
|
118
|
+
*
|
|
119
|
+
* When used as an action target (click, fill, …) the visible/steady
|
|
120
|
+
* fields are ignored — there are no corresponding fields on the action
|
|
121
|
+
* requests.
|
|
122
|
+
*
|
|
123
|
+
* @param selector - CSS selector matching the element
|
|
124
|
+
*
|
|
125
|
+
* @returns Locator usable as a wait condition or as an action target
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* // As a wait condition.
|
|
129
|
+
* await browser.wait(css("button.submit"));
|
|
130
|
+
* // As an action target.
|
|
131
|
+
* await browser.click(css("button.submit"));
|
|
132
|
+
*/
|
|
133
|
+
export declare function css(selector: string): Locator;
|
|
134
|
+
/**
|
|
135
|
+
* js waits for / targets the result of a JavaScript expression.
|
|
136
|
+
*
|
|
137
|
+
* Same wait defaults as {@link css} (`DefaultVisible`=true,
|
|
138
|
+
* `DefaultSteadyMs`=500); these only apply when the expression returns a
|
|
139
|
+
* DOM Element. For non-Element truthy values (boolean, string, number,
|
|
140
|
+
* plain object) both fields are no-ops and the condition matches as soon
|
|
141
|
+
* as the value is truthy. Use `.visible(false)` / `.steady(0)` to opt out.
|
|
142
|
+
*
|
|
143
|
+
* @param expression - JavaScript expression evaluated in the target frame
|
|
144
|
+
*
|
|
145
|
+
* @returns Locator usable as a wait condition or as an action target
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* await browser.wait(js("window.__ready === true"));
|
|
149
|
+
*/
|
|
150
|
+
export declare function js(expression: string): Locator;
|
|
151
|
+
/**
|
|
152
|
+
* node targets an element by its DevTools backendNodeId.
|
|
153
|
+
*
|
|
154
|
+
* Use this when you already have a backendNodeId from a previous result
|
|
155
|
+
* (e.g. a wait or evaluate result) and want to act on the exact same
|
|
156
|
+
* element without re-resolving by selector. Action-only — using it in
|
|
157
|
+
* wait() throws at send time.
|
|
158
|
+
*
|
|
159
|
+
* @param backendNodeId - DevTools backendNodeId of the target element
|
|
160
|
+
*
|
|
161
|
+
* @returns Locator usable only as an action target
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* const r = await browser.click(css("button.open"));
|
|
165
|
+
* await browser.click(node(r.backendNodeId));
|
|
166
|
+
*/
|
|
167
|
+
export declare function node(backendNodeId: number): Locator;
|
|
168
|
+
/**
|
|
169
|
+
* at targets viewport coordinates instead of an element.
|
|
170
|
+
*
|
|
171
|
+
* Useful for clicking inside a canvas, hovering decorative regions, or
|
|
172
|
+
* dispatching events at synthetic positions. Action-only — using it in
|
|
173
|
+
* wait() throws at send time. Note that only click and moveTo accept at;
|
|
174
|
+
* scrollTo, drag, fill and select all require a real element.
|
|
175
|
+
*
|
|
176
|
+
* @param x - viewport-relative x in CSS pixels
|
|
177
|
+
* @param y - viewport-relative y in CSS pixels
|
|
178
|
+
*
|
|
179
|
+
* @returns Locator usable only as an action target
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* // Click at canvas-relative coordinates.
|
|
183
|
+
* await browser.click(at(120, 240));
|
|
184
|
+
*/
|
|
185
|
+
export declare function at(x: number, y: number): Locator;
|
|
186
|
+
/**
|
|
187
|
+
* @internal — returns the frame to send on the wire: opts.inFrame wins
|
|
188
|
+
* over the locator's own frameId. Empty everywhere → undefined (server
|
|
189
|
+
* uses main frame).
|
|
190
|
+
*/
|
|
191
|
+
export declare function pickFrame(optsFrame: string | undefined, locator: Locator): string | undefined;
|
package/dist/locator.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { DefaultSteadyMs, DefaultVisible } from "./defaults.js";
|
|
2
|
+
import { WRCError } from "./errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* AllFrames is the WRC.pdl sentinel for "match in every frame on the page".
|
|
5
|
+
* Use it for any field documented as accepting a frameId — both
|
|
6
|
+
* Locator.inFrame and *Opts.inFrame.
|
|
7
|
+
*/
|
|
8
|
+
export const AllFrames = "ALL_FRAMES";
|
|
9
|
+
/**
|
|
10
|
+
* Locator is the universal "what element / what condition" type. It is used
|
|
11
|
+
* both as a wait condition (passed to wait()/waitAny()) and as a target for
|
|
12
|
+
* element actions (passed to click(), fill(), …).
|
|
13
|
+
*
|
|
14
|
+
* Not every field is meaningful in every context:
|
|
15
|
+
* - selector / jsExpression → both wait and actions
|
|
16
|
+
* - backendNodeId → actions only (wait rejects it)
|
|
17
|
+
* - visible / steadyMs → wait only (silently ignored by actions)
|
|
18
|
+
* - x / y → actions only (wait rejects it)
|
|
19
|
+
* - frameId → both, may be overridden by call-level
|
|
20
|
+
* opts.inFrame
|
|
21
|
+
*
|
|
22
|
+
* Use the css() / js() / node() / at() constructors instead of building
|
|
23
|
+
* this class by hand.
|
|
24
|
+
*
|
|
25
|
+
* Modifiers (visible, steady, inFrame, inAllFrames) are immutable — they
|
|
26
|
+
* return a new Locator and leave the original untouched, so it's safe to
|
|
27
|
+
* share a base locator across calls.
|
|
28
|
+
*/
|
|
29
|
+
export class Locator {
|
|
30
|
+
constructor(init) {
|
|
31
|
+
this.selector = "";
|
|
32
|
+
this.jsExpression = "";
|
|
33
|
+
this.backendNodeId = 0;
|
|
34
|
+
this.frameId = "";
|
|
35
|
+
Object.assign(this, init);
|
|
36
|
+
}
|
|
37
|
+
/** Internal — clone the locator with one or more fields overridden. */
|
|
38
|
+
with(patch) {
|
|
39
|
+
return new Locator({
|
|
40
|
+
selector: this.selector,
|
|
41
|
+
jsExpression: this.jsExpression,
|
|
42
|
+
backendNodeId: this.backendNodeId,
|
|
43
|
+
frameId: this.frameId,
|
|
44
|
+
visibleFlag: this.visibleFlag,
|
|
45
|
+
steadyMs: this.steadyMs,
|
|
46
|
+
x: this.x,
|
|
47
|
+
y: this.y,
|
|
48
|
+
...patch,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// ── Modifiers (return new Locator) ──────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Enforces or disables the visibility check for this Locator's wait
|
|
54
|
+
* condition.
|
|
55
|
+
*
|
|
56
|
+
* Pass `false` to opt out of the default `DefaultVisible` (true). Has
|
|
57
|
+
* no effect when used as an action target — actions never check
|
|
58
|
+
* visibility before dispatching.
|
|
59
|
+
*
|
|
60
|
+
* @param v - true to require visibility, false to skip the check
|
|
61
|
+
*
|
|
62
|
+
* @returns a new Locator with the override applied
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* await browser.wait(css("#hidden").visible(false));
|
|
66
|
+
*/
|
|
67
|
+
visible(v) {
|
|
68
|
+
return this.with({ visibleFlag: v });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Requires the element to keep a stable position and size for at least
|
|
72
|
+
* `ms` milliseconds before the wait matches.
|
|
73
|
+
*
|
|
74
|
+
* Pass 0 to disable the default `DefaultSteadyMs` (500). Has no effect
|
|
75
|
+
* for js() expressions that return a non-Element value, nor when used
|
|
76
|
+
* as an action target.
|
|
77
|
+
*
|
|
78
|
+
* @param ms - steady-state duration in milliseconds; 0 disables
|
|
79
|
+
*
|
|
80
|
+
* @returns a new Locator with the override applied
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* await browser.wait(css(".banner").steady(0));
|
|
84
|
+
*/
|
|
85
|
+
steady(ms) {
|
|
86
|
+
return this.with({ steadyMs: ms });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Scopes this Locator to a specific frameId.
|
|
90
|
+
*
|
|
91
|
+
* Use the frameId from a previous result or {@link CloudBrowser.getPages}
|
|
92
|
+
* to target elements inside a known iframe.
|
|
93
|
+
*
|
|
94
|
+
* @param frameId - id of the frame to scope to
|
|
95
|
+
*
|
|
96
|
+
* @returns a new Locator scoped to that frame
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const pages = await browser.getPages();
|
|
100
|
+
* const iframeId = pages[0].frameTree.children[0].frameId;
|
|
101
|
+
* await browser.click(css("button").inFrame(iframeId));
|
|
102
|
+
*/
|
|
103
|
+
inFrame(frameId) {
|
|
104
|
+
return this.with({ frameId });
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Scopes this Locator to every frame.
|
|
108
|
+
*
|
|
109
|
+
* Equivalent to `.inFrame(AllFrames)`. Use this when an element might
|
|
110
|
+
* appear inside any of several frames and you do not want to enumerate
|
|
111
|
+
* them.
|
|
112
|
+
*
|
|
113
|
+
* @returns a new Locator scoped to all frames
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* await browser.wait(css("button.consent").inAllFrames());
|
|
117
|
+
*/
|
|
118
|
+
inAllFrames() {
|
|
119
|
+
return this.with({ frameId: AllFrames });
|
|
120
|
+
}
|
|
121
|
+
/** @internal — used by action methods to validate at send time. */
|
|
122
|
+
validateTarget(cmd, allowCoords) {
|
|
123
|
+
const hasCoords = this.x !== undefined && this.y !== undefined;
|
|
124
|
+
const hasElement = this.selector !== "" || this.jsExpression !== "" || this.backendNodeId !== 0;
|
|
125
|
+
if (!hasElement && !hasCoords) {
|
|
126
|
+
throw new WRCError(`wrc.${cmd}: target must have selector, JS expression, backendNodeId, or at(x,y)`);
|
|
127
|
+
}
|
|
128
|
+
if (hasCoords && !allowCoords) {
|
|
129
|
+
throw new WRCError(`wrc.${cmd}: at(x,y) is not supported here — use an element locator`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// ── Constructors (top-level functions re-export these) ──────────────
|
|
133
|
+
/** @internal */
|
|
134
|
+
static _css(selector) {
|
|
135
|
+
return new Locator({
|
|
136
|
+
selector,
|
|
137
|
+
visibleFlag: DefaultVisible,
|
|
138
|
+
steadyMs: DefaultSteadyMs,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** @internal */
|
|
142
|
+
static _js(expression) {
|
|
143
|
+
return new Locator({
|
|
144
|
+
jsExpression: expression,
|
|
145
|
+
visibleFlag: DefaultVisible,
|
|
146
|
+
steadyMs: DefaultSteadyMs,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/** @internal */
|
|
150
|
+
static _node(backendNodeId) {
|
|
151
|
+
return new Locator({ backendNodeId });
|
|
152
|
+
}
|
|
153
|
+
/** @internal */
|
|
154
|
+
static _at(x, y) {
|
|
155
|
+
return new Locator({ x, y });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
159
|
+
// Top-level constructors (preferred over Locator.* statics)
|
|
160
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* css waits for / targets an element matching the given CSS selector.
|
|
163
|
+
*
|
|
164
|
+
* When used in {@link CloudBrowser.wait}/{@link CloudBrowser.waitAny}, the
|
|
165
|
+
* returned Locator carries the SDK defaults `DefaultVisible` (true) and
|
|
166
|
+
* `DefaultSteadyMs` (500). Override per call with `.visible(false)` /
|
|
167
|
+
* `.steady(ms)` (use `.steady(0)` to disable the steady check).
|
|
168
|
+
*
|
|
169
|
+
* When used as an action target (click, fill, …) the visible/steady
|
|
170
|
+
* fields are ignored — there are no corresponding fields on the action
|
|
171
|
+
* requests.
|
|
172
|
+
*
|
|
173
|
+
* @param selector - CSS selector matching the element
|
|
174
|
+
*
|
|
175
|
+
* @returns Locator usable as a wait condition or as an action target
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* // As a wait condition.
|
|
179
|
+
* await browser.wait(css("button.submit"));
|
|
180
|
+
* // As an action target.
|
|
181
|
+
* await browser.click(css("button.submit"));
|
|
182
|
+
*/
|
|
183
|
+
export function css(selector) {
|
|
184
|
+
return Locator._css(selector);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* js waits for / targets the result of a JavaScript expression.
|
|
188
|
+
*
|
|
189
|
+
* Same wait defaults as {@link css} (`DefaultVisible`=true,
|
|
190
|
+
* `DefaultSteadyMs`=500); these only apply when the expression returns a
|
|
191
|
+
* DOM Element. For non-Element truthy values (boolean, string, number,
|
|
192
|
+
* plain object) both fields are no-ops and the condition matches as soon
|
|
193
|
+
* as the value is truthy. Use `.visible(false)` / `.steady(0)` to opt out.
|
|
194
|
+
*
|
|
195
|
+
* @param expression - JavaScript expression evaluated in the target frame
|
|
196
|
+
*
|
|
197
|
+
* @returns Locator usable as a wait condition or as an action target
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* await browser.wait(js("window.__ready === true"));
|
|
201
|
+
*/
|
|
202
|
+
export function js(expression) {
|
|
203
|
+
return Locator._js(expression);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* node targets an element by its DevTools backendNodeId.
|
|
207
|
+
*
|
|
208
|
+
* Use this when you already have a backendNodeId from a previous result
|
|
209
|
+
* (e.g. a wait or evaluate result) and want to act on the exact same
|
|
210
|
+
* element without re-resolving by selector. Action-only — using it in
|
|
211
|
+
* wait() throws at send time.
|
|
212
|
+
*
|
|
213
|
+
* @param backendNodeId - DevTools backendNodeId of the target element
|
|
214
|
+
*
|
|
215
|
+
* @returns Locator usable only as an action target
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* const r = await browser.click(css("button.open"));
|
|
219
|
+
* await browser.click(node(r.backendNodeId));
|
|
220
|
+
*/
|
|
221
|
+
export function node(backendNodeId) {
|
|
222
|
+
return Locator._node(backendNodeId);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* at targets viewport coordinates instead of an element.
|
|
226
|
+
*
|
|
227
|
+
* Useful for clicking inside a canvas, hovering decorative regions, or
|
|
228
|
+
* dispatching events at synthetic positions. Action-only — using it in
|
|
229
|
+
* wait() throws at send time. Note that only click and moveTo accept at;
|
|
230
|
+
* scrollTo, drag, fill and select all require a real element.
|
|
231
|
+
*
|
|
232
|
+
* @param x - viewport-relative x in CSS pixels
|
|
233
|
+
* @param y - viewport-relative y in CSS pixels
|
|
234
|
+
*
|
|
235
|
+
* @returns Locator usable only as an action target
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* // Click at canvas-relative coordinates.
|
|
239
|
+
* await browser.click(at(120, 240));
|
|
240
|
+
*/
|
|
241
|
+
export function at(x, y) {
|
|
242
|
+
return Locator._at(x, y);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* @internal — returns the frame to send on the wire: opts.inFrame wins
|
|
246
|
+
* over the locator's own frameId. Empty everywhere → undefined (server
|
|
247
|
+
* uses main frame).
|
|
248
|
+
*/
|
|
249
|
+
export function pickFrame(optsFrame, locator) {
|
|
250
|
+
if (optsFrame)
|
|
251
|
+
return optsFrame;
|
|
252
|
+
if (locator.frameId)
|
|
253
|
+
return locator.frameId;
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|