@ricsam/isolate-playwright 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +254 -15
- package/dist/cjs/client.cjs +5 -318
- package/dist/cjs/client.cjs.map +3 -3
- package/dist/cjs/handler.cjs +1406 -0
- package/dist/cjs/handler.cjs.map +10 -0
- package/dist/cjs/index.cjs +793 -576
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/types.cjs +14 -1
- package/dist/cjs/types.cjs.map +4 -3
- package/dist/mjs/client.mjs +8 -317
- package/dist/mjs/client.mjs.map +3 -3
- package/dist/mjs/handler.mjs +1378 -0
- package/dist/mjs/handler.mjs.map +10 -0
- package/dist/mjs/index.mjs +802 -576
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/types.mjs +6 -1
- package/dist/mjs/types.mjs.map +4 -3
- package/dist/types/client.d.ts +3 -13
- package/dist/types/handler.d.ts +44 -0
- package/dist/types/index.d.ts +7 -72
- package/dist/types/types.d.ts +65 -11
- package/package.json +1 -1
package/dist/mjs/index.mjs
CHANGED
|
@@ -1,445 +1,151 @@
|
|
|
1
1
|
// packages/playwright/src/index.ts
|
|
2
2
|
import ivm from "isolated-vm";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
case "css":
|
|
17
|
-
locator = page.locator(selectorValue);
|
|
18
|
-
break;
|
|
19
|
-
case "role":
|
|
20
|
-
locator = page.getByRole(selectorValue, roleOptions && Object.keys(roleOptions).length > 0 ? roleOptions : undefined);
|
|
21
|
-
break;
|
|
22
|
-
case "text":
|
|
23
|
-
locator = page.getByText(selectorValue);
|
|
24
|
-
break;
|
|
25
|
-
case "label":
|
|
26
|
-
locator = page.getByLabel(selectorValue);
|
|
27
|
-
break;
|
|
28
|
-
case "placeholder":
|
|
29
|
-
locator = page.getByPlaceholder(selectorValue);
|
|
30
|
-
break;
|
|
31
|
-
case "testId":
|
|
32
|
-
locator = page.getByTestId(selectorValue);
|
|
33
|
-
break;
|
|
34
|
-
case "or": {
|
|
35
|
-
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
36
|
-
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
37
|
-
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
38
|
-
locator = first.or(second);
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
default:
|
|
42
|
-
locator = page.locator(selectorValue);
|
|
43
|
-
}
|
|
44
|
-
if (nthIndex !== undefined) {
|
|
45
|
-
locator = locator.nth(nthIndex);
|
|
46
|
-
}
|
|
47
|
-
if (options?.filter) {
|
|
48
|
-
const filterOpts = { ...options.filter };
|
|
49
|
-
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
50
|
-
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
51
|
-
}
|
|
52
|
-
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
53
|
-
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
54
|
-
}
|
|
55
|
-
locator = locator.filter(filterOpts);
|
|
56
|
-
}
|
|
57
|
-
return locator;
|
|
58
|
-
}
|
|
59
|
-
async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
60
|
-
switch (action) {
|
|
61
|
-
case "click":
|
|
62
|
-
await locator.click({ timeout });
|
|
63
|
-
return null;
|
|
64
|
-
case "dblclick":
|
|
65
|
-
await locator.dblclick({ timeout });
|
|
66
|
-
return null;
|
|
67
|
-
case "fill":
|
|
68
|
-
await locator.fill(String(actionArg ?? ""), { timeout });
|
|
69
|
-
return null;
|
|
70
|
-
case "type":
|
|
71
|
-
await locator.pressSequentially(String(actionArg ?? ""), { timeout });
|
|
72
|
-
return null;
|
|
73
|
-
case "check":
|
|
74
|
-
await locator.check({ timeout });
|
|
75
|
-
return null;
|
|
76
|
-
case "uncheck":
|
|
77
|
-
await locator.uncheck({ timeout });
|
|
78
|
-
return null;
|
|
79
|
-
case "selectOption":
|
|
80
|
-
await locator.selectOption(String(actionArg ?? ""), { timeout });
|
|
81
|
-
return null;
|
|
82
|
-
case "clear":
|
|
83
|
-
await locator.clear({ timeout });
|
|
84
|
-
return null;
|
|
85
|
-
case "press":
|
|
86
|
-
await locator.press(String(actionArg ?? ""), { timeout });
|
|
87
|
-
return null;
|
|
88
|
-
case "hover":
|
|
89
|
-
await locator.hover({ timeout });
|
|
90
|
-
return null;
|
|
91
|
-
case "focus":
|
|
92
|
-
await locator.focus({ timeout });
|
|
93
|
-
return null;
|
|
94
|
-
case "getText":
|
|
95
|
-
return await locator.textContent({ timeout });
|
|
96
|
-
case "getValue":
|
|
97
|
-
return await locator.inputValue({ timeout });
|
|
98
|
-
case "isVisible":
|
|
99
|
-
return await locator.isVisible();
|
|
100
|
-
case "isEnabled":
|
|
101
|
-
return await locator.isEnabled();
|
|
102
|
-
case "isChecked":
|
|
103
|
-
return await locator.isChecked();
|
|
104
|
-
case "count":
|
|
105
|
-
return await locator.count();
|
|
106
|
-
case "getAttribute":
|
|
107
|
-
return await locator.getAttribute(String(actionArg ?? ""), { timeout });
|
|
108
|
-
case "isDisabled":
|
|
109
|
-
return await locator.isDisabled();
|
|
110
|
-
case "isHidden":
|
|
111
|
-
return await locator.isHidden();
|
|
112
|
-
case "innerHTML":
|
|
113
|
-
return await locator.innerHTML({ timeout });
|
|
114
|
-
case "innerText":
|
|
115
|
-
return await locator.innerText({ timeout });
|
|
116
|
-
case "allTextContents":
|
|
117
|
-
return await locator.allTextContents();
|
|
118
|
-
case "allInnerTexts":
|
|
119
|
-
return await locator.allInnerTexts();
|
|
120
|
-
case "waitFor": {
|
|
121
|
-
const opts = actionArg && typeof actionArg === "object" ? actionArg : {};
|
|
122
|
-
await locator.waitFor({ state: opts.state, timeout: opts.timeout ?? timeout });
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
case "boundingBox":
|
|
126
|
-
return await locator.boundingBox({ timeout });
|
|
127
|
-
default:
|
|
128
|
-
throw new Error(`Unknown action: ${action}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
async function executeExpectAssertion(locator, matcher, expected, negated, timeout) {
|
|
132
|
-
switch (matcher) {
|
|
133
|
-
case "toBeVisible": {
|
|
134
|
-
const isVisible = await locator.isVisible();
|
|
135
|
-
if (negated) {
|
|
136
|
-
if (isVisible)
|
|
137
|
-
throw new Error("Expected element to not be visible, but it was visible");
|
|
138
|
-
} else {
|
|
139
|
-
if (!isVisible)
|
|
140
|
-
throw new Error("Expected element to be visible, but it was not");
|
|
141
|
-
}
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
case "toContainText": {
|
|
145
|
-
const text = await locator.textContent({ timeout });
|
|
146
|
-
let matches;
|
|
147
|
-
let expectedDisplay;
|
|
148
|
-
if (expected && typeof expected === "object" && expected.$regex) {
|
|
149
|
-
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
150
|
-
matches = regex.test(text ?? "");
|
|
151
|
-
expectedDisplay = String(regex);
|
|
152
|
-
} else {
|
|
153
|
-
matches = text?.includes(String(expected)) ?? false;
|
|
154
|
-
expectedDisplay = String(expected);
|
|
155
|
-
}
|
|
156
|
-
if (negated) {
|
|
157
|
-
if (matches)
|
|
158
|
-
throw new Error(`Expected text to not contain ${expectedDisplay}, but got "${text}"`);
|
|
159
|
-
} else {
|
|
160
|
-
if (!matches)
|
|
161
|
-
throw new Error(`Expected text to contain ${expectedDisplay}, but got "${text}"`);
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
case "toHaveValue": {
|
|
166
|
-
const value = await locator.inputValue({ timeout });
|
|
167
|
-
const matches = value === String(expected);
|
|
168
|
-
if (negated) {
|
|
169
|
-
if (matches)
|
|
170
|
-
throw new Error(`Expected value to not be "${expected}", but it was`);
|
|
171
|
-
} else {
|
|
172
|
-
if (!matches)
|
|
173
|
-
throw new Error(`Expected value to be "${expected}", but got "${value}"`);
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
case "toBeEnabled": {
|
|
178
|
-
const isEnabled = await locator.isEnabled();
|
|
179
|
-
if (negated) {
|
|
180
|
-
if (isEnabled)
|
|
181
|
-
throw new Error("Expected element to be disabled, but it was enabled");
|
|
182
|
-
} else {
|
|
183
|
-
if (!isEnabled)
|
|
184
|
-
throw new Error("Expected element to be enabled, but it was disabled");
|
|
185
|
-
}
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
case "toBeChecked": {
|
|
189
|
-
const isChecked = await locator.isChecked();
|
|
190
|
-
if (negated) {
|
|
191
|
-
if (isChecked)
|
|
192
|
-
throw new Error("Expected element to not be checked, but it was checked");
|
|
193
|
-
} else {
|
|
194
|
-
if (!isChecked)
|
|
195
|
-
throw new Error("Expected element to be checked, but it was not");
|
|
196
|
-
}
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
case "toHaveAttribute": {
|
|
200
|
-
const { name, value } = expected;
|
|
201
|
-
const actual = await locator.getAttribute(name, { timeout });
|
|
202
|
-
if (value instanceof RegExp || value && typeof value === "object" && value.$regex) {
|
|
203
|
-
const regex = value.$regex ? new RegExp(value.$regex, value.$flags) : value;
|
|
204
|
-
const matches = regex.test(actual ?? "");
|
|
205
|
-
if (negated) {
|
|
206
|
-
if (matches)
|
|
207
|
-
throw new Error(`Expected attribute "${name}" to not match ${regex}, but got "${actual}"`);
|
|
208
|
-
} else {
|
|
209
|
-
if (!matches)
|
|
210
|
-
throw new Error(`Expected attribute "${name}" to match ${regex}, but got "${actual}"`);
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
const matches = actual === String(value);
|
|
214
|
-
if (negated) {
|
|
215
|
-
if (matches)
|
|
216
|
-
throw new Error(`Expected attribute "${name}" to not be "${value}", but it was`);
|
|
217
|
-
} else {
|
|
218
|
-
if (!matches)
|
|
219
|
-
throw new Error(`Expected attribute "${name}" to be "${value}", but got "${actual}"`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
case "toHaveText": {
|
|
225
|
-
const text = await locator.textContent({ timeout }) ?? "";
|
|
226
|
-
let matches;
|
|
227
|
-
let expectedDisplay;
|
|
228
|
-
if (expected && typeof expected === "object" && expected.$regex) {
|
|
229
|
-
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
230
|
-
matches = regex.test(text);
|
|
231
|
-
expectedDisplay = String(regex);
|
|
232
|
-
} else {
|
|
233
|
-
matches = text === String(expected);
|
|
234
|
-
expectedDisplay = JSON.stringify(expected);
|
|
235
|
-
}
|
|
236
|
-
if (negated) {
|
|
237
|
-
if (matches)
|
|
238
|
-
throw new Error(`Expected text to not be ${expectedDisplay}, but got "${text}"`);
|
|
239
|
-
} else {
|
|
240
|
-
if (!matches)
|
|
241
|
-
throw new Error(`Expected text to be ${expectedDisplay}, but got "${text}"`);
|
|
242
|
-
}
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
case "toHaveCount": {
|
|
246
|
-
const count = await locator.count();
|
|
247
|
-
const expectedCount = Number(expected);
|
|
248
|
-
if (negated) {
|
|
249
|
-
if (count === expectedCount)
|
|
250
|
-
throw new Error(`Expected count to not be ${expectedCount}, but it was`);
|
|
251
|
-
} else {
|
|
252
|
-
if (count !== expectedCount)
|
|
253
|
-
throw new Error(`Expected count to be ${expectedCount}, but got ${count}`);
|
|
254
|
-
}
|
|
255
|
-
break;
|
|
256
|
-
}
|
|
257
|
-
case "toBeHidden": {
|
|
258
|
-
const isHidden = await locator.isHidden();
|
|
259
|
-
if (negated) {
|
|
260
|
-
if (isHidden)
|
|
261
|
-
throw new Error("Expected element to not be hidden, but it was hidden");
|
|
262
|
-
} else {
|
|
263
|
-
if (!isHidden)
|
|
264
|
-
throw new Error("Expected element to be hidden, but it was not");
|
|
265
|
-
}
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
case "toBeDisabled": {
|
|
269
|
-
const isDisabled = await locator.isDisabled();
|
|
270
|
-
if (negated) {
|
|
271
|
-
if (isDisabled)
|
|
272
|
-
throw new Error("Expected element to not be disabled, but it was disabled");
|
|
273
|
-
} else {
|
|
274
|
-
if (!isDisabled)
|
|
275
|
-
throw new Error("Expected element to be disabled, but it was not");
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
case "toBeFocused": {
|
|
280
|
-
const isFocused = await locator.evaluate((el) => document.activeElement === el).catch(() => false);
|
|
281
|
-
if (negated) {
|
|
282
|
-
if (isFocused)
|
|
283
|
-
throw new Error("Expected element to not be focused, but it was focused");
|
|
284
|
-
} else {
|
|
285
|
-
if (!isFocused)
|
|
286
|
-
throw new Error("Expected element to be focused, but it was not");
|
|
287
|
-
}
|
|
288
|
-
break;
|
|
289
|
-
}
|
|
290
|
-
case "toBeEmpty": {
|
|
291
|
-
const text = await locator.textContent({ timeout });
|
|
292
|
-
const value = await locator.inputValue({ timeout }).catch(() => null);
|
|
293
|
-
const isEmpty = value !== null ? value === "" : (text ?? "") === "";
|
|
294
|
-
if (negated) {
|
|
295
|
-
if (isEmpty)
|
|
296
|
-
throw new Error("Expected element to not be empty, but it was");
|
|
297
|
-
} else {
|
|
298
|
-
if (!isEmpty)
|
|
299
|
-
throw new Error("Expected element to be empty, but it was not");
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
default:
|
|
304
|
-
throw new Error(`Unknown matcher: ${matcher}`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
function createPlaywrightHandler(page, options) {
|
|
308
|
-
const timeout = options?.timeout ?? 30000;
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_PLAYWRIGHT_HANDLER_META
|
|
5
|
+
} from "./types.mjs";
|
|
6
|
+
import {
|
|
7
|
+
createPlaywrightHandler,
|
|
8
|
+
defaultPlaywrightHandler,
|
|
9
|
+
getDefaultPlaywrightHandlerMetadata
|
|
10
|
+
} from "./handler.mjs";
|
|
11
|
+
import {
|
|
12
|
+
createPlaywrightHandler as createPlaywrightHandler2,
|
|
13
|
+
getDefaultPlaywrightHandlerMetadata as getDefaultPlaywrightHandlerMetadata2
|
|
14
|
+
} from "./handler.mjs";
|
|
15
|
+
function wrapHandlerWithPredicateSupport(handler, evaluatePredicate, defaultTimeout) {
|
|
309
16
|
return async (op) => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
case "content":
|
|
328
|
-
return { ok: true, value: await page.content() };
|
|
329
|
-
case "waitForSelector": {
|
|
330
|
-
const [selector, optionsJson] = op.args;
|
|
331
|
-
const opts = optionsJson ? JSON.parse(optionsJson) : {};
|
|
332
|
-
await page.waitForSelector(selector, { timeout, ...opts });
|
|
333
|
-
return { ok: true };
|
|
334
|
-
}
|
|
335
|
-
case "waitForTimeout": {
|
|
336
|
-
const [ms] = op.args;
|
|
337
|
-
await page.waitForTimeout(ms);
|
|
338
|
-
return { ok: true };
|
|
339
|
-
}
|
|
340
|
-
case "waitForLoadState": {
|
|
341
|
-
const [state] = op.args;
|
|
342
|
-
await page.waitForLoadState(state ?? "load", { timeout });
|
|
343
|
-
return { ok: true };
|
|
344
|
-
}
|
|
345
|
-
case "evaluate": {
|
|
346
|
-
const [script, arg] = op.args;
|
|
347
|
-
if (op.args.length > 1) {
|
|
348
|
-
const fn = new Function("return (" + script + ")")();
|
|
349
|
-
const result2 = await page.evaluate(fn, arg);
|
|
350
|
-
return { ok: true, value: result2 };
|
|
351
|
-
}
|
|
352
|
-
const result = await page.evaluate(script);
|
|
353
|
-
return { ok: true, value: result };
|
|
354
|
-
}
|
|
355
|
-
case "locatorAction": {
|
|
356
|
-
const [selectorType, selectorValue, roleOptions, action, actionArg] = op.args;
|
|
357
|
-
const locator = getLocator(page, selectorType, selectorValue, roleOptions);
|
|
358
|
-
const result = await executeLocatorAction(locator, action, actionArg, timeout);
|
|
359
|
-
return { ok: true, value: result };
|
|
360
|
-
}
|
|
361
|
-
case "expectLocator": {
|
|
362
|
-
const [selectorType, selectorValue, roleOptions, matcher, expected, negated, customTimeout] = op.args;
|
|
363
|
-
const locator = getLocator(page, selectorType, selectorValue, roleOptions);
|
|
364
|
-
const effectiveTimeout = customTimeout ?? timeout;
|
|
365
|
-
await executeExpectAssertion(locator, matcher, expected, negated ?? false, effectiveTimeout);
|
|
366
|
-
return { ok: true };
|
|
367
|
-
}
|
|
368
|
-
case "request": {
|
|
369
|
-
const [url, method, data, headers] = op.args;
|
|
370
|
-
const requestOptions = {
|
|
371
|
-
timeout
|
|
372
|
-
};
|
|
373
|
-
if (headers) {
|
|
374
|
-
requestOptions.headers = headers;
|
|
17
|
+
switch (op.type) {
|
|
18
|
+
case "waitForURLPredicate": {
|
|
19
|
+
const [predicateId, customTimeout, waitUntil] = op.args;
|
|
20
|
+
const effectiveTimeout = customTimeout ?? defaultTimeout;
|
|
21
|
+
const startTime = Date.now();
|
|
22
|
+
const pollInterval = 100;
|
|
23
|
+
while (true) {
|
|
24
|
+
const urlResult = await handler({ type: "url", args: [], pageId: op.pageId, contextId: op.contextId });
|
|
25
|
+
if (urlResult.ok) {
|
|
26
|
+
try {
|
|
27
|
+
if (evaluatePredicate(predicateId, urlResult.value)) {
|
|
28
|
+
return { ok: true };
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {
|
|
31
|
+
const error = e;
|
|
32
|
+
return { ok: false, error: { name: error.name, message: error.message } };
|
|
33
|
+
}
|
|
375
34
|
}
|
|
376
|
-
if (
|
|
377
|
-
|
|
35
|
+
if (effectiveTimeout > 0 && Date.now() - startTime >= effectiveTimeout) {
|
|
36
|
+
return { ok: false, error: { name: "Error", message: `Timeout ${effectiveTimeout}ms exceeded waiting for URL` } };
|
|
378
37
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
38
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
case "waitForResponsePredicateFinish": {
|
|
42
|
+
const [initialListenerId, predicateId, customTimeout] = op.args;
|
|
43
|
+
const effectiveTimeout = customTimeout ?? defaultTimeout;
|
|
44
|
+
const broadMatcher = { type: "regex", value: { $regex: ".*", $flags: "" } };
|
|
45
|
+
const startTime = Date.now();
|
|
46
|
+
let currentListenerId = initialListenerId;
|
|
47
|
+
while (true) {
|
|
48
|
+
const finishResult = await handler({
|
|
49
|
+
type: "waitForResponseFinish",
|
|
50
|
+
args: [currentListenerId],
|
|
51
|
+
pageId: op.pageId,
|
|
52
|
+
contextId: op.contextId
|
|
382
53
|
});
|
|
383
|
-
|
|
384
|
-
|
|
54
|
+
if (!finishResult.ok)
|
|
55
|
+
return finishResult;
|
|
56
|
+
const responseData = finishResult.value;
|
|
385
57
|
try {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
body: null
|
|
58
|
+
const serialized = {
|
|
59
|
+
method: "",
|
|
60
|
+
headers: Object.entries(responseData.headers || {}),
|
|
61
|
+
url: responseData.url,
|
|
62
|
+
status: responseData.status,
|
|
63
|
+
statusText: responseData.statusText,
|
|
64
|
+
body: responseData.text || ""
|
|
65
|
+
};
|
|
66
|
+
if (evaluatePredicate(predicateId, serialized)) {
|
|
67
|
+
return finishResult;
|
|
397
68
|
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
69
|
+
} catch (e) {
|
|
70
|
+
const error = e;
|
|
71
|
+
return { ok: false, error: { name: error.name, message: error.message } };
|
|
72
|
+
}
|
|
73
|
+
if (effectiveTimeout > 0 && Date.now() - startTime >= effectiveTimeout) {
|
|
74
|
+
return { ok: false, error: { name: "Error", message: `Timeout ${effectiveTimeout}ms exceeded waiting for response` } };
|
|
75
|
+
}
|
|
76
|
+
const remainingTimeout = effectiveTimeout > 0 ? Math.max(1, effectiveTimeout - (Date.now() - startTime)) : effectiveTimeout;
|
|
77
|
+
const nextStartResult = await handler({
|
|
78
|
+
type: "waitForResponseStart",
|
|
79
|
+
args: [broadMatcher, remainingTimeout],
|
|
80
|
+
pageId: op.pageId,
|
|
81
|
+
contextId: op.contextId
|
|
405
82
|
});
|
|
406
|
-
|
|
83
|
+
if (!nextStartResult.ok)
|
|
84
|
+
return nextStartResult;
|
|
85
|
+
currentListenerId = nextStartResult.value.listenerId;
|
|
407
86
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
87
|
+
}
|
|
88
|
+
case "waitForRequestPredicateFinish": {
|
|
89
|
+
const [initialListenerId, predicateId, customTimeout] = op.args;
|
|
90
|
+
const effectiveTimeout = customTimeout ?? defaultTimeout;
|
|
91
|
+
const broadMatcher = { type: "regex", value: { $regex: ".*", $flags: "" } };
|
|
92
|
+
const startTime = Date.now();
|
|
93
|
+
let currentListenerId = initialListenerId;
|
|
94
|
+
while (true) {
|
|
95
|
+
const finishResult = await handler({
|
|
96
|
+
type: "waitForRequestFinish",
|
|
97
|
+
args: [currentListenerId],
|
|
98
|
+
pageId: op.pageId,
|
|
99
|
+
contextId: op.contextId
|
|
413
100
|
});
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
101
|
+
if (!finishResult.ok)
|
|
102
|
+
return finishResult;
|
|
103
|
+
const requestData = finishResult.value;
|
|
104
|
+
try {
|
|
105
|
+
const serialized = {
|
|
106
|
+
method: requestData.method,
|
|
107
|
+
headers: Object.entries(requestData.headers || {}),
|
|
108
|
+
url: requestData.url,
|
|
109
|
+
body: requestData.postData || ""
|
|
110
|
+
};
|
|
111
|
+
if (evaluatePredicate(predicateId, serialized)) {
|
|
112
|
+
return finishResult;
|
|
113
|
+
}
|
|
114
|
+
} catch (e) {
|
|
115
|
+
const error = e;
|
|
116
|
+
return { ok: false, error: { name: error.name, message: error.message } };
|
|
117
|
+
}
|
|
118
|
+
if (effectiveTimeout > 0 && Date.now() - startTime >= effectiveTimeout) {
|
|
119
|
+
return { ok: false, error: { name: "Error", message: `Timeout ${effectiveTimeout}ms exceeded waiting for request` } };
|
|
120
|
+
}
|
|
121
|
+
const remainingTimeout = effectiveTimeout > 0 ? Math.max(1, effectiveTimeout - (Date.now() - startTime)) : effectiveTimeout;
|
|
122
|
+
const nextStartResult = await handler({
|
|
123
|
+
type: "waitForRequestStart",
|
|
124
|
+
args: [broadMatcher, remainingTimeout],
|
|
125
|
+
pageId: op.pageId,
|
|
126
|
+
contextId: op.contextId
|
|
421
127
|
});
|
|
422
|
-
|
|
128
|
+
if (!nextStartResult.ok)
|
|
129
|
+
return nextStartResult;
|
|
130
|
+
currentListenerId = nextStartResult.value.listenerId;
|
|
423
131
|
}
|
|
424
|
-
case "clearCookies": {
|
|
425
|
-
await page.context().clearCookies();
|
|
426
|
-
return { ok: true };
|
|
427
|
-
}
|
|
428
|
-
default:
|
|
429
|
-
return { ok: false, error: { name: "Error", message: `Unknown operation: ${op.type}` } };
|
|
430
132
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return { ok: false, error: { name: error.name, message: error.message } };
|
|
133
|
+
default:
|
|
134
|
+
return handler(op);
|
|
434
135
|
}
|
|
435
136
|
};
|
|
436
137
|
}
|
|
437
138
|
async function setupPlaywright(context, options) {
|
|
438
139
|
const timeout = options.timeout ?? 30000;
|
|
439
|
-
const
|
|
140
|
+
const explicitPage = "page" in options ? options.page : undefined;
|
|
440
141
|
const handler = "handler" in options ? options.handler : undefined;
|
|
441
|
-
const
|
|
442
|
-
|
|
142
|
+
const handlerMetadata = handler ? getDefaultPlaywrightHandlerMetadata2(handler) : undefined;
|
|
143
|
+
const page = explicitPage ?? handlerMetadata?.page;
|
|
144
|
+
const createPage = "createPage" in options ? options.createPage : undefined;
|
|
145
|
+
const createContext = "createContext" in options ? options.createContext : undefined;
|
|
146
|
+
const readFile = "readFile" in options ? options.readFile : undefined;
|
|
147
|
+
const writeFile = "writeFile" in options ? options.writeFile : undefined;
|
|
148
|
+
if (!handler && !page) {
|
|
443
149
|
throw new Error("Either page or handler must be provided to setupPlaywright");
|
|
444
150
|
}
|
|
445
151
|
const browserConsoleLogs = [];
|
|
@@ -518,15 +224,10 @@ async function setupPlaywright(context, options) {
|
|
|
518
224
|
page.on("response", responseHandler);
|
|
519
225
|
page.on("console", consoleHandler);
|
|
520
226
|
}
|
|
521
|
-
global.setSync("__Playwright_handler_ref", new ivm.Reference(async (opJson) => {
|
|
522
|
-
const op = JSON.parse(opJson);
|
|
523
|
-
const result = await effectiveHandler(op);
|
|
524
|
-
return JSON.stringify(result);
|
|
525
|
-
}));
|
|
526
227
|
context.evalSync(`
|
|
527
228
|
(function() {
|
|
528
|
-
globalThis.
|
|
529
|
-
const op = JSON.stringify({ type, args });
|
|
229
|
+
globalThis.__pw_invoke_sync = function(type, args, options) {
|
|
230
|
+
const op = JSON.stringify({ type, args, pageId: options?.pageId, contextId: options?.contextId });
|
|
530
231
|
const resultJson = __Playwright_handler_ref.applySyncPromise(undefined, [op]);
|
|
531
232
|
const result = JSON.parse(resultJson);
|
|
532
233
|
if (result.ok) {
|
|
@@ -537,215 +238,607 @@ async function setupPlaywright(context, options) {
|
|
|
537
238
|
throw error;
|
|
538
239
|
}
|
|
539
240
|
};
|
|
241
|
+
globalThis.__pw_invoke = async function(type, args, options) {
|
|
242
|
+
return globalThis.__pw_invoke_sync(type, args, options);
|
|
243
|
+
};
|
|
540
244
|
})();
|
|
541
245
|
`);
|
|
542
246
|
context.evalSync(`
|
|
543
247
|
(function() {
|
|
544
|
-
|
|
545
|
-
|
|
248
|
+
const __pw_predicates = new Map();
|
|
249
|
+
let __pw_next_id = 0;
|
|
250
|
+
globalThis.__pw_register_predicate = function(fn) {
|
|
251
|
+
const id = __pw_next_id++;
|
|
252
|
+
__pw_predicates.set(id, fn);
|
|
253
|
+
return id;
|
|
254
|
+
};
|
|
255
|
+
globalThis.__pw_unregister_predicate = function(id) {
|
|
256
|
+
__pw_predicates.delete(id);
|
|
257
|
+
};
|
|
258
|
+
globalThis.__pw_evaluate_predicate = function(id, data) {
|
|
259
|
+
const fn = __pw_predicates.get(id);
|
|
260
|
+
if (!fn) throw new Error('Predicate not found: ' + id);
|
|
261
|
+
const result = fn(data);
|
|
262
|
+
if (result && typeof result === 'object' && typeof result.then === 'function') {
|
|
263
|
+
throw new Error('Async predicates are not supported. Use a synchronous predicate function.');
|
|
264
|
+
}
|
|
265
|
+
return !!result;
|
|
266
|
+
};
|
|
267
|
+
})();
|
|
268
|
+
`);
|
|
269
|
+
const evaluatePredicateRef = context.global.getSync("__pw_evaluate_predicate", { reference: true });
|
|
270
|
+
const evaluatePredicateFn = (predicateId, data) => {
|
|
271
|
+
return evaluatePredicateRef.applySync(undefined, [new ivm.ExternalCopy(predicateId).copyInto(), new ivm.ExternalCopy(data).copyInto()]);
|
|
272
|
+
};
|
|
273
|
+
let effectiveHandler;
|
|
274
|
+
if (handler && handlerMetadata?.page) {
|
|
275
|
+
effectiveHandler = createPlaywrightHandler2(handlerMetadata.page, {
|
|
276
|
+
...handlerMetadata.options,
|
|
277
|
+
evaluatePredicate: evaluatePredicateFn
|
|
278
|
+
});
|
|
279
|
+
} else if (handler) {
|
|
280
|
+
effectiveHandler = wrapHandlerWithPredicateSupport(handler, evaluatePredicateFn, timeout);
|
|
281
|
+
} else if (page) {
|
|
282
|
+
effectiveHandler = createPlaywrightHandler2(page, {
|
|
283
|
+
timeout,
|
|
284
|
+
readFile,
|
|
285
|
+
writeFile,
|
|
286
|
+
createPage,
|
|
287
|
+
createContext,
|
|
288
|
+
evaluatePredicate: evaluatePredicateFn
|
|
289
|
+
});
|
|
290
|
+
} else {
|
|
291
|
+
throw new Error("Either page or handler must be provided to setupPlaywright");
|
|
292
|
+
}
|
|
293
|
+
global.setSync("__Playwright_handler_ref", new ivm.Reference(async (opJson) => {
|
|
294
|
+
const op = JSON.parse(opJson);
|
|
295
|
+
const result = await effectiveHandler(op);
|
|
296
|
+
return JSON.stringify(result);
|
|
297
|
+
}));
|
|
298
|
+
context.evalSync(`
|
|
299
|
+
(function() {
|
|
300
|
+
// IsolatePage class - represents a page with a specific pageId
|
|
301
|
+
class IsolatePage {
|
|
302
|
+
#pageId; #contextId;
|
|
303
|
+
constructor(pageId, contextId) {
|
|
304
|
+
this.#pageId = pageId;
|
|
305
|
+
this.#contextId = contextId;
|
|
306
|
+
}
|
|
307
|
+
get __isPage() { return true; }
|
|
308
|
+
get __pageId() { return this.#pageId; }
|
|
309
|
+
get __contextId() { return this.#contextId; }
|
|
310
|
+
|
|
546
311
|
async goto(url, options) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
__pw_currentUrl = resolvedUrl || url;
|
|
550
|
-
return result;
|
|
551
|
-
},
|
|
312
|
+
await __pw_invoke("goto", [url, options?.waitUntil || null], { pageId: this.#pageId });
|
|
313
|
+
}
|
|
552
314
|
async reload() {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
url() {
|
|
559
|
-
return __pw_currentUrl;
|
|
560
|
-
},
|
|
561
|
-
async title() {
|
|
562
|
-
return __pw_invoke("title", []);
|
|
563
|
-
},
|
|
564
|
-
async content() {
|
|
565
|
-
return __pw_invoke("content", []);
|
|
566
|
-
},
|
|
315
|
+
await __pw_invoke("reload", [], { pageId: this.#pageId });
|
|
316
|
+
}
|
|
317
|
+
url() { return __pw_invoke_sync("url", [], { pageId: this.#pageId }); }
|
|
318
|
+
async title() { return __pw_invoke("title", [], { pageId: this.#pageId }); }
|
|
319
|
+
async content() { return __pw_invoke("content", [], { pageId: this.#pageId }); }
|
|
567
320
|
async waitForSelector(selector, options) {
|
|
568
|
-
return __pw_invoke("waitForSelector", [selector, options ? JSON.stringify(options) : null]);
|
|
569
|
-
}
|
|
570
|
-
async waitForTimeout(ms) {
|
|
571
|
-
|
|
572
|
-
},
|
|
573
|
-
async waitForLoadState(state) {
|
|
574
|
-
return __pw_invoke("waitForLoadState", [state || null]);
|
|
575
|
-
},
|
|
321
|
+
return __pw_invoke("waitForSelector", [selector, options ? JSON.stringify(options) : null], { pageId: this.#pageId });
|
|
322
|
+
}
|
|
323
|
+
async waitForTimeout(ms) { return __pw_invoke("waitForTimeout", [ms], { pageId: this.#pageId }); }
|
|
324
|
+
async waitForLoadState(state) { return __pw_invoke("waitForLoadState", [state || null], { pageId: this.#pageId }); }
|
|
576
325
|
async evaluate(script, arg) {
|
|
577
326
|
const hasArg = arguments.length > 1;
|
|
578
327
|
if (hasArg) {
|
|
579
328
|
const serialized = typeof script === "function" ? script.toString() : script;
|
|
580
|
-
return __pw_invoke("evaluate", [serialized, arg]);
|
|
329
|
+
return __pw_invoke("evaluate", [serialized, arg], { pageId: this.#pageId });
|
|
581
330
|
}
|
|
582
331
|
const serialized = typeof script === "function" ? "(" + script.toString() + ")()" : script;
|
|
583
|
-
return __pw_invoke("evaluate", [serialized]);
|
|
584
|
-
}
|
|
585
|
-
locator(selector) { return new Locator("css", selector, null); }
|
|
332
|
+
return __pw_invoke("evaluate", [serialized], { pageId: this.#pageId });
|
|
333
|
+
}
|
|
334
|
+
locator(selector) { return new Locator("css", selector, null, this.#pageId); }
|
|
586
335
|
getByRole(role, options) {
|
|
587
336
|
if (options) {
|
|
588
337
|
const serialized = { ...options };
|
|
589
|
-
// Use duck-typing RegExp detection (instanceof fails across isolated-vm boundary)
|
|
590
338
|
const name = options.name;
|
|
591
339
|
if (name && typeof name === 'object' && typeof name.source === 'string' && typeof name.flags === 'string') {
|
|
592
340
|
serialized.name = { $regex: name.source, $flags: name.flags };
|
|
593
341
|
}
|
|
594
|
-
return new Locator("role", role, JSON.stringify(serialized));
|
|
342
|
+
return new Locator("role", role, JSON.stringify(serialized), this.#pageId);
|
|
595
343
|
}
|
|
596
|
-
return new Locator("role", role, null);
|
|
597
|
-
}
|
|
598
|
-
getByText(text) { return new Locator("text", text, null); }
|
|
599
|
-
getByLabel(label) { return new Locator("label", label, null); }
|
|
600
|
-
getByPlaceholder(p) { return new Locator("placeholder", p, null); }
|
|
601
|
-
getByTestId(id) { return new Locator("testId", id, null); }
|
|
344
|
+
return new Locator("role", role, null, this.#pageId);
|
|
345
|
+
}
|
|
346
|
+
getByText(text) { return new Locator("text", text, null, this.#pageId); }
|
|
347
|
+
getByLabel(label) { return new Locator("label", label, null, this.#pageId); }
|
|
348
|
+
getByPlaceholder(p) { return new Locator("placeholder", p, null, this.#pageId); }
|
|
349
|
+
getByTestId(id) { return new Locator("testId", id, null, this.#pageId); }
|
|
350
|
+
getByAltText(alt) { return new Locator("altText", alt, null, this.#pageId); }
|
|
351
|
+
getByTitle(title) { return new Locator("title", title, null, this.#pageId); }
|
|
352
|
+
frameLocator(selector) {
|
|
353
|
+
const pageId = this.#pageId;
|
|
354
|
+
return {
|
|
355
|
+
locator(innerSelector) { return new Locator("frame", JSON.stringify([["css", selector, null], ["css", innerSelector, null]]), null, pageId); },
|
|
356
|
+
getByRole(role, options) { return new Locator("frame", JSON.stringify([["css", selector, null], ["role", role, options ? JSON.stringify(options) : null]]), null, pageId); },
|
|
357
|
+
getByText(text) { return new Locator("frame", JSON.stringify([["css", selector, null], ["text", text, null]]), null, pageId); },
|
|
358
|
+
getByLabel(label) { return new Locator("frame", JSON.stringify([["css", selector, null], ["label", label, null]]), null, pageId); },
|
|
359
|
+
getByPlaceholder(placeholder) { return new Locator("frame", JSON.stringify([["css", selector, null], ["placeholder", placeholder, null]]), null, pageId); },
|
|
360
|
+
getByTestId(testId) { return new Locator("frame", JSON.stringify([["css", selector, null], ["testId", testId, null]]), null, pageId); },
|
|
361
|
+
getByAltText(alt) { return new Locator("frame", JSON.stringify([["css", selector, null], ["altText", alt, null]]), null, pageId); },
|
|
362
|
+
getByTitle(title) { return new Locator("frame", JSON.stringify([["css", selector, null], ["title", title, null]]), null, pageId); },
|
|
363
|
+
};
|
|
364
|
+
}
|
|
602
365
|
async goBack(options) {
|
|
603
|
-
await __pw_invoke("goBack", [options?.waitUntil || null]);
|
|
604
|
-
|
|
605
|
-
if (resolvedUrl) __pw_currentUrl = resolvedUrl;
|
|
606
|
-
},
|
|
366
|
+
await __pw_invoke("goBack", [options?.waitUntil || null], { pageId: this.#pageId });
|
|
367
|
+
}
|
|
607
368
|
async goForward(options) {
|
|
608
|
-
await __pw_invoke("goForward", [options?.waitUntil || null]);
|
|
609
|
-
|
|
610
|
-
if (resolvedUrl) __pw_currentUrl = resolvedUrl;
|
|
611
|
-
},
|
|
369
|
+
await __pw_invoke("goForward", [options?.waitUntil || null], { pageId: this.#pageId });
|
|
370
|
+
}
|
|
612
371
|
async waitForURL(url, options) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
372
|
+
if (typeof url === 'function') {
|
|
373
|
+
const predicateId = __pw_register_predicate(url);
|
|
374
|
+
try {
|
|
375
|
+
await __pw_invoke("waitForURLPredicate", [predicateId, options?.timeout || null, options?.waitUntil || null], { pageId: this.#pageId });
|
|
376
|
+
} finally {
|
|
377
|
+
__pw_unregister_predicate(predicateId);
|
|
378
|
+
}
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
let serializedUrl;
|
|
382
|
+
if (typeof url === 'string') {
|
|
383
|
+
serializedUrl = { type: 'string', value: url };
|
|
384
|
+
} else if (url && typeof url === 'object' && typeof url.source === 'string' && typeof url.flags === 'string') {
|
|
385
|
+
serializedUrl = { type: 'regex', value: { $regex: url.source, $flags: url.flags } };
|
|
386
|
+
} else {
|
|
387
|
+
serializedUrl = url;
|
|
388
|
+
}
|
|
389
|
+
return __pw_invoke("waitForURL", [serializedUrl, options?.timeout || null, options?.waitUntil || null], { pageId: this.#pageId });
|
|
390
|
+
}
|
|
391
|
+
waitForRequest(urlOrPredicate, options) {
|
|
392
|
+
if (typeof urlOrPredicate === 'function') {
|
|
393
|
+
const userPredicate = urlOrPredicate;
|
|
394
|
+
const wrappedPredicate = (data) => {
|
|
395
|
+
const requestLike = {
|
|
396
|
+
url: () => data.url,
|
|
397
|
+
method: () => data.method,
|
|
398
|
+
headers: () => Object.fromEntries(data.headers),
|
|
399
|
+
headersArray: () => data.headers.map(h => ({ name: h[0], value: h[1] })),
|
|
400
|
+
postData: () => data.body || null,
|
|
401
|
+
};
|
|
402
|
+
return userPredicate(requestLike);
|
|
403
|
+
};
|
|
404
|
+
const predicateId = __pw_register_predicate(wrappedPredicate);
|
|
405
|
+
const pageId = this.#pageId;
|
|
406
|
+
// Start listening immediately (before the user triggers the request)
|
|
407
|
+
const broadMatcher = { type: 'regex', value: { $regex: '.*', $flags: '' } };
|
|
408
|
+
const startResult = __pw_invoke_sync("waitForRequestStart", [broadMatcher, options?.timeout || null], { pageId });
|
|
409
|
+
const listenerId = startResult.listenerId;
|
|
410
|
+
return {
|
|
411
|
+
then(resolve, reject) {
|
|
412
|
+
try {
|
|
413
|
+
const r = __pw_invoke_sync("waitForRequestPredicateFinish", [listenerId, predicateId, options?.timeout || null], { pageId });
|
|
414
|
+
resolve({ url: () => r.url, method: () => r.method, headers: () => r.headers, postData: () => r.postData });
|
|
415
|
+
} catch(e) { reject(e); } finally { __pw_unregister_predicate(predicateId); }
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
let serializedMatcher;
|
|
420
|
+
if (typeof urlOrPredicate === 'string') {
|
|
421
|
+
serializedMatcher = { type: 'string', value: urlOrPredicate };
|
|
422
|
+
} else if (urlOrPredicate && typeof urlOrPredicate === 'object'
|
|
423
|
+
&& typeof urlOrPredicate.source === 'string'
|
|
424
|
+
&& typeof urlOrPredicate.flags === 'string') {
|
|
425
|
+
serializedMatcher = { type: 'regex', value: { $regex: urlOrPredicate.source, $flags: urlOrPredicate.flags } };
|
|
426
|
+
} else {
|
|
427
|
+
throw new Error('waitForRequest requires a URL string, RegExp, or predicate function');
|
|
428
|
+
}
|
|
429
|
+
const startResult = __pw_invoke_sync("waitForRequestStart", [serializedMatcher, options?.timeout || null], { pageId: this.#pageId });
|
|
430
|
+
const listenerId = startResult.listenerId;
|
|
431
|
+
const pageId = this.#pageId;
|
|
616
432
|
return {
|
|
617
|
-
|
|
618
|
-
|
|
433
|
+
then(resolve, reject) {
|
|
434
|
+
try {
|
|
435
|
+
const r = __pw_invoke_sync("waitForRequestFinish", [listenerId], { pageId });
|
|
436
|
+
resolve({ url: () => r.url, method: () => r.method, headers: () => r.headers, postData: () => r.postData });
|
|
437
|
+
} catch(e) { reject(e); }
|
|
619
438
|
}
|
|
620
439
|
};
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
440
|
+
}
|
|
441
|
+
waitForResponse(urlOrPredicate, options) {
|
|
442
|
+
if (typeof urlOrPredicate === 'function') {
|
|
443
|
+
const userPredicate = urlOrPredicate;
|
|
444
|
+
const wrappedPredicate = (data) => {
|
|
445
|
+
const responseLike = {
|
|
446
|
+
url: () => data.url,
|
|
447
|
+
status: () => data.status,
|
|
448
|
+
statusText: () => data.statusText,
|
|
449
|
+
headers: () => Object.fromEntries(data.headers),
|
|
450
|
+
headersArray: () => data.headers.map(h => ({ name: h[0], value: h[1] })),
|
|
451
|
+
ok: () => data.status >= 200 && data.status < 300,
|
|
452
|
+
};
|
|
453
|
+
return userPredicate(responseLike);
|
|
454
|
+
};
|
|
455
|
+
const predicateId = __pw_register_predicate(wrappedPredicate);
|
|
456
|
+
const pageId = this.#pageId;
|
|
457
|
+
// Start listening immediately (before the user triggers the response)
|
|
458
|
+
const broadMatcher = { type: 'regex', value: { $regex: '.*', $flags: '' } };
|
|
459
|
+
const startResult = __pw_invoke_sync("waitForResponseStart", [broadMatcher, options?.timeout || null], { pageId });
|
|
460
|
+
const listenerId = startResult.listenerId;
|
|
627
461
|
return {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
462
|
+
then(resolve, reject) {
|
|
463
|
+
try {
|
|
464
|
+
const r = __pw_invoke_sync("waitForResponsePredicateFinish", [listenerId, predicateId, options?.timeout || null], { pageId });
|
|
465
|
+
resolve({
|
|
466
|
+
url: () => r.url, status: () => r.status, statusText: () => r.statusText,
|
|
467
|
+
headers: () => r.headers, headersArray: () => r.headersArray,
|
|
468
|
+
ok: () => r.ok, json: async () => r.json, text: async () => r.text, body: async () => r.body,
|
|
469
|
+
});
|
|
470
|
+
} catch(e) { reject(e); } finally { __pw_unregister_predicate(predicateId); }
|
|
471
|
+
}
|
|
634
472
|
};
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
473
|
+
}
|
|
474
|
+
let serializedMatcher;
|
|
475
|
+
if (typeof urlOrPredicate === 'string') {
|
|
476
|
+
serializedMatcher = { type: 'string', value: urlOrPredicate };
|
|
477
|
+
} else if (urlOrPredicate && typeof urlOrPredicate === 'object'
|
|
478
|
+
&& typeof urlOrPredicate.source === 'string'
|
|
479
|
+
&& typeof urlOrPredicate.flags === 'string') {
|
|
480
|
+
serializedMatcher = { type: 'regex', value: { $regex: urlOrPredicate.source, $flags: urlOrPredicate.flags } };
|
|
481
|
+
} else {
|
|
482
|
+
throw new Error('waitForResponse requires a URL string, RegExp, or predicate function');
|
|
483
|
+
}
|
|
484
|
+
const startResult = __pw_invoke_sync("waitForResponseStart", [serializedMatcher, options?.timeout || null], { pageId: this.#pageId });
|
|
485
|
+
const listenerId = startResult.listenerId;
|
|
486
|
+
const pageId = this.#pageId;
|
|
487
|
+
return {
|
|
488
|
+
then(resolve, reject) {
|
|
489
|
+
try {
|
|
490
|
+
const r = __pw_invoke_sync("waitForResponseFinish", [listenerId], { pageId });
|
|
491
|
+
resolve({
|
|
492
|
+
url: () => r.url, status: () => r.status, statusText: () => r.statusText,
|
|
493
|
+
headers: () => r.headers, headersArray: () => r.headersArray,
|
|
494
|
+
ok: () => r.ok, json: async () => r.json, text: async () => r.text, body: async () => r.body,
|
|
495
|
+
});
|
|
496
|
+
} catch(e) { reject(e); }
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
context() {
|
|
501
|
+
const contextId = this.#contextId;
|
|
502
|
+
return new IsolateContext(contextId);
|
|
503
|
+
}
|
|
504
|
+
async click(selector) { return this.locator(selector).click(); }
|
|
505
|
+
async fill(selector, value) { return this.locator(selector).fill(value); }
|
|
506
|
+
async textContent(selector) { return this.locator(selector).textContent(); }
|
|
507
|
+
async innerText(selector) { return this.locator(selector).innerText(); }
|
|
508
|
+
async innerHTML(selector) { return this.locator(selector).innerHTML(); }
|
|
509
|
+
async getAttribute(selector, name) { return this.locator(selector).getAttribute(name); }
|
|
510
|
+
async inputValue(selector) { return this.locator(selector).inputValue(); }
|
|
511
|
+
async isVisible(selector) { return this.locator(selector).isVisible(); }
|
|
512
|
+
async isEnabled(selector) { return this.locator(selector).isEnabled(); }
|
|
513
|
+
async isChecked(selector) { return this.locator(selector).isChecked(); }
|
|
514
|
+
async isHidden(selector) { return this.locator(selector).isHidden(); }
|
|
515
|
+
async isDisabled(selector) { return this.locator(selector).isDisabled(); }
|
|
516
|
+
async screenshot(options) { return __pw_invoke("screenshot", [options || {}], { pageId: this.#pageId }); }
|
|
517
|
+
async setViewportSize(size) { return __pw_invoke("setViewportSize", [size], { pageId: this.#pageId }); }
|
|
518
|
+
async viewportSize() { return __pw_invoke("viewportSize", [], { pageId: this.#pageId }); }
|
|
519
|
+
async emulateMedia(options) { return __pw_invoke("emulateMedia", [options], { pageId: this.#pageId }); }
|
|
520
|
+
async setExtraHTTPHeaders(headers) { return __pw_invoke("setExtraHTTPHeaders", [headers], { pageId: this.#pageId }); }
|
|
521
|
+
async bringToFront() { return __pw_invoke("bringToFront", [], { pageId: this.#pageId }); }
|
|
522
|
+
async close() { return __pw_invoke("close", [], { pageId: this.#pageId }); }
|
|
523
|
+
async isClosed() { return __pw_invoke("isClosed", [], { pageId: this.#pageId }); }
|
|
524
|
+
async pdf(options) { return __pw_invoke("pdf", [options || {}], { pageId: this.#pageId }); }
|
|
525
|
+
async pause() { return __pw_invoke("pause", [], { pageId: this.#pageId }); }
|
|
526
|
+
async frames() { return __pw_invoke("frames", [], { pageId: this.#pageId }); }
|
|
527
|
+
async mainFrame() { return __pw_invoke("mainFrame", [], { pageId: this.#pageId }); }
|
|
528
|
+
get keyboard() {
|
|
529
|
+
const pageId = this.#pageId;
|
|
530
|
+
return {
|
|
531
|
+
async type(text, options) { return __pw_invoke("keyboardType", [text, options], { pageId }); },
|
|
532
|
+
async press(key, options) { return __pw_invoke("keyboardPress", [key, options], { pageId }); },
|
|
533
|
+
async down(key) { return __pw_invoke("keyboardDown", [key], { pageId }); },
|
|
534
|
+
async up(key) { return __pw_invoke("keyboardUp", [key], { pageId }); },
|
|
535
|
+
async insertText(text) { return __pw_invoke("keyboardInsertText", [text], { pageId }); }
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
get mouse() {
|
|
539
|
+
const pageId = this.#pageId;
|
|
540
|
+
return {
|
|
541
|
+
async move(x, y, options) { return __pw_invoke("mouseMove", [x, y, options], { pageId }); },
|
|
542
|
+
async click(x, y, options) { return __pw_invoke("mouseClick", [x, y, options], { pageId }); },
|
|
543
|
+
async down(options) { return __pw_invoke("mouseDown", [options], { pageId }); },
|
|
544
|
+
async up(options) { return __pw_invoke("mouseUp", [options], { pageId }); },
|
|
545
|
+
async wheel(deltaX, deltaY) { return __pw_invoke("mouseWheel", [deltaX, deltaY], { pageId }); }
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
get request() {
|
|
549
|
+
const pageId = this.#pageId;
|
|
550
|
+
return {
|
|
551
|
+
async fetch(url, options) {
|
|
552
|
+
const result = await __pw_invoke("request", [url, options?.method || "GET", options?.data, options?.headers], { pageId });
|
|
553
|
+
return {
|
|
554
|
+
status: () => result.status,
|
|
555
|
+
ok: () => result.ok,
|
|
556
|
+
headers: () => result.headers,
|
|
557
|
+
json: async () => result.json,
|
|
558
|
+
text: async () => result.text,
|
|
559
|
+
body: async () => result.body,
|
|
560
|
+
};
|
|
561
|
+
},
|
|
562
|
+
async get(url, options) { return this.fetch(url, { ...options, method: "GET" }); },
|
|
563
|
+
async post(url, options) { return this.fetch(url, { ...options, method: "POST" }); },
|
|
564
|
+
async put(url, options) { return this.fetch(url, { ...options, method: "PUT" }); },
|
|
565
|
+
async delete(url, options) { return this.fetch(url, { ...options, method: "DELETE" }); },
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
globalThis.IsolatePage = IsolatePage;
|
|
570
|
+
|
|
571
|
+
// IsolateContext class - represents a browser context with a specific contextId
|
|
572
|
+
class IsolateContext {
|
|
573
|
+
#contextId;
|
|
574
|
+
constructor(contextId) { this.#contextId = contextId; }
|
|
575
|
+
get __contextId() { return this.#contextId; }
|
|
576
|
+
|
|
577
|
+
async newPage() {
|
|
578
|
+
const result = await __pw_invoke("newPage", [], { contextId: this.#contextId });
|
|
579
|
+
return new IsolatePage(result.pageId, this.#contextId);
|
|
580
|
+
}
|
|
581
|
+
async close() { return __pw_invoke("closeContext", [], { contextId: this.#contextId }); }
|
|
582
|
+
async clearCookies() { return __pw_invoke("clearCookies", [], { contextId: this.#contextId }); }
|
|
583
|
+
async addCookies(cookies) { return __pw_invoke("addCookies", [cookies], { contextId: this.#contextId }); }
|
|
584
|
+
async cookies(urls) { return __pw_invoke("cookies", [urls], { contextId: this.#contextId }); }
|
|
585
|
+
}
|
|
586
|
+
globalThis.IsolateContext = IsolateContext;
|
|
587
|
+
|
|
588
|
+
// browser global - for creating new contexts
|
|
589
|
+
globalThis.browser = {
|
|
590
|
+
async newContext(options) {
|
|
591
|
+
const result = await __pw_invoke("newContext", [options || null]);
|
|
592
|
+
return new IsolateContext(result.contextId);
|
|
593
|
+
}
|
|
649
594
|
};
|
|
595
|
+
|
|
596
|
+
// context global - represents the default context
|
|
597
|
+
globalThis.context = new IsolateContext("ctx_0");
|
|
598
|
+
|
|
599
|
+
// page global - represents the default page
|
|
600
|
+
globalThis.page = new IsolatePage("page_0", "ctx_0");
|
|
650
601
|
})();
|
|
651
602
|
`);
|
|
652
603
|
context.evalSync(`
|
|
653
604
|
(function() {
|
|
605
|
+
// Helper to serialize options including RegExp
|
|
606
|
+
function serializeOptions(options) {
|
|
607
|
+
if (!options) return null;
|
|
608
|
+
const serialized = { ...options };
|
|
609
|
+
if (options.name && typeof options.name === 'object' && typeof options.name.source === 'string' && typeof options.name.flags === 'string') {
|
|
610
|
+
serialized.name = { $regex: options.name.source, $flags: options.name.flags };
|
|
611
|
+
}
|
|
612
|
+
return JSON.stringify(serialized);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const INPUT_FILES_VALIDATION_ERROR =
|
|
616
|
+
"setInputFiles() expects a file path string, an array of file path strings, " +
|
|
617
|
+
"a single inline file object ({ name, mimeType, buffer }), or an array of inline file objects.";
|
|
618
|
+
|
|
619
|
+
function isInlineFileObject(value) {
|
|
620
|
+
return !!value
|
|
621
|
+
&& typeof value === 'object'
|
|
622
|
+
&& typeof value.name === 'string'
|
|
623
|
+
&& typeof value.mimeType === 'string'
|
|
624
|
+
&& 'buffer' in value;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function encodeInlineFileBuffer(buffer) {
|
|
628
|
+
if (typeof buffer === 'string') {
|
|
629
|
+
return buffer;
|
|
630
|
+
}
|
|
631
|
+
let bytes;
|
|
632
|
+
if (buffer instanceof ArrayBuffer) {
|
|
633
|
+
bytes = new Uint8Array(buffer);
|
|
634
|
+
} else if (ArrayBuffer.isView(buffer)) {
|
|
635
|
+
bytes = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
636
|
+
} else {
|
|
637
|
+
throw new Error(
|
|
638
|
+
"setInputFiles() inline file buffer must be a base64 string, ArrayBuffer, or TypedArray."
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
let binary = '';
|
|
642
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
643
|
+
binary += String.fromCharCode(bytes[i]);
|
|
644
|
+
}
|
|
645
|
+
return btoa(binary);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function serializeInlineFile(file) {
|
|
649
|
+
return {
|
|
650
|
+
name: file.name,
|
|
651
|
+
mimeType: file.mimeType,
|
|
652
|
+
buffer: encodeInlineFileBuffer(file.buffer),
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function normalizeSetInputFilesArg(files) {
|
|
657
|
+
if (typeof files === 'string') {
|
|
658
|
+
return files;
|
|
659
|
+
}
|
|
660
|
+
if (isInlineFileObject(files)) {
|
|
661
|
+
return serializeInlineFile(files);
|
|
662
|
+
}
|
|
663
|
+
if (!Array.isArray(files)) {
|
|
664
|
+
throw new Error(INPUT_FILES_VALIDATION_ERROR);
|
|
665
|
+
}
|
|
666
|
+
if (files.length === 0) {
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
let hasPaths = false;
|
|
671
|
+
let hasInline = false;
|
|
672
|
+
const inlineFiles = [];
|
|
673
|
+
|
|
674
|
+
for (const file of files) {
|
|
675
|
+
if (typeof file === 'string') {
|
|
676
|
+
hasPaths = true;
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
if (isInlineFileObject(file)) {
|
|
680
|
+
hasInline = true;
|
|
681
|
+
inlineFiles.push(serializeInlineFile(file));
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
throw new Error(INPUT_FILES_VALIDATION_ERROR);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (hasPaths && hasInline) {
|
|
688
|
+
throw new Error(
|
|
689
|
+
"setInputFiles() does not support mixing file paths and inline file objects in the same array."
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
return hasInline ? inlineFiles : files;
|
|
693
|
+
}
|
|
694
|
+
|
|
654
695
|
class Locator {
|
|
655
|
-
#type; #value; #options;
|
|
656
|
-
constructor(type, value, options) {
|
|
696
|
+
#type; #value; #options; #pageId;
|
|
697
|
+
constructor(type, value, options, pageId) {
|
|
657
698
|
this.#type = type;
|
|
658
699
|
this.#value = value;
|
|
659
700
|
this.#options = options;
|
|
701
|
+
this.#pageId = pageId || "page_0";
|
|
660
702
|
}
|
|
661
703
|
|
|
662
704
|
_getInfo() { return [this.#type, this.#value, this.#options]; }
|
|
705
|
+
_getPageId() { return this.#pageId; }
|
|
706
|
+
|
|
707
|
+
// Helper to create a chained locator
|
|
708
|
+
_chain(childType, childValue, childOptions) {
|
|
709
|
+
const parentInfo = this._getInfo();
|
|
710
|
+
const childInfo = [childType, childValue, childOptions];
|
|
711
|
+
return new Locator("chained", JSON.stringify([parentInfo, childInfo]), null, this.#pageId);
|
|
712
|
+
}
|
|
663
713
|
|
|
664
714
|
async click() {
|
|
665
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "click", null]);
|
|
715
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "click", null], { pageId: this.#pageId });
|
|
666
716
|
}
|
|
667
717
|
async dblclick() {
|
|
668
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "dblclick", null]);
|
|
718
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "dblclick", null], { pageId: this.#pageId });
|
|
669
719
|
}
|
|
670
720
|
async fill(text) {
|
|
671
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "fill", text]);
|
|
721
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "fill", text], { pageId: this.#pageId });
|
|
672
722
|
}
|
|
673
723
|
async type(text) {
|
|
674
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "type", text]);
|
|
724
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "type", text], { pageId: this.#pageId });
|
|
675
725
|
}
|
|
676
726
|
async check() {
|
|
677
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "check", null]);
|
|
727
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "check", null], { pageId: this.#pageId });
|
|
678
728
|
}
|
|
679
729
|
async uncheck() {
|
|
680
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "uncheck", null]);
|
|
730
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "uncheck", null], { pageId: this.#pageId });
|
|
681
731
|
}
|
|
682
732
|
async selectOption(value) {
|
|
683
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "selectOption", value]);
|
|
733
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "selectOption", value], { pageId: this.#pageId });
|
|
684
734
|
}
|
|
685
735
|
async clear() {
|
|
686
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "clear", null]);
|
|
736
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "clear", null], { pageId: this.#pageId });
|
|
687
737
|
}
|
|
688
738
|
async press(key) {
|
|
689
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "press", key]);
|
|
739
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "press", key], { pageId: this.#pageId });
|
|
690
740
|
}
|
|
691
741
|
async hover() {
|
|
692
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "hover", null]);
|
|
742
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "hover", null], { pageId: this.#pageId });
|
|
693
743
|
}
|
|
694
744
|
async focus() {
|
|
695
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "focus", null]);
|
|
745
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "focus", null], { pageId: this.#pageId });
|
|
696
746
|
}
|
|
697
747
|
async textContent() {
|
|
698
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getText", null]);
|
|
748
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getText", null], { pageId: this.#pageId });
|
|
699
749
|
}
|
|
700
750
|
async inputValue() {
|
|
701
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getValue", null]);
|
|
751
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getValue", null], { pageId: this.#pageId });
|
|
702
752
|
}
|
|
703
753
|
async isVisible() {
|
|
704
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isVisible", null]);
|
|
754
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isVisible", null], { pageId: this.#pageId });
|
|
705
755
|
}
|
|
706
756
|
async isEnabled() {
|
|
707
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isEnabled", null]);
|
|
757
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isEnabled", null], { pageId: this.#pageId });
|
|
708
758
|
}
|
|
709
759
|
async isChecked() {
|
|
710
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isChecked", null]);
|
|
760
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isChecked", null], { pageId: this.#pageId });
|
|
711
761
|
}
|
|
712
762
|
async count() {
|
|
713
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "count", null]);
|
|
763
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "count", null], { pageId: this.#pageId });
|
|
714
764
|
}
|
|
715
765
|
async getAttribute(name) {
|
|
716
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name]);
|
|
766
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name], { pageId: this.#pageId });
|
|
717
767
|
}
|
|
718
768
|
async isDisabled() {
|
|
719
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null]);
|
|
769
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null], { pageId: this.#pageId });
|
|
720
770
|
}
|
|
721
771
|
async isHidden() {
|
|
722
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null]);
|
|
772
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null], { pageId: this.#pageId });
|
|
723
773
|
}
|
|
724
774
|
async innerHTML() {
|
|
725
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null]);
|
|
775
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null], { pageId: this.#pageId });
|
|
726
776
|
}
|
|
727
777
|
async innerText() {
|
|
728
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null]);
|
|
778
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null], { pageId: this.#pageId });
|
|
729
779
|
}
|
|
730
780
|
async allTextContents() {
|
|
731
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null]);
|
|
781
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null], { pageId: this.#pageId });
|
|
732
782
|
}
|
|
733
783
|
async allInnerTexts() {
|
|
734
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null]);
|
|
784
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null], { pageId: this.#pageId });
|
|
735
785
|
}
|
|
736
786
|
async waitFor(options) {
|
|
737
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}]);
|
|
787
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}], { pageId: this.#pageId });
|
|
738
788
|
}
|
|
739
789
|
async boundingBox() {
|
|
740
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null]);
|
|
790
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null], { pageId: this.#pageId });
|
|
791
|
+
}
|
|
792
|
+
async setInputFiles(files) {
|
|
793
|
+
const serializedFiles = normalizeSetInputFilesArg(files);
|
|
794
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "setInputFiles", serializedFiles], { pageId: this.#pageId });
|
|
795
|
+
}
|
|
796
|
+
async screenshot(options) {
|
|
797
|
+
const base64 = await __pw_invoke("locatorAction", [...this._getInfo(), "screenshot", options || {}], { pageId: this.#pageId });
|
|
798
|
+
return base64;
|
|
799
|
+
}
|
|
800
|
+
async dragTo(target) {
|
|
801
|
+
const targetInfo = target._getInfo();
|
|
802
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "dragTo", targetInfo], { pageId: this.#pageId });
|
|
803
|
+
}
|
|
804
|
+
async scrollIntoViewIfNeeded() {
|
|
805
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "scrollIntoViewIfNeeded", null], { pageId: this.#pageId });
|
|
806
|
+
}
|
|
807
|
+
async highlight() {
|
|
808
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "highlight", null], { pageId: this.#pageId });
|
|
809
|
+
}
|
|
810
|
+
async evaluate(fn, arg) {
|
|
811
|
+
const fnString = typeof fn === 'function' ? fn.toString() : fn;
|
|
812
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "evaluate", [fnString, arg]], { pageId: this.#pageId });
|
|
813
|
+
}
|
|
814
|
+
async evaluateAll(fn, arg) {
|
|
815
|
+
const fnString = typeof fn === 'function' ? fn.toString() : fn;
|
|
816
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "evaluateAll", [fnString, arg]], { pageId: this.#pageId });
|
|
741
817
|
}
|
|
742
818
|
locator(selector) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
819
|
+
return this._chain("css", selector, null);
|
|
820
|
+
}
|
|
821
|
+
// Chaining: getBy* methods within a locator
|
|
822
|
+
getByRole(role, options) {
|
|
823
|
+
return this._chain("role", role, serializeOptions(options));
|
|
824
|
+
}
|
|
825
|
+
getByText(text) {
|
|
826
|
+
return this._chain("text", text, null);
|
|
827
|
+
}
|
|
828
|
+
getByLabel(label) {
|
|
829
|
+
return this._chain("label", label, null);
|
|
830
|
+
}
|
|
831
|
+
getByPlaceholder(placeholder) {
|
|
832
|
+
return this._chain("placeholder", placeholder, null);
|
|
833
|
+
}
|
|
834
|
+
getByTestId(testId) {
|
|
835
|
+
return this._chain("testId", testId, null);
|
|
836
|
+
}
|
|
837
|
+
getByAltText(altText) {
|
|
838
|
+
return this._chain("altText", altText, null);
|
|
839
|
+
}
|
|
840
|
+
getByTitle(title) {
|
|
841
|
+
return this._chain("title", title, null);
|
|
749
842
|
}
|
|
750
843
|
async all() {
|
|
751
844
|
const n = await this.count();
|
|
@@ -757,7 +850,7 @@ async function setupPlaywright(context, options) {
|
|
|
757
850
|
}
|
|
758
851
|
nth(index) {
|
|
759
852
|
const existingOpts = this.#options ? JSON.parse(this.#options) : {};
|
|
760
|
-
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }));
|
|
853
|
+
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }), this.#pageId);
|
|
761
854
|
}
|
|
762
855
|
first() {
|
|
763
856
|
return this.nth(0);
|
|
@@ -777,13 +870,28 @@ async function setupPlaywright(context, options) {
|
|
|
777
870
|
if (hasNotText && typeof hasNotText === 'object' && typeof hasNotText.source === 'string' && typeof hasNotText.flags === 'string') {
|
|
778
871
|
serializedFilter.hasNotText = { $regex: hasNotText.source, $flags: hasNotText.flags };
|
|
779
872
|
}
|
|
780
|
-
|
|
873
|
+
// Serialize has/hasNot locators using duck-typing
|
|
874
|
+
const has = options.has;
|
|
875
|
+
if (has && typeof has === 'object' && typeof has._getInfo === 'function') {
|
|
876
|
+
serializedFilter.has = { $locator: has._getInfo() };
|
|
877
|
+
}
|
|
878
|
+
const hasNot = options.hasNot;
|
|
879
|
+
if (hasNot && typeof hasNot === 'object' && typeof hasNot._getInfo === 'function') {
|
|
880
|
+
serializedFilter.hasNot = { $locator: hasNot._getInfo() };
|
|
881
|
+
}
|
|
882
|
+
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, filter: serializedFilter }), this.#pageId);
|
|
781
883
|
}
|
|
782
884
|
or(other) {
|
|
783
885
|
// Create a composite locator that matches either this or other
|
|
784
886
|
const thisInfo = this._getInfo();
|
|
785
887
|
const otherInfo = other._getInfo();
|
|
786
|
-
return new Locator("or", JSON.stringify([thisInfo, otherInfo]), null);
|
|
888
|
+
return new Locator("or", JSON.stringify([thisInfo, otherInfo]), null, this.#pageId);
|
|
889
|
+
}
|
|
890
|
+
and(other) {
|
|
891
|
+
// Create a composite locator that matches both this and other
|
|
892
|
+
const thisInfo = this._getInfo();
|
|
893
|
+
const otherInfo = other._getInfo();
|
|
894
|
+
return new Locator("and", JSON.stringify([thisInfo, otherInfo]), null, this.#pageId);
|
|
787
895
|
}
|
|
788
896
|
}
|
|
789
897
|
globalThis.Locator = Locator;
|
|
@@ -794,84 +902,157 @@ async function setupPlaywright(context, options) {
|
|
|
794
902
|
// Helper to create locator matchers
|
|
795
903
|
function createLocatorMatchers(locator, baseMatchers) {
|
|
796
904
|
const info = locator._getInfo();
|
|
905
|
+
const pageId = locator._getPageId ? locator._getPageId() : "page_0";
|
|
906
|
+
|
|
907
|
+
// Helper for serializing regex values
|
|
908
|
+
function serializeExpected(expected) {
|
|
909
|
+
if (expected instanceof RegExp) {
|
|
910
|
+
return { $regex: expected.source, $flags: expected.flags };
|
|
911
|
+
}
|
|
912
|
+
return expected;
|
|
913
|
+
}
|
|
797
914
|
|
|
798
915
|
const locatorMatchers = {
|
|
799
916
|
async toBeVisible(options) {
|
|
800
|
-
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout]);
|
|
917
|
+
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout], { pageId });
|
|
801
918
|
},
|
|
802
919
|
async toContainText(expected, options) {
|
|
803
|
-
|
|
804
|
-
return __pw_invoke("expectLocator", [...info, "toContainText", serialized, false, options?.timeout]);
|
|
920
|
+
return __pw_invoke("expectLocator", [...info, "toContainText", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
805
921
|
},
|
|
806
922
|
async toHaveValue(expected, options) {
|
|
807
|
-
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout]);
|
|
923
|
+
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout], { pageId });
|
|
808
924
|
},
|
|
809
925
|
async toBeEnabled(options) {
|
|
810
|
-
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, false, options?.timeout]);
|
|
926
|
+
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, false, options?.timeout], { pageId });
|
|
811
927
|
},
|
|
812
928
|
async toBeChecked(options) {
|
|
813
|
-
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout]);
|
|
929
|
+
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout], { pageId });
|
|
814
930
|
},
|
|
815
931
|
async toHaveAttribute(name, value, options) {
|
|
816
|
-
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, false, options?.timeout]);
|
|
932
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value: serializeExpected(value) }, false, options?.timeout], { pageId });
|
|
817
933
|
},
|
|
818
934
|
async toHaveText(expected, options) {
|
|
819
|
-
|
|
820
|
-
return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, false, options?.timeout]);
|
|
935
|
+
return __pw_invoke("expectLocator", [...info, "toHaveText", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
821
936
|
},
|
|
822
937
|
async toHaveCount(count, options) {
|
|
823
|
-
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout]);
|
|
938
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout], { pageId });
|
|
824
939
|
},
|
|
825
940
|
async toBeHidden(options) {
|
|
826
|
-
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout]);
|
|
941
|
+
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout], { pageId });
|
|
827
942
|
},
|
|
828
943
|
async toBeDisabled(options) {
|
|
829
|
-
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout]);
|
|
944
|
+
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout], { pageId });
|
|
830
945
|
},
|
|
831
946
|
async toBeFocused(options) {
|
|
832
|
-
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout]);
|
|
947
|
+
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout], { pageId });
|
|
833
948
|
},
|
|
834
949
|
async toBeEmpty(options) {
|
|
835
|
-
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout]);
|
|
950
|
+
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout], { pageId });
|
|
951
|
+
},
|
|
952
|
+
// New matchers
|
|
953
|
+
async toBeAttached(options) {
|
|
954
|
+
return __pw_invoke("expectLocator", [...info, "toBeAttached", null, false, options?.timeout], { pageId });
|
|
955
|
+
},
|
|
956
|
+
async toBeEditable(options) {
|
|
957
|
+
return __pw_invoke("expectLocator", [...info, "toBeEditable", null, false, options?.timeout], { pageId });
|
|
958
|
+
},
|
|
959
|
+
async toHaveClass(expected, options) {
|
|
960
|
+
return __pw_invoke("expectLocator", [...info, "toHaveClass", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
961
|
+
},
|
|
962
|
+
async toContainClass(expected, options) {
|
|
963
|
+
return __pw_invoke("expectLocator", [...info, "toContainClass", expected, false, options?.timeout], { pageId });
|
|
964
|
+
},
|
|
965
|
+
async toHaveId(expected, options) {
|
|
966
|
+
return __pw_invoke("expectLocator", [...info, "toHaveId", expected, false, options?.timeout], { pageId });
|
|
967
|
+
},
|
|
968
|
+
async toBeInViewport(options) {
|
|
969
|
+
return __pw_invoke("expectLocator", [...info, "toBeInViewport", null, false, options?.timeout], { pageId });
|
|
970
|
+
},
|
|
971
|
+
async toHaveCSS(name, value, options) {
|
|
972
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCSS", { name, value: serializeExpected(value) }, false, options?.timeout], { pageId });
|
|
973
|
+
},
|
|
974
|
+
async toHaveJSProperty(name, value, options) {
|
|
975
|
+
return __pw_invoke("expectLocator", [...info, "toHaveJSProperty", { name, value }, false, options?.timeout], { pageId });
|
|
976
|
+
},
|
|
977
|
+
async toHaveAccessibleName(expected, options) {
|
|
978
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleName", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
979
|
+
},
|
|
980
|
+
async toHaveAccessibleDescription(expected, options) {
|
|
981
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleDescription", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
982
|
+
},
|
|
983
|
+
async toHaveRole(expected, options) {
|
|
984
|
+
return __pw_invoke("expectLocator", [...info, "toHaveRole", expected, false, options?.timeout], { pageId });
|
|
836
985
|
},
|
|
837
986
|
not: {
|
|
838
987
|
async toBeVisible(options) {
|
|
839
|
-
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout]);
|
|
988
|
+
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout], { pageId });
|
|
840
989
|
},
|
|
841
990
|
async toContainText(expected, options) {
|
|
842
|
-
|
|
843
|
-
return __pw_invoke("expectLocator", [...info, "toContainText", serialized, true, options?.timeout]);
|
|
991
|
+
return __pw_invoke("expectLocator", [...info, "toContainText", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
844
992
|
},
|
|
845
993
|
async toHaveValue(expected, options) {
|
|
846
|
-
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout]);
|
|
994
|
+
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout], { pageId });
|
|
847
995
|
},
|
|
848
996
|
async toBeEnabled(options) {
|
|
849
|
-
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, true, options?.timeout]);
|
|
997
|
+
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, true, options?.timeout], { pageId });
|
|
850
998
|
},
|
|
851
999
|
async toBeChecked(options) {
|
|
852
|
-
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout]);
|
|
1000
|
+
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout], { pageId });
|
|
853
1001
|
},
|
|
854
1002
|
async toHaveAttribute(name, value, options) {
|
|
855
|
-
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, true, options?.timeout]);
|
|
1003
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value: serializeExpected(value) }, true, options?.timeout], { pageId });
|
|
856
1004
|
},
|
|
857
1005
|
async toHaveText(expected, options) {
|
|
858
|
-
|
|
859
|
-
return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, true, options?.timeout]);
|
|
1006
|
+
return __pw_invoke("expectLocator", [...info, "toHaveText", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
860
1007
|
},
|
|
861
1008
|
async toHaveCount(count, options) {
|
|
862
|
-
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout]);
|
|
1009
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout], { pageId });
|
|
863
1010
|
},
|
|
864
1011
|
async toBeHidden(options) {
|
|
865
|
-
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout]);
|
|
1012
|
+
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout], { pageId });
|
|
866
1013
|
},
|
|
867
1014
|
async toBeDisabled(options) {
|
|
868
|
-
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout]);
|
|
1015
|
+
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout], { pageId });
|
|
869
1016
|
},
|
|
870
1017
|
async toBeFocused(options) {
|
|
871
|
-
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout]);
|
|
1018
|
+
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout], { pageId });
|
|
872
1019
|
},
|
|
873
1020
|
async toBeEmpty(options) {
|
|
874
|
-
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout]);
|
|
1021
|
+
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout], { pageId });
|
|
1022
|
+
},
|
|
1023
|
+
// New negated matchers
|
|
1024
|
+
async toBeAttached(options) {
|
|
1025
|
+
return __pw_invoke("expectLocator", [...info, "toBeAttached", null, true, options?.timeout], { pageId });
|
|
1026
|
+
},
|
|
1027
|
+
async toBeEditable(options) {
|
|
1028
|
+
return __pw_invoke("expectLocator", [...info, "toBeEditable", null, true, options?.timeout], { pageId });
|
|
1029
|
+
},
|
|
1030
|
+
async toHaveClass(expected, options) {
|
|
1031
|
+
return __pw_invoke("expectLocator", [...info, "toHaveClass", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1032
|
+
},
|
|
1033
|
+
async toContainClass(expected, options) {
|
|
1034
|
+
return __pw_invoke("expectLocator", [...info, "toContainClass", expected, true, options?.timeout], { pageId });
|
|
1035
|
+
},
|
|
1036
|
+
async toHaveId(expected, options) {
|
|
1037
|
+
return __pw_invoke("expectLocator", [...info, "toHaveId", expected, true, options?.timeout], { pageId });
|
|
1038
|
+
},
|
|
1039
|
+
async toBeInViewport(options) {
|
|
1040
|
+
return __pw_invoke("expectLocator", [...info, "toBeInViewport", null, true, options?.timeout], { pageId });
|
|
1041
|
+
},
|
|
1042
|
+
async toHaveCSS(name, value, options) {
|
|
1043
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCSS", { name, value: serializeExpected(value) }, true, options?.timeout], { pageId });
|
|
1044
|
+
},
|
|
1045
|
+
async toHaveJSProperty(name, value, options) {
|
|
1046
|
+
return __pw_invoke("expectLocator", [...info, "toHaveJSProperty", { name, value }, true, options?.timeout], { pageId });
|
|
1047
|
+
},
|
|
1048
|
+
async toHaveAccessibleName(expected, options) {
|
|
1049
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleName", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1050
|
+
},
|
|
1051
|
+
async toHaveAccessibleDescription(expected, options) {
|
|
1052
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleDescription", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1053
|
+
},
|
|
1054
|
+
async toHaveRole(expected, options) {
|
|
1055
|
+
return __pw_invoke("expectLocator", [...info, "toHaveRole", expected, true, options?.timeout], { pageId });
|
|
875
1056
|
},
|
|
876
1057
|
}
|
|
877
1058
|
};
|
|
@@ -887,6 +1068,44 @@ async function setupPlaywright(context, options) {
|
|
|
887
1068
|
return locatorMatchers;
|
|
888
1069
|
}
|
|
889
1070
|
|
|
1071
|
+
// Helper to create page matchers
|
|
1072
|
+
function createPageMatchers(page, baseMatchers) {
|
|
1073
|
+
const pageId = page.__pageId || "page_0";
|
|
1074
|
+
|
|
1075
|
+
function serializeExpected(expected) {
|
|
1076
|
+
if (expected instanceof RegExp) {
|
|
1077
|
+
return { $regex: expected.source, $flags: expected.flags };
|
|
1078
|
+
}
|
|
1079
|
+
return expected;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
const pageMatchers = {
|
|
1083
|
+
async toHaveURL(expected, options) {
|
|
1084
|
+
return __pw_invoke("expectPage", ["toHaveURL", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1085
|
+
},
|
|
1086
|
+
async toHaveTitle(expected, options) {
|
|
1087
|
+
return __pw_invoke("expectPage", ["toHaveTitle", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1088
|
+
},
|
|
1089
|
+
not: {
|
|
1090
|
+
async toHaveURL(expected, options) {
|
|
1091
|
+
return __pw_invoke("expectPage", ["toHaveURL", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1092
|
+
},
|
|
1093
|
+
async toHaveTitle(expected, options) {
|
|
1094
|
+
return __pw_invoke("expectPage", ["toHaveTitle", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1095
|
+
},
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
if (baseMatchers) {
|
|
1100
|
+
return {
|
|
1101
|
+
...baseMatchers,
|
|
1102
|
+
...pageMatchers,
|
|
1103
|
+
not: { ...baseMatchers.not, ...pageMatchers.not }
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
return pageMatchers;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
890
1109
|
// Only extend expect if test-environment already defined it
|
|
891
1110
|
if (typeof globalThis.expect === 'function') {
|
|
892
1111
|
const originalExpect = globalThis.expect;
|
|
@@ -896,6 +1115,10 @@ async function setupPlaywright(context, options) {
|
|
|
896
1115
|
if (actual && actual.constructor && actual.constructor.name === 'Locator') {
|
|
897
1116
|
return createLocatorMatchers(actual, baseMatchers);
|
|
898
1117
|
}
|
|
1118
|
+
// If actual is the page object (IsolatePage), add page-specific matchers
|
|
1119
|
+
if (actual && actual.__isPage === true) {
|
|
1120
|
+
return createPageMatchers(actual, baseMatchers);
|
|
1121
|
+
}
|
|
899
1122
|
return baseMatchers;
|
|
900
1123
|
};
|
|
901
1124
|
}
|
|
@@ -931,7 +1154,10 @@ async function setupPlaywright(context, options) {
|
|
|
931
1154
|
}
|
|
932
1155
|
export {
|
|
933
1156
|
setupPlaywright,
|
|
934
|
-
|
|
1157
|
+
getDefaultPlaywrightHandlerMetadata,
|
|
1158
|
+
defaultPlaywrightHandler,
|
|
1159
|
+
createPlaywrightHandler,
|
|
1160
|
+
DEFAULT_PLAYWRIGHT_HANDLER_META
|
|
935
1161
|
};
|
|
936
1162
|
|
|
937
|
-
//# debugId=
|
|
1163
|
+
//# debugId=917AA0A37B759B7B64756E2164756E21
|