@ricsam/isolate-playwright 0.1.11 → 0.1.13
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 +209 -20
- package/dist/cjs/client.cjs +866 -22
- package/dist/cjs/client.cjs.map +3 -3
- package/dist/cjs/index.cjs +1116 -181
- 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 +868 -22
- package/dist/mjs/client.mjs.map +3 -3
- package/dist/mjs/index.mjs +1118 -181
- 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 +13 -7
- package/dist/types/index.d.ts +33 -20
- package/dist/types/types.d.ts +62 -13
- package/package.json +1 -1
package/dist/cjs/client.cjs
CHANGED
|
@@ -29,15 +29,22 @@ var __export = (target, all) => {
|
|
|
29
29
|
// packages/playwright/src/client.ts
|
|
30
30
|
var exports_client = {};
|
|
31
31
|
__export(exports_client, {
|
|
32
|
+
getDefaultPlaywrightHandlerMetadata: () => getDefaultPlaywrightHandlerMetadata,
|
|
33
|
+
defaultPlaywrightHandler: () => defaultPlaywrightHandler,
|
|
32
34
|
createPlaywrightHandler: () => createPlaywrightHandler
|
|
33
35
|
});
|
|
34
36
|
module.exports = __toCommonJS(exports_client);
|
|
37
|
+
var import_types = require("./types.cjs");
|
|
35
38
|
function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
36
39
|
const options = optionsJson ? JSON.parse(optionsJson) : undefined;
|
|
37
40
|
const nthIndex = options?.nth;
|
|
38
41
|
const roleOptions = options ? { ...options } : undefined;
|
|
39
42
|
if (roleOptions) {
|
|
40
43
|
delete roleOptions.nth;
|
|
44
|
+
delete roleOptions.filter;
|
|
45
|
+
if (roleOptions.name && typeof roleOptions.name === "object" && roleOptions.name.$regex) {
|
|
46
|
+
roleOptions.name = new RegExp(roleOptions.name.$regex, roleOptions.name.$flags);
|
|
47
|
+
}
|
|
41
48
|
}
|
|
42
49
|
let locator;
|
|
43
50
|
switch (selectorType) {
|
|
@@ -59,15 +66,180 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
|
59
66
|
case "testId":
|
|
60
67
|
locator = page.getByTestId(selectorValue);
|
|
61
68
|
break;
|
|
69
|
+
case "or": {
|
|
70
|
+
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
71
|
+
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
72
|
+
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
73
|
+
locator = first.or(second);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case "and": {
|
|
77
|
+
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
78
|
+
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
79
|
+
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
80
|
+
locator = first.and(second);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "chained": {
|
|
84
|
+
const [parentInfo, childInfo] = JSON.parse(selectorValue);
|
|
85
|
+
const parent = getLocator(page, parentInfo[0], parentInfo[1], parentInfo[2]);
|
|
86
|
+
const childType = childInfo[0];
|
|
87
|
+
const childValue = childInfo[1];
|
|
88
|
+
const childOptionsJson = childInfo[2];
|
|
89
|
+
const childOptions = childOptionsJson ? JSON.parse(childOptionsJson) : undefined;
|
|
90
|
+
switch (childType) {
|
|
91
|
+
case "css":
|
|
92
|
+
locator = parent.locator(childValue);
|
|
93
|
+
break;
|
|
94
|
+
case "role": {
|
|
95
|
+
const roleOpts = childOptions ? { ...childOptions } : undefined;
|
|
96
|
+
if (roleOpts) {
|
|
97
|
+
delete roleOpts.nth;
|
|
98
|
+
delete roleOpts.filter;
|
|
99
|
+
if (roleOpts.name && typeof roleOpts.name === "object" && roleOpts.name.$regex) {
|
|
100
|
+
roleOpts.name = new RegExp(roleOpts.name.$regex, roleOpts.name.$flags);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
locator = parent.getByRole(childValue, roleOpts && Object.keys(roleOpts).length > 0 ? roleOpts : undefined);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case "text":
|
|
107
|
+
locator = parent.getByText(childValue);
|
|
108
|
+
break;
|
|
109
|
+
case "label":
|
|
110
|
+
locator = parent.getByLabel(childValue);
|
|
111
|
+
break;
|
|
112
|
+
case "placeholder":
|
|
113
|
+
locator = parent.getByPlaceholder(childValue);
|
|
114
|
+
break;
|
|
115
|
+
case "testId":
|
|
116
|
+
locator = parent.getByTestId(childValue);
|
|
117
|
+
break;
|
|
118
|
+
case "altText":
|
|
119
|
+
locator = parent.getByAltText(childValue);
|
|
120
|
+
break;
|
|
121
|
+
case "title":
|
|
122
|
+
locator = parent.getByTitle(childValue);
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
locator = parent.locator(childValue);
|
|
126
|
+
}
|
|
127
|
+
if (childOptions?.nth !== undefined) {
|
|
128
|
+
locator = locator.nth(childOptions.nth);
|
|
129
|
+
}
|
|
130
|
+
if (childOptions?.filter) {
|
|
131
|
+
const filterOpts = { ...childOptions.filter };
|
|
132
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
133
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
134
|
+
}
|
|
135
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
136
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
137
|
+
}
|
|
138
|
+
if (filterOpts.has && typeof filterOpts.has === "object" && filterOpts.has.$locator) {
|
|
139
|
+
const [type, value, opts] = filterOpts.has.$locator;
|
|
140
|
+
filterOpts.has = getLocator(page, type, value, opts);
|
|
141
|
+
}
|
|
142
|
+
if (filterOpts.hasNot && typeof filterOpts.hasNot === "object" && filterOpts.hasNot.$locator) {
|
|
143
|
+
const [type, value, opts] = filterOpts.hasNot.$locator;
|
|
144
|
+
filterOpts.hasNot = getLocator(page, type, value, opts);
|
|
145
|
+
}
|
|
146
|
+
locator = locator.filter(filterOpts);
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "altText":
|
|
151
|
+
locator = page.getByAltText(selectorValue);
|
|
152
|
+
break;
|
|
153
|
+
case "title":
|
|
154
|
+
locator = page.getByTitle(selectorValue);
|
|
155
|
+
break;
|
|
156
|
+
case "frame": {
|
|
157
|
+
const [frameSelectorInfo, innerLocatorInfo] = JSON.parse(selectorValue);
|
|
158
|
+
const frameSelector = frameSelectorInfo[1];
|
|
159
|
+
const frame = page.frameLocator(frameSelector);
|
|
160
|
+
const innerType = innerLocatorInfo[0];
|
|
161
|
+
const innerValue = innerLocatorInfo[1];
|
|
162
|
+
const innerOptionsJson = innerLocatorInfo[2];
|
|
163
|
+
const innerOptions = innerOptionsJson ? JSON.parse(innerOptionsJson) : undefined;
|
|
164
|
+
switch (innerType) {
|
|
165
|
+
case "css":
|
|
166
|
+
locator = frame.locator(innerValue);
|
|
167
|
+
break;
|
|
168
|
+
case "role": {
|
|
169
|
+
const roleOpts = innerOptions ? { ...innerOptions } : undefined;
|
|
170
|
+
if (roleOpts) {
|
|
171
|
+
delete roleOpts.nth;
|
|
172
|
+
delete roleOpts.filter;
|
|
173
|
+
if (roleOpts.name && typeof roleOpts.name === "object" && roleOpts.name.$regex) {
|
|
174
|
+
roleOpts.name = new RegExp(roleOpts.name.$regex, roleOpts.name.$flags);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
locator = frame.getByRole(innerValue, roleOpts && Object.keys(roleOpts).length > 0 ? roleOpts : undefined);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case "text":
|
|
181
|
+
locator = frame.getByText(innerValue);
|
|
182
|
+
break;
|
|
183
|
+
case "label":
|
|
184
|
+
locator = frame.getByLabel(innerValue);
|
|
185
|
+
break;
|
|
186
|
+
case "placeholder":
|
|
187
|
+
locator = frame.getByPlaceholder(innerValue);
|
|
188
|
+
break;
|
|
189
|
+
case "testId":
|
|
190
|
+
locator = frame.getByTestId(innerValue);
|
|
191
|
+
break;
|
|
192
|
+
case "altText":
|
|
193
|
+
locator = frame.getByAltText(innerValue);
|
|
194
|
+
break;
|
|
195
|
+
case "title":
|
|
196
|
+
locator = frame.getByTitle(innerValue);
|
|
197
|
+
break;
|
|
198
|
+
default:
|
|
199
|
+
locator = frame.locator(innerValue);
|
|
200
|
+
}
|
|
201
|
+
if (innerOptions?.nth !== undefined) {
|
|
202
|
+
locator = locator.nth(innerOptions.nth);
|
|
203
|
+
}
|
|
204
|
+
if (innerOptions?.filter) {
|
|
205
|
+
const filterOpts = { ...innerOptions.filter };
|
|
206
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
207
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
208
|
+
}
|
|
209
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
210
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
211
|
+
}
|
|
212
|
+
locator = locator.filter(filterOpts);
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
62
216
|
default:
|
|
63
217
|
locator = page.locator(selectorValue);
|
|
64
218
|
}
|
|
65
219
|
if (nthIndex !== undefined) {
|
|
66
220
|
locator = locator.nth(nthIndex);
|
|
67
221
|
}
|
|
222
|
+
if (options?.filter) {
|
|
223
|
+
const filterOpts = { ...options.filter };
|
|
224
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
225
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
226
|
+
}
|
|
227
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
228
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
229
|
+
}
|
|
230
|
+
if (filterOpts.has && typeof filterOpts.has === "object" && filterOpts.has.$locator) {
|
|
231
|
+
const [type, value, opts] = filterOpts.has.$locator;
|
|
232
|
+
filterOpts.has = getLocator(page, type, value, opts);
|
|
233
|
+
}
|
|
234
|
+
if (filterOpts.hasNot && typeof filterOpts.hasNot === "object" && filterOpts.hasNot.$locator) {
|
|
235
|
+
const [type, value, opts] = filterOpts.hasNot.$locator;
|
|
236
|
+
filterOpts.hasNot = getLocator(page, type, value, opts);
|
|
237
|
+
}
|
|
238
|
+
locator = locator.filter(filterOpts);
|
|
239
|
+
}
|
|
68
240
|
return locator;
|
|
69
241
|
}
|
|
70
|
-
async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
242
|
+
async function executeLocatorAction(locator, action, actionArg, timeout, fileIO) {
|
|
71
243
|
switch (action) {
|
|
72
244
|
case "click":
|
|
73
245
|
await locator.click({ timeout });
|
|
@@ -114,6 +286,94 @@ async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
|
114
286
|
return await locator.isChecked();
|
|
115
287
|
case "count":
|
|
116
288
|
return await locator.count();
|
|
289
|
+
case "getAttribute":
|
|
290
|
+
return await locator.getAttribute(String(actionArg ?? ""), { timeout });
|
|
291
|
+
case "isDisabled":
|
|
292
|
+
return await locator.isDisabled();
|
|
293
|
+
case "isHidden":
|
|
294
|
+
return await locator.isHidden();
|
|
295
|
+
case "innerHTML":
|
|
296
|
+
return await locator.innerHTML({ timeout });
|
|
297
|
+
case "innerText":
|
|
298
|
+
return await locator.innerText({ timeout });
|
|
299
|
+
case "allTextContents":
|
|
300
|
+
return await locator.allTextContents();
|
|
301
|
+
case "allInnerTexts":
|
|
302
|
+
return await locator.allInnerTexts();
|
|
303
|
+
case "waitFor": {
|
|
304
|
+
const opts = actionArg && typeof actionArg === "object" ? actionArg : {};
|
|
305
|
+
await locator.waitFor({ state: opts.state, timeout: opts.timeout ?? timeout });
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
case "boundingBox":
|
|
309
|
+
return await locator.boundingBox({ timeout });
|
|
310
|
+
case "setInputFiles": {
|
|
311
|
+
const files = actionArg;
|
|
312
|
+
if (Array.isArray(files) && files.length === 0) {
|
|
313
|
+
await locator.setInputFiles([], { timeout });
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
if (Array.isArray(files) && files.length > 0 && typeof files[0] === "object" && "buffer" in files[0]) {
|
|
317
|
+
const fileBuffers2 = files.map((f) => ({
|
|
318
|
+
name: f.name,
|
|
319
|
+
mimeType: f.mimeType,
|
|
320
|
+
buffer: Buffer.from(f.buffer, "base64")
|
|
321
|
+
}));
|
|
322
|
+
await locator.setInputFiles(fileBuffers2, { timeout });
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
const filePaths = Array.isArray(files) ? files : [files];
|
|
326
|
+
if (!fileIO?.readFile) {
|
|
327
|
+
throw new Error("setInputFiles() with file paths requires a readFile callback to be provided. " + "Either provide a readFile callback in defaultPlaywrightHandler options, or pass file data directly " + "as { name, mimeType, buffer } objects.");
|
|
328
|
+
}
|
|
329
|
+
const fileBuffers = await Promise.all(filePaths.map(async (filePath) => {
|
|
330
|
+
const fileData = await fileIO.readFile(filePath);
|
|
331
|
+
return {
|
|
332
|
+
name: fileData.name,
|
|
333
|
+
mimeType: fileData.mimeType,
|
|
334
|
+
buffer: fileData.buffer
|
|
335
|
+
};
|
|
336
|
+
}));
|
|
337
|
+
await locator.setInputFiles(fileBuffers, { timeout });
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
case "screenshot": {
|
|
341
|
+
const opts = actionArg;
|
|
342
|
+
const buffer = await locator.screenshot({
|
|
343
|
+
timeout,
|
|
344
|
+
type: opts?.type,
|
|
345
|
+
quality: opts?.quality
|
|
346
|
+
});
|
|
347
|
+
if (opts?.path) {
|
|
348
|
+
if (!fileIO?.writeFile) {
|
|
349
|
+
throw new Error("screenshot() with path option requires a writeFile callback to be provided. " + "Either provide a writeFile callback in defaultPlaywrightHandler options, or omit the path option " + "and handle the returned base64 data yourself.");
|
|
350
|
+
}
|
|
351
|
+
await fileIO.writeFile(opts.path, buffer);
|
|
352
|
+
}
|
|
353
|
+
return buffer.toString("base64");
|
|
354
|
+
}
|
|
355
|
+
case "dragTo": {
|
|
356
|
+
const targetInfo = actionArg;
|
|
357
|
+
const targetLocator = getLocator(locator.page(), targetInfo[0], targetInfo[1], targetInfo[2]);
|
|
358
|
+
await locator.dragTo(targetLocator, { timeout });
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
case "scrollIntoViewIfNeeded":
|
|
362
|
+
await locator.scrollIntoViewIfNeeded({ timeout });
|
|
363
|
+
return null;
|
|
364
|
+
case "highlight":
|
|
365
|
+
await locator.highlight();
|
|
366
|
+
return null;
|
|
367
|
+
case "evaluate": {
|
|
368
|
+
const [fnString, arg] = actionArg;
|
|
369
|
+
const fn = new Function("return (" + fnString + ")")();
|
|
370
|
+
return await locator.evaluate(fn, arg);
|
|
371
|
+
}
|
|
372
|
+
case "evaluateAll": {
|
|
373
|
+
const [fnString, arg] = actionArg;
|
|
374
|
+
const fn = new Function("return (" + fnString + ")")();
|
|
375
|
+
return await locator.evaluateAll(fn, arg);
|
|
376
|
+
}
|
|
117
377
|
default:
|
|
118
378
|
throw new Error(`Unknown action: ${action}`);
|
|
119
379
|
}
|
|
@@ -133,13 +393,22 @@ async function executeExpectAssertion(locator, matcher, expected, negated, timeo
|
|
|
133
393
|
}
|
|
134
394
|
case "toContainText": {
|
|
135
395
|
const text = await locator.textContent({ timeout });
|
|
136
|
-
|
|
396
|
+
let matches;
|
|
397
|
+
let expectedDisplay;
|
|
398
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
399
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
400
|
+
matches = regex.test(text ?? "");
|
|
401
|
+
expectedDisplay = String(regex);
|
|
402
|
+
} else {
|
|
403
|
+
matches = text?.includes(String(expected)) ?? false;
|
|
404
|
+
expectedDisplay = String(expected);
|
|
405
|
+
}
|
|
137
406
|
if (negated) {
|
|
138
407
|
if (matches)
|
|
139
|
-
throw new Error(`Expected text to not contain
|
|
408
|
+
throw new Error(`Expected text to not contain ${expectedDisplay}, but got "${text}"`);
|
|
140
409
|
} else {
|
|
141
410
|
if (!matches)
|
|
142
|
-
throw new Error(`Expected text to contain
|
|
411
|
+
throw new Error(`Expected text to contain ${expectedDisplay}, but got "${text}"`);
|
|
143
412
|
}
|
|
144
413
|
break;
|
|
145
414
|
}
|
|
@@ -177,71 +446,468 @@ async function executeExpectAssertion(locator, matcher, expected, negated, timeo
|
|
|
177
446
|
}
|
|
178
447
|
break;
|
|
179
448
|
}
|
|
449
|
+
case "toHaveAttribute": {
|
|
450
|
+
const { name, value } = expected;
|
|
451
|
+
const actual = await locator.getAttribute(name, { timeout });
|
|
452
|
+
if (value instanceof RegExp || value && typeof value === "object" && value.$regex) {
|
|
453
|
+
const regex = value.$regex ? new RegExp(value.$regex, value.$flags) : value;
|
|
454
|
+
const matches = regex.test(actual ?? "");
|
|
455
|
+
if (negated) {
|
|
456
|
+
if (matches)
|
|
457
|
+
throw new Error(`Expected attribute "${name}" to not match ${regex}, but got "${actual}"`);
|
|
458
|
+
} else {
|
|
459
|
+
if (!matches)
|
|
460
|
+
throw new Error(`Expected attribute "${name}" to match ${regex}, but got "${actual}"`);
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
const matches = actual === String(value);
|
|
464
|
+
if (negated) {
|
|
465
|
+
if (matches)
|
|
466
|
+
throw new Error(`Expected attribute "${name}" to not be "${value}", but it was`);
|
|
467
|
+
} else {
|
|
468
|
+
if (!matches)
|
|
469
|
+
throw new Error(`Expected attribute "${name}" to be "${value}", but got "${actual}"`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
case "toHaveText": {
|
|
475
|
+
const text = await locator.textContent({ timeout }) ?? "";
|
|
476
|
+
let matches;
|
|
477
|
+
let expectedDisplay;
|
|
478
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
479
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
480
|
+
matches = regex.test(text);
|
|
481
|
+
expectedDisplay = String(regex);
|
|
482
|
+
} else {
|
|
483
|
+
matches = text === String(expected);
|
|
484
|
+
expectedDisplay = JSON.stringify(expected);
|
|
485
|
+
}
|
|
486
|
+
if (negated) {
|
|
487
|
+
if (matches)
|
|
488
|
+
throw new Error(`Expected text to not be ${expectedDisplay}, but got "${text}"`);
|
|
489
|
+
} else {
|
|
490
|
+
if (!matches)
|
|
491
|
+
throw new Error(`Expected text to be ${expectedDisplay}, but got "${text}"`);
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case "toHaveCount": {
|
|
496
|
+
const count = await locator.count();
|
|
497
|
+
const expectedCount = Number(expected);
|
|
498
|
+
if (negated) {
|
|
499
|
+
if (count === expectedCount)
|
|
500
|
+
throw new Error(`Expected count to not be ${expectedCount}, but it was`);
|
|
501
|
+
} else {
|
|
502
|
+
if (count !== expectedCount)
|
|
503
|
+
throw new Error(`Expected count to be ${expectedCount}, but got ${count}`);
|
|
504
|
+
}
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
case "toBeHidden": {
|
|
508
|
+
const isHidden = await locator.isHidden();
|
|
509
|
+
if (negated) {
|
|
510
|
+
if (isHidden)
|
|
511
|
+
throw new Error("Expected element to not be hidden, but it was hidden");
|
|
512
|
+
} else {
|
|
513
|
+
if (!isHidden)
|
|
514
|
+
throw new Error("Expected element to be hidden, but it was not");
|
|
515
|
+
}
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
case "toBeDisabled": {
|
|
519
|
+
const isDisabled = await locator.isDisabled();
|
|
520
|
+
if (negated) {
|
|
521
|
+
if (isDisabled)
|
|
522
|
+
throw new Error("Expected element to not be disabled, but it was disabled");
|
|
523
|
+
} else {
|
|
524
|
+
if (!isDisabled)
|
|
525
|
+
throw new Error("Expected element to be disabled, but it was not");
|
|
526
|
+
}
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
case "toBeFocused": {
|
|
530
|
+
const isFocused = await locator.evaluate((el) => document.activeElement === el).catch(() => false);
|
|
531
|
+
if (negated) {
|
|
532
|
+
if (isFocused)
|
|
533
|
+
throw new Error("Expected element to not be focused, but it was focused");
|
|
534
|
+
} else {
|
|
535
|
+
if (!isFocused)
|
|
536
|
+
throw new Error("Expected element to be focused, but it was not");
|
|
537
|
+
}
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
case "toBeEmpty": {
|
|
541
|
+
const text = await locator.textContent({ timeout });
|
|
542
|
+
const value = await locator.inputValue({ timeout }).catch(() => null);
|
|
543
|
+
const isEmpty = value !== null ? value === "" : (text ?? "") === "";
|
|
544
|
+
if (negated) {
|
|
545
|
+
if (isEmpty)
|
|
546
|
+
throw new Error("Expected element to not be empty, but it was");
|
|
547
|
+
} else {
|
|
548
|
+
if (!isEmpty)
|
|
549
|
+
throw new Error("Expected element to be empty, but it was not");
|
|
550
|
+
}
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
case "toBeAttached": {
|
|
554
|
+
const count = await locator.count();
|
|
555
|
+
const isAttached = count > 0;
|
|
556
|
+
if (negated) {
|
|
557
|
+
if (isAttached)
|
|
558
|
+
throw new Error("Expected element to not be attached to DOM, but it was");
|
|
559
|
+
} else {
|
|
560
|
+
if (!isAttached)
|
|
561
|
+
throw new Error("Expected element to be attached to DOM, but it was not");
|
|
562
|
+
}
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
case "toBeEditable": {
|
|
566
|
+
const isEditable = await locator.isEditable({ timeout });
|
|
567
|
+
if (negated) {
|
|
568
|
+
if (isEditable)
|
|
569
|
+
throw new Error("Expected element to not be editable, but it was");
|
|
570
|
+
} else {
|
|
571
|
+
if (!isEditable)
|
|
572
|
+
throw new Error("Expected element to be editable, but it was not");
|
|
573
|
+
}
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
case "toHaveClass": {
|
|
577
|
+
const classAttr = await locator.getAttribute("class", { timeout }) ?? "";
|
|
578
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
579
|
+
let matches;
|
|
580
|
+
let expectedDisplay;
|
|
581
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
582
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
583
|
+
matches = regex.test(classAttr);
|
|
584
|
+
expectedDisplay = String(regex);
|
|
585
|
+
} else if (Array.isArray(expected)) {
|
|
586
|
+
matches = expected.every((c) => classes.includes(c));
|
|
587
|
+
expectedDisplay = JSON.stringify(expected);
|
|
588
|
+
} else {
|
|
589
|
+
matches = classAttr === String(expected) || classes.includes(String(expected));
|
|
590
|
+
expectedDisplay = String(expected);
|
|
591
|
+
}
|
|
592
|
+
if (negated) {
|
|
593
|
+
if (matches)
|
|
594
|
+
throw new Error(`Expected class to not match ${expectedDisplay}, but got "${classAttr}"`);
|
|
595
|
+
} else {
|
|
596
|
+
if (!matches)
|
|
597
|
+
throw new Error(`Expected class to match ${expectedDisplay}, but got "${classAttr}"`);
|
|
598
|
+
}
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
case "toContainClass": {
|
|
602
|
+
const classAttr = await locator.getAttribute("class", { timeout }) ?? "";
|
|
603
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
604
|
+
const expectedClass = String(expected);
|
|
605
|
+
const hasClass = classes.includes(expectedClass);
|
|
606
|
+
if (negated) {
|
|
607
|
+
if (hasClass)
|
|
608
|
+
throw new Error(`Expected element to not contain class "${expectedClass}", but it does`);
|
|
609
|
+
} else {
|
|
610
|
+
if (!hasClass)
|
|
611
|
+
throw new Error(`Expected element to contain class "${expectedClass}", but classes are "${classAttr}"`);
|
|
612
|
+
}
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
case "toHaveId": {
|
|
616
|
+
const id = await locator.getAttribute("id", { timeout });
|
|
617
|
+
const matches = id === String(expected);
|
|
618
|
+
if (negated) {
|
|
619
|
+
if (matches)
|
|
620
|
+
throw new Error(`Expected id to not be "${expected}", but it was`);
|
|
621
|
+
} else {
|
|
622
|
+
if (!matches)
|
|
623
|
+
throw new Error(`Expected id to be "${expected}", but got "${id}"`);
|
|
624
|
+
}
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case "toBeInViewport": {
|
|
628
|
+
const isInViewport = await locator.evaluate((el) => {
|
|
629
|
+
const rect = el.getBoundingClientRect();
|
|
630
|
+
return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
|
|
631
|
+
});
|
|
632
|
+
if (negated) {
|
|
633
|
+
if (isInViewport)
|
|
634
|
+
throw new Error("Expected element to not be in viewport, but it was");
|
|
635
|
+
} else {
|
|
636
|
+
if (!isInViewport)
|
|
637
|
+
throw new Error("Expected element to be in viewport, but it was not");
|
|
638
|
+
}
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
case "toHaveCSS": {
|
|
642
|
+
const { name, value } = expected;
|
|
643
|
+
const actual = await locator.evaluate((el, propName) => {
|
|
644
|
+
return getComputedStyle(el).getPropertyValue(propName);
|
|
645
|
+
}, name);
|
|
646
|
+
let matches;
|
|
647
|
+
if (value && typeof value === "object" && value.$regex) {
|
|
648
|
+
const regex = new RegExp(value.$regex, value.$flags);
|
|
649
|
+
matches = regex.test(actual);
|
|
650
|
+
} else {
|
|
651
|
+
matches = actual === String(value);
|
|
652
|
+
}
|
|
653
|
+
if (negated) {
|
|
654
|
+
if (matches)
|
|
655
|
+
throw new Error(`Expected CSS "${name}" to not be "${value}", but it was`);
|
|
656
|
+
} else {
|
|
657
|
+
if (!matches)
|
|
658
|
+
throw new Error(`Expected CSS "${name}" to be "${value}", but got "${actual}"`);
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
case "toHaveJSProperty": {
|
|
663
|
+
const { name, value } = expected;
|
|
664
|
+
const actual = await locator.evaluate((el, propName) => {
|
|
665
|
+
return el[propName];
|
|
666
|
+
}, name);
|
|
667
|
+
const matches = JSON.stringify(actual) === JSON.stringify(value);
|
|
668
|
+
if (negated) {
|
|
669
|
+
if (matches)
|
|
670
|
+
throw new Error(`Expected JS property "${name}" to not be ${JSON.stringify(value)}, but it was`);
|
|
671
|
+
} else {
|
|
672
|
+
if (!matches)
|
|
673
|
+
throw new Error(`Expected JS property "${name}" to be ${JSON.stringify(value)}, but got ${JSON.stringify(actual)}`);
|
|
674
|
+
}
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
case "toHaveAccessibleName": {
|
|
678
|
+
const accessibleName = await locator.evaluate((el) => {
|
|
679
|
+
return el.getAttribute("aria-label") || el.getAttribute("aria-labelledby") || el.innerText || "";
|
|
680
|
+
});
|
|
681
|
+
let matches;
|
|
682
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
683
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
684
|
+
matches = regex.test(accessibleName);
|
|
685
|
+
} else {
|
|
686
|
+
matches = accessibleName === String(expected);
|
|
687
|
+
}
|
|
688
|
+
if (negated) {
|
|
689
|
+
if (matches)
|
|
690
|
+
throw new Error(`Expected accessible name to not be "${expected}", but it was`);
|
|
691
|
+
} else {
|
|
692
|
+
if (!matches)
|
|
693
|
+
throw new Error(`Expected accessible name to be "${expected}", but got "${accessibleName}"`);
|
|
694
|
+
}
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
case "toHaveAccessibleDescription": {
|
|
698
|
+
const accessibleDesc = await locator.evaluate((el) => {
|
|
699
|
+
const describedby = el.getAttribute("aria-describedby");
|
|
700
|
+
if (describedby) {
|
|
701
|
+
const descEl = document.getElementById(describedby);
|
|
702
|
+
return descEl?.textContent || "";
|
|
703
|
+
}
|
|
704
|
+
return el.getAttribute("aria-description") || "";
|
|
705
|
+
});
|
|
706
|
+
let matches;
|
|
707
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
708
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
709
|
+
matches = regex.test(accessibleDesc);
|
|
710
|
+
} else {
|
|
711
|
+
matches = accessibleDesc === String(expected);
|
|
712
|
+
}
|
|
713
|
+
if (negated) {
|
|
714
|
+
if (matches)
|
|
715
|
+
throw new Error(`Expected accessible description to not be "${expected}", but it was`);
|
|
716
|
+
} else {
|
|
717
|
+
if (!matches)
|
|
718
|
+
throw new Error(`Expected accessible description to be "${expected}", but got "${accessibleDesc}"`);
|
|
719
|
+
}
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
case "toHaveRole": {
|
|
723
|
+
const role = await locator.evaluate((el) => {
|
|
724
|
+
return el.getAttribute("role") || el.tagName.toLowerCase();
|
|
725
|
+
});
|
|
726
|
+
const matches = role === String(expected);
|
|
727
|
+
if (negated) {
|
|
728
|
+
if (matches)
|
|
729
|
+
throw new Error(`Expected role to not be "${expected}", but it was`);
|
|
730
|
+
} else {
|
|
731
|
+
if (!matches)
|
|
732
|
+
throw new Error(`Expected role to be "${expected}", but got "${role}"`);
|
|
733
|
+
}
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
180
736
|
default:
|
|
181
737
|
throw new Error(`Unknown matcher: ${matcher}`);
|
|
182
738
|
}
|
|
183
739
|
}
|
|
740
|
+
async function executePageExpectAssertion(page, matcher, expected, negated, timeout) {
|
|
741
|
+
let expectedValue = expected;
|
|
742
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
743
|
+
expectedValue = new RegExp(expected.$regex, expected.$flags);
|
|
744
|
+
}
|
|
745
|
+
switch (matcher) {
|
|
746
|
+
case "toHaveURL": {
|
|
747
|
+
const expectedUrl = expectedValue;
|
|
748
|
+
const startTime = Date.now();
|
|
749
|
+
let lastUrl = "";
|
|
750
|
+
while (Date.now() - startTime < timeout) {
|
|
751
|
+
lastUrl = page.url();
|
|
752
|
+
const matches = expectedUrl instanceof RegExp ? expectedUrl.test(lastUrl) : lastUrl === expectedUrl;
|
|
753
|
+
if (negated ? !matches : matches)
|
|
754
|
+
return;
|
|
755
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
756
|
+
}
|
|
757
|
+
if (negated) {
|
|
758
|
+
throw new Error(`Expected URL to not match "${expectedUrl}", but got "${lastUrl}"`);
|
|
759
|
+
} else {
|
|
760
|
+
throw new Error(`Expected URL to be "${expectedUrl}", but got "${lastUrl}"`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
case "toHaveTitle": {
|
|
764
|
+
const expectedTitle = expectedValue;
|
|
765
|
+
const startTime = Date.now();
|
|
766
|
+
let lastTitle = "";
|
|
767
|
+
while (Date.now() - startTime < timeout) {
|
|
768
|
+
lastTitle = await page.title();
|
|
769
|
+
const matches = expectedTitle instanceof RegExp ? expectedTitle.test(lastTitle) : lastTitle === expectedTitle;
|
|
770
|
+
if (negated ? !matches : matches)
|
|
771
|
+
return;
|
|
772
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
773
|
+
}
|
|
774
|
+
if (negated) {
|
|
775
|
+
throw new Error(`Expected title to not match "${expectedTitle}", but got "${lastTitle}"`);
|
|
776
|
+
} else {
|
|
777
|
+
throw new Error(`Expected title to be "${expectedTitle}", but got "${lastTitle}"`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
default:
|
|
781
|
+
throw new Error(`Unknown page matcher: ${matcher}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
184
784
|
function createPlaywrightHandler(page, options) {
|
|
185
785
|
const timeout = options?.timeout ?? 30000;
|
|
186
|
-
const
|
|
786
|
+
const fileIO = {
|
|
787
|
+
readFile: options?.readFile,
|
|
788
|
+
writeFile: options?.writeFile
|
|
789
|
+
};
|
|
790
|
+
const registry = {
|
|
791
|
+
pages: new Map([["page_0", page]]),
|
|
792
|
+
contexts: new Map([["ctx_0", page.context()]]),
|
|
793
|
+
nextPageId: 1,
|
|
794
|
+
nextContextId: 1
|
|
795
|
+
};
|
|
187
796
|
return async (op) => {
|
|
188
797
|
try {
|
|
798
|
+
switch (op.type) {
|
|
799
|
+
case "newContext": {
|
|
800
|
+
if (!options?.createContext) {
|
|
801
|
+
return { ok: false, error: { name: "Error", message: "createContext callback not provided. Configure createContext in playwright options to enable browser.newContext()." } };
|
|
802
|
+
}
|
|
803
|
+
const [contextOptions] = op.args;
|
|
804
|
+
const newContext = await options.createContext(contextOptions);
|
|
805
|
+
const contextId2 = `ctx_${registry.nextContextId++}`;
|
|
806
|
+
registry.contexts.set(contextId2, newContext);
|
|
807
|
+
return { ok: true, value: { contextId: contextId2 } };
|
|
808
|
+
}
|
|
809
|
+
case "newPage": {
|
|
810
|
+
if (!options?.createPage) {
|
|
811
|
+
return { ok: false, error: { name: "Error", message: "createPage callback not provided. Configure createPage in playwright options to enable context.newPage()." } };
|
|
812
|
+
}
|
|
813
|
+
const contextId2 = op.contextId ?? "ctx_0";
|
|
814
|
+
const targetContext2 = registry.contexts.get(contextId2);
|
|
815
|
+
if (!targetContext2) {
|
|
816
|
+
return { ok: false, error: { name: "Error", message: `Context ${contextId2} not found` } };
|
|
817
|
+
}
|
|
818
|
+
const newPage = await options.createPage(targetContext2);
|
|
819
|
+
const pageId2 = `page_${registry.nextPageId++}`;
|
|
820
|
+
registry.pages.set(pageId2, newPage);
|
|
821
|
+
return { ok: true, value: { pageId: pageId2 } };
|
|
822
|
+
}
|
|
823
|
+
case "closeContext": {
|
|
824
|
+
const contextId2 = op.contextId ?? "ctx_0";
|
|
825
|
+
const context = registry.contexts.get(contextId2);
|
|
826
|
+
if (!context) {
|
|
827
|
+
return { ok: false, error: { name: "Error", message: `Context ${contextId2} not found` } };
|
|
828
|
+
}
|
|
829
|
+
await context.close();
|
|
830
|
+
registry.contexts.delete(contextId2);
|
|
831
|
+
for (const [pid, p] of registry.pages) {
|
|
832
|
+
if (p.context() === context) {
|
|
833
|
+
registry.pages.delete(pid);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return { ok: true };
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
const pageId = op.pageId ?? "page_0";
|
|
840
|
+
const targetPage = registry.pages.get(pageId);
|
|
841
|
+
if (!targetPage) {
|
|
842
|
+
return { ok: false, error: { name: "Error", message: `Page ${pageId} not found` } };
|
|
843
|
+
}
|
|
844
|
+
const contextId = op.contextId ?? "ctx_0";
|
|
845
|
+
const targetContext = registry.contexts.get(contextId);
|
|
189
846
|
switch (op.type) {
|
|
190
847
|
case "goto": {
|
|
191
848
|
const [url, waitUntil] = op.args;
|
|
192
|
-
|
|
193
|
-
await page.goto(targetUrl, {
|
|
849
|
+
await targetPage.goto(url, {
|
|
194
850
|
timeout,
|
|
195
851
|
waitUntil: waitUntil ?? "load"
|
|
196
852
|
});
|
|
197
853
|
return { ok: true };
|
|
198
854
|
}
|
|
199
855
|
case "reload":
|
|
200
|
-
await
|
|
856
|
+
await targetPage.reload({ timeout });
|
|
201
857
|
return { ok: true };
|
|
202
858
|
case "url":
|
|
203
|
-
return { ok: true, value:
|
|
859
|
+
return { ok: true, value: targetPage.url() };
|
|
204
860
|
case "title":
|
|
205
|
-
return { ok: true, value: await
|
|
861
|
+
return { ok: true, value: await targetPage.title() };
|
|
206
862
|
case "content":
|
|
207
|
-
return { ok: true, value: await
|
|
863
|
+
return { ok: true, value: await targetPage.content() };
|
|
208
864
|
case "waitForSelector": {
|
|
209
865
|
const [selector, optionsJson] = op.args;
|
|
210
866
|
const opts = optionsJson ? JSON.parse(optionsJson) : {};
|
|
211
|
-
await
|
|
867
|
+
await targetPage.waitForSelector(selector, { timeout, ...opts });
|
|
212
868
|
return { ok: true };
|
|
213
869
|
}
|
|
214
870
|
case "waitForTimeout": {
|
|
215
871
|
const [ms] = op.args;
|
|
216
|
-
await
|
|
872
|
+
await targetPage.waitForTimeout(ms);
|
|
217
873
|
return { ok: true };
|
|
218
874
|
}
|
|
219
875
|
case "waitForLoadState": {
|
|
220
876
|
const [state] = op.args;
|
|
221
|
-
await
|
|
877
|
+
await targetPage.waitForLoadState(state ?? "load", { timeout });
|
|
222
878
|
return { ok: true };
|
|
223
879
|
}
|
|
224
880
|
case "evaluate": {
|
|
225
|
-
const [script] = op.args;
|
|
226
|
-
|
|
881
|
+
const [script, arg] = op.args;
|
|
882
|
+
if (op.args.length > 1) {
|
|
883
|
+
const fn = new Function("return (" + script + ")")();
|
|
884
|
+
const result2 = await targetPage.evaluate(fn, arg);
|
|
885
|
+
return { ok: true, value: result2 };
|
|
886
|
+
}
|
|
887
|
+
const result = await targetPage.evaluate(script);
|
|
227
888
|
return { ok: true, value: result };
|
|
228
889
|
}
|
|
229
890
|
case "locatorAction": {
|
|
230
891
|
const [selectorType, selectorValue, roleOptions, action, actionArg] = op.args;
|
|
231
|
-
const locator = getLocator(
|
|
232
|
-
const result = await executeLocatorAction(locator, action, actionArg, timeout);
|
|
892
|
+
const locator = getLocator(targetPage, selectorType, selectorValue, roleOptions);
|
|
893
|
+
const result = await executeLocatorAction(locator, action, actionArg, timeout, fileIO);
|
|
233
894
|
return { ok: true, value: result };
|
|
234
895
|
}
|
|
235
896
|
case "expectLocator": {
|
|
236
897
|
const [selectorType, selectorValue, roleOptions, matcher, expected, negated, customTimeout] = op.args;
|
|
237
|
-
const locator = getLocator(
|
|
898
|
+
const locator = getLocator(targetPage, selectorType, selectorValue, roleOptions);
|
|
238
899
|
const effectiveTimeout = customTimeout ?? timeout;
|
|
239
900
|
await executeExpectAssertion(locator, matcher, expected, negated ?? false, effectiveTimeout);
|
|
240
901
|
return { ok: true };
|
|
241
902
|
}
|
|
903
|
+
case "expectPage": {
|
|
904
|
+
const [matcher, expected, negated, customTimeout] = op.args;
|
|
905
|
+
const effectiveTimeout = customTimeout ?? timeout;
|
|
906
|
+
await executePageExpectAssertion(targetPage, matcher, expected, negated ?? false, effectiveTimeout);
|
|
907
|
+
return { ok: true };
|
|
908
|
+
}
|
|
242
909
|
case "request": {
|
|
243
910
|
const [url, method, data, headers] = op.args;
|
|
244
|
-
const targetUrl = baseUrl && !url.startsWith("http") ? `${baseUrl}${url}` : url;
|
|
245
911
|
const requestOptions = {
|
|
246
912
|
timeout
|
|
247
913
|
};
|
|
@@ -251,7 +917,7 @@ function createPlaywrightHandler(page, options) {
|
|
|
251
917
|
if (data !== undefined && data !== null) {
|
|
252
918
|
requestOptions.data = data;
|
|
253
919
|
}
|
|
254
|
-
const response = await
|
|
920
|
+
const response = await targetPage.request.fetch(url, {
|
|
255
921
|
method,
|
|
256
922
|
...requestOptions
|
|
257
923
|
});
|
|
@@ -272,6 +938,176 @@ function createPlaywrightHandler(page, options) {
|
|
|
272
938
|
}
|
|
273
939
|
};
|
|
274
940
|
}
|
|
941
|
+
case "goBack": {
|
|
942
|
+
const [waitUntil] = op.args;
|
|
943
|
+
await targetPage.goBack({
|
|
944
|
+
timeout,
|
|
945
|
+
waitUntil: waitUntil ?? "load"
|
|
946
|
+
});
|
|
947
|
+
return { ok: true };
|
|
948
|
+
}
|
|
949
|
+
case "goForward": {
|
|
950
|
+
const [waitUntil] = op.args;
|
|
951
|
+
await targetPage.goForward({
|
|
952
|
+
timeout,
|
|
953
|
+
waitUntil: waitUntil ?? "load"
|
|
954
|
+
});
|
|
955
|
+
return { ok: true };
|
|
956
|
+
}
|
|
957
|
+
case "waitForURL": {
|
|
958
|
+
const [urlArg, customTimeout, waitUntil] = op.args;
|
|
959
|
+
const url = urlArg && typeof urlArg === "object" && "$regex" in urlArg ? new RegExp(urlArg.$regex, urlArg.$flags) : urlArg;
|
|
960
|
+
await targetPage.waitForURL(url, {
|
|
961
|
+
timeout: customTimeout ?? timeout,
|
|
962
|
+
waitUntil: waitUntil ?? undefined
|
|
963
|
+
});
|
|
964
|
+
return { ok: true };
|
|
965
|
+
}
|
|
966
|
+
case "clearCookies": {
|
|
967
|
+
const ctx = targetContext ?? targetPage.context();
|
|
968
|
+
await ctx.clearCookies();
|
|
969
|
+
return { ok: true };
|
|
970
|
+
}
|
|
971
|
+
case "screenshot": {
|
|
972
|
+
const [screenshotOptions] = op.args;
|
|
973
|
+
const buffer = await targetPage.screenshot({
|
|
974
|
+
type: screenshotOptions?.type,
|
|
975
|
+
quality: screenshotOptions?.quality,
|
|
976
|
+
fullPage: screenshotOptions?.fullPage,
|
|
977
|
+
clip: screenshotOptions?.clip
|
|
978
|
+
});
|
|
979
|
+
if (screenshotOptions?.path) {
|
|
980
|
+
if (!fileIO.writeFile) {
|
|
981
|
+
throw new Error("screenshot() with path option requires a writeFile callback to be provided. " + "Either provide a writeFile callback in defaultPlaywrightHandler options, or omit the path option " + "and handle the returned base64 data yourself.");
|
|
982
|
+
}
|
|
983
|
+
await fileIO.writeFile(screenshotOptions.path, buffer);
|
|
984
|
+
}
|
|
985
|
+
return { ok: true, value: buffer.toString("base64") };
|
|
986
|
+
}
|
|
987
|
+
case "setViewportSize": {
|
|
988
|
+
const [size] = op.args;
|
|
989
|
+
await targetPage.setViewportSize(size);
|
|
990
|
+
return { ok: true };
|
|
991
|
+
}
|
|
992
|
+
case "viewportSize": {
|
|
993
|
+
return { ok: true, value: targetPage.viewportSize() };
|
|
994
|
+
}
|
|
995
|
+
case "keyboardType": {
|
|
996
|
+
const [text, typeOptions] = op.args;
|
|
997
|
+
await targetPage.keyboard.type(text, typeOptions);
|
|
998
|
+
return { ok: true };
|
|
999
|
+
}
|
|
1000
|
+
case "keyboardPress": {
|
|
1001
|
+
const [key, pressOptions] = op.args;
|
|
1002
|
+
await targetPage.keyboard.press(key, pressOptions);
|
|
1003
|
+
return { ok: true };
|
|
1004
|
+
}
|
|
1005
|
+
case "keyboardDown": {
|
|
1006
|
+
const [key] = op.args;
|
|
1007
|
+
await targetPage.keyboard.down(key);
|
|
1008
|
+
return { ok: true };
|
|
1009
|
+
}
|
|
1010
|
+
case "keyboardUp": {
|
|
1011
|
+
const [key] = op.args;
|
|
1012
|
+
await targetPage.keyboard.up(key);
|
|
1013
|
+
return { ok: true };
|
|
1014
|
+
}
|
|
1015
|
+
case "keyboardInsertText": {
|
|
1016
|
+
const [text] = op.args;
|
|
1017
|
+
await targetPage.keyboard.insertText(text);
|
|
1018
|
+
return { ok: true };
|
|
1019
|
+
}
|
|
1020
|
+
case "mouseMove": {
|
|
1021
|
+
const [x, y, moveOptions] = op.args;
|
|
1022
|
+
await targetPage.mouse.move(x, y, moveOptions);
|
|
1023
|
+
return { ok: true };
|
|
1024
|
+
}
|
|
1025
|
+
case "mouseClick": {
|
|
1026
|
+
const [x, y, clickOptions] = op.args;
|
|
1027
|
+
await targetPage.mouse.click(x, y, clickOptions);
|
|
1028
|
+
return { ok: true };
|
|
1029
|
+
}
|
|
1030
|
+
case "mouseDown": {
|
|
1031
|
+
const [downOptions] = op.args;
|
|
1032
|
+
if (downOptions) {
|
|
1033
|
+
await targetPage.mouse.down(downOptions);
|
|
1034
|
+
} else {
|
|
1035
|
+
await targetPage.mouse.down();
|
|
1036
|
+
}
|
|
1037
|
+
return { ok: true };
|
|
1038
|
+
}
|
|
1039
|
+
case "mouseUp": {
|
|
1040
|
+
const [upOptions] = op.args;
|
|
1041
|
+
if (upOptions) {
|
|
1042
|
+
await targetPage.mouse.up(upOptions);
|
|
1043
|
+
} else {
|
|
1044
|
+
await targetPage.mouse.up();
|
|
1045
|
+
}
|
|
1046
|
+
return { ok: true };
|
|
1047
|
+
}
|
|
1048
|
+
case "mouseWheel": {
|
|
1049
|
+
const [deltaX, deltaY] = op.args;
|
|
1050
|
+
await targetPage.mouse.wheel(deltaX, deltaY);
|
|
1051
|
+
return { ok: true };
|
|
1052
|
+
}
|
|
1053
|
+
case "frames": {
|
|
1054
|
+
const frames = targetPage.frames();
|
|
1055
|
+
return { ok: true, value: frames.map((f) => ({ name: f.name(), url: f.url() })) };
|
|
1056
|
+
}
|
|
1057
|
+
case "mainFrame": {
|
|
1058
|
+
const mainFrame = targetPage.mainFrame();
|
|
1059
|
+
return { ok: true, value: { name: mainFrame.name(), url: mainFrame.url() } };
|
|
1060
|
+
}
|
|
1061
|
+
case "bringToFront": {
|
|
1062
|
+
await targetPage.bringToFront();
|
|
1063
|
+
return { ok: true };
|
|
1064
|
+
}
|
|
1065
|
+
case "close": {
|
|
1066
|
+
await targetPage.close();
|
|
1067
|
+
registry.pages.delete(pageId);
|
|
1068
|
+
return { ok: true };
|
|
1069
|
+
}
|
|
1070
|
+
case "isClosed": {
|
|
1071
|
+
return { ok: true, value: targetPage.isClosed() };
|
|
1072
|
+
}
|
|
1073
|
+
case "pdf": {
|
|
1074
|
+
const [pdfOptions] = op.args;
|
|
1075
|
+
const { path: pdfPath, ...restPdfOptions } = pdfOptions ?? {};
|
|
1076
|
+
const buffer = await targetPage.pdf(restPdfOptions);
|
|
1077
|
+
if (pdfPath) {
|
|
1078
|
+
if (!fileIO.writeFile) {
|
|
1079
|
+
throw new Error("pdf() with path option requires a writeFile callback to be provided. " + "Either provide a writeFile callback in defaultPlaywrightHandler options, or omit the path option " + "and handle the returned base64 data yourself.");
|
|
1080
|
+
}
|
|
1081
|
+
await fileIO.writeFile(pdfPath, buffer);
|
|
1082
|
+
}
|
|
1083
|
+
return { ok: true, value: buffer.toString("base64") };
|
|
1084
|
+
}
|
|
1085
|
+
case "emulateMedia": {
|
|
1086
|
+
const [mediaOptions] = op.args;
|
|
1087
|
+
await targetPage.emulateMedia(mediaOptions);
|
|
1088
|
+
return { ok: true };
|
|
1089
|
+
}
|
|
1090
|
+
case "addCookies": {
|
|
1091
|
+
const [cookies] = op.args;
|
|
1092
|
+
const ctx = targetContext ?? targetPage.context();
|
|
1093
|
+
await ctx.addCookies(cookies);
|
|
1094
|
+
return { ok: true };
|
|
1095
|
+
}
|
|
1096
|
+
case "cookies": {
|
|
1097
|
+
const [urls] = op.args;
|
|
1098
|
+
const ctx = targetContext ?? targetPage.context();
|
|
1099
|
+
const cookies = await ctx.cookies(urls);
|
|
1100
|
+
return { ok: true, value: cookies };
|
|
1101
|
+
}
|
|
1102
|
+
case "setExtraHTTPHeaders": {
|
|
1103
|
+
const [headers] = op.args;
|
|
1104
|
+
await targetPage.setExtraHTTPHeaders(headers);
|
|
1105
|
+
return { ok: true };
|
|
1106
|
+
}
|
|
1107
|
+
case "pause": {
|
|
1108
|
+
await targetPage.pause();
|
|
1109
|
+
return { ok: true };
|
|
1110
|
+
}
|
|
275
1111
|
default:
|
|
276
1112
|
return { ok: false, error: { name: "Error", message: `Unknown operation: ${op.type}` } };
|
|
277
1113
|
}
|
|
@@ -281,5 +1117,13 @@ function createPlaywrightHandler(page, options) {
|
|
|
281
1117
|
}
|
|
282
1118
|
};
|
|
283
1119
|
}
|
|
1120
|
+
function defaultPlaywrightHandler(page, options) {
|
|
1121
|
+
const handler = createPlaywrightHandler(page, options);
|
|
1122
|
+
handler[import_types.DEFAULT_PLAYWRIGHT_HANDLER_META] = { page, options };
|
|
1123
|
+
return handler;
|
|
1124
|
+
}
|
|
1125
|
+
function getDefaultPlaywrightHandlerMetadata(handler) {
|
|
1126
|
+
return handler[import_types.DEFAULT_PLAYWRIGHT_HANDLER_META];
|
|
1127
|
+
}
|
|
284
1128
|
|
|
285
|
-
//# debugId=
|
|
1129
|
+
//# debugId=F37479A1CB5EC1B964756E2164756E21
|