@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/index.cjs
CHANGED
|
@@ -43,10 +43,14 @@ var __export = (target, all) => {
|
|
|
43
43
|
var exports_src = {};
|
|
44
44
|
__export(exports_src, {
|
|
45
45
|
setupPlaywright: () => setupPlaywright,
|
|
46
|
-
|
|
46
|
+
getDefaultPlaywrightHandlerMetadata: () => getDefaultPlaywrightHandlerMetadata,
|
|
47
|
+
defaultPlaywrightHandler: () => defaultPlaywrightHandler,
|
|
48
|
+
createPlaywrightHandler: () => createPlaywrightHandler,
|
|
49
|
+
DEFAULT_PLAYWRIGHT_HANDLER_META: () => import_types.DEFAULT_PLAYWRIGHT_HANDLER_META
|
|
47
50
|
});
|
|
48
51
|
module.exports = __toCommonJS(exports_src);
|
|
49
52
|
var import_isolated_vm = __toESM(require("isolated-vm"));
|
|
53
|
+
var import_types = require("./types.cjs");
|
|
50
54
|
function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
51
55
|
const options = optionsJson ? JSON.parse(optionsJson) : undefined;
|
|
52
56
|
const nthIndex = options?.nth;
|
|
@@ -78,6 +82,145 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
|
78
82
|
case "testId":
|
|
79
83
|
locator = page.getByTestId(selectorValue);
|
|
80
84
|
break;
|
|
85
|
+
case "or": {
|
|
86
|
+
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
87
|
+
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
88
|
+
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
89
|
+
locator = first.or(second);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case "and": {
|
|
93
|
+
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
94
|
+
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
95
|
+
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
96
|
+
locator = first.and(second);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "chained": {
|
|
100
|
+
const [parentInfo, childInfo] = JSON.parse(selectorValue);
|
|
101
|
+
const parent = getLocator(page, parentInfo[0], parentInfo[1], parentInfo[2]);
|
|
102
|
+
const childType = childInfo[0];
|
|
103
|
+
const childValue = childInfo[1];
|
|
104
|
+
const childOptionsJson = childInfo[2];
|
|
105
|
+
const childOptions = childOptionsJson ? JSON.parse(childOptionsJson) : undefined;
|
|
106
|
+
switch (childType) {
|
|
107
|
+
case "css":
|
|
108
|
+
locator = parent.locator(childValue);
|
|
109
|
+
break;
|
|
110
|
+
case "role": {
|
|
111
|
+
const roleOpts = childOptions ? { ...childOptions } : undefined;
|
|
112
|
+
if (roleOpts) {
|
|
113
|
+
delete roleOpts.nth;
|
|
114
|
+
delete roleOpts.filter;
|
|
115
|
+
if (roleOpts.name && typeof roleOpts.name === "object" && roleOpts.name.$regex) {
|
|
116
|
+
roleOpts.name = new RegExp(roleOpts.name.$regex, roleOpts.name.$flags);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
locator = parent.getByRole(childValue, roleOpts && Object.keys(roleOpts).length > 0 ? roleOpts : undefined);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case "text":
|
|
123
|
+
locator = parent.getByText(childValue);
|
|
124
|
+
break;
|
|
125
|
+
case "label":
|
|
126
|
+
locator = parent.getByLabel(childValue);
|
|
127
|
+
break;
|
|
128
|
+
case "placeholder":
|
|
129
|
+
locator = parent.getByPlaceholder(childValue);
|
|
130
|
+
break;
|
|
131
|
+
case "testId":
|
|
132
|
+
locator = parent.getByTestId(childValue);
|
|
133
|
+
break;
|
|
134
|
+
case "altText":
|
|
135
|
+
locator = parent.getByAltText(childValue);
|
|
136
|
+
break;
|
|
137
|
+
case "title":
|
|
138
|
+
locator = parent.getByTitle(childValue);
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
locator = parent.locator(childValue);
|
|
142
|
+
}
|
|
143
|
+
if (childOptions?.nth !== undefined) {
|
|
144
|
+
locator = locator.nth(childOptions.nth);
|
|
145
|
+
}
|
|
146
|
+
if (childOptions?.filter) {
|
|
147
|
+
const filterOpts = { ...childOptions.filter };
|
|
148
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
149
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
150
|
+
}
|
|
151
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
152
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
153
|
+
}
|
|
154
|
+
locator = locator.filter(filterOpts);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "altText":
|
|
159
|
+
locator = page.getByAltText(selectorValue);
|
|
160
|
+
break;
|
|
161
|
+
case "title":
|
|
162
|
+
locator = page.getByTitle(selectorValue);
|
|
163
|
+
break;
|
|
164
|
+
case "frame": {
|
|
165
|
+
const [frameSelectorInfo, innerLocatorInfo] = JSON.parse(selectorValue);
|
|
166
|
+
const frameSelector = frameSelectorInfo[1];
|
|
167
|
+
const frame = page.frameLocator(frameSelector);
|
|
168
|
+
const innerType = innerLocatorInfo[0];
|
|
169
|
+
const innerValue = innerLocatorInfo[1];
|
|
170
|
+
const innerOptionsJson = innerLocatorInfo[2];
|
|
171
|
+
const innerOptions = innerOptionsJson ? JSON.parse(innerOptionsJson) : undefined;
|
|
172
|
+
switch (innerType) {
|
|
173
|
+
case "css":
|
|
174
|
+
locator = frame.locator(innerValue);
|
|
175
|
+
break;
|
|
176
|
+
case "role": {
|
|
177
|
+
const roleOpts = innerOptions ? { ...innerOptions } : undefined;
|
|
178
|
+
if (roleOpts) {
|
|
179
|
+
delete roleOpts.nth;
|
|
180
|
+
delete roleOpts.filter;
|
|
181
|
+
if (roleOpts.name && typeof roleOpts.name === "object" && roleOpts.name.$regex) {
|
|
182
|
+
roleOpts.name = new RegExp(roleOpts.name.$regex, roleOpts.name.$flags);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
locator = frame.getByRole(innerValue, roleOpts && Object.keys(roleOpts).length > 0 ? roleOpts : undefined);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case "text":
|
|
189
|
+
locator = frame.getByText(innerValue);
|
|
190
|
+
break;
|
|
191
|
+
case "label":
|
|
192
|
+
locator = frame.getByLabel(innerValue);
|
|
193
|
+
break;
|
|
194
|
+
case "placeholder":
|
|
195
|
+
locator = frame.getByPlaceholder(innerValue);
|
|
196
|
+
break;
|
|
197
|
+
case "testId":
|
|
198
|
+
locator = frame.getByTestId(innerValue);
|
|
199
|
+
break;
|
|
200
|
+
case "altText":
|
|
201
|
+
locator = frame.getByAltText(innerValue);
|
|
202
|
+
break;
|
|
203
|
+
case "title":
|
|
204
|
+
locator = frame.getByTitle(innerValue);
|
|
205
|
+
break;
|
|
206
|
+
default:
|
|
207
|
+
locator = frame.locator(innerValue);
|
|
208
|
+
}
|
|
209
|
+
if (innerOptions?.nth !== undefined) {
|
|
210
|
+
locator = locator.nth(innerOptions.nth);
|
|
211
|
+
}
|
|
212
|
+
if (innerOptions?.filter) {
|
|
213
|
+
const filterOpts = { ...innerOptions.filter };
|
|
214
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
215
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
216
|
+
}
|
|
217
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
218
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
219
|
+
}
|
|
220
|
+
locator = locator.filter(filterOpts);
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
81
224
|
default:
|
|
82
225
|
locator = page.locator(selectorValue);
|
|
83
226
|
}
|
|
@@ -96,7 +239,7 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
|
96
239
|
}
|
|
97
240
|
return locator;
|
|
98
241
|
}
|
|
99
|
-
async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
242
|
+
async function executeLocatorAction(locator, action, actionArg, timeout, fileIO) {
|
|
100
243
|
switch (action) {
|
|
101
244
|
case "click":
|
|
102
245
|
await locator.click({ timeout });
|
|
@@ -164,6 +307,69 @@ async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
|
164
307
|
}
|
|
165
308
|
case "boundingBox":
|
|
166
309
|
return await locator.boundingBox({ timeout });
|
|
310
|
+
case "setInputFiles": {
|
|
311
|
+
const files = actionArg;
|
|
312
|
+
if (Array.isArray(files) && files.length > 0 && typeof files[0] === "object" && "buffer" in files[0]) {
|
|
313
|
+
const fileBuffers2 = files.map((f) => ({
|
|
314
|
+
name: f.name,
|
|
315
|
+
mimeType: f.mimeType,
|
|
316
|
+
buffer: Buffer.from(f.buffer, "base64")
|
|
317
|
+
}));
|
|
318
|
+
await locator.setInputFiles(fileBuffers2, { timeout });
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
const filePaths = Array.isArray(files) ? files : [files];
|
|
322
|
+
if (!fileIO?.readFile) {
|
|
323
|
+
throw new Error("setInputFiles() with file paths requires a readFile callback in defaultPlaywrightHandler options. " + "Either provide file data directly using { name, mimeType, buffer } format, or " + "configure a readFile callback to control file access from the isolate.");
|
|
324
|
+
}
|
|
325
|
+
const fileBuffers = await Promise.all(filePaths.map(async (filePath) => {
|
|
326
|
+
const fileData = await fileIO.readFile(filePath);
|
|
327
|
+
return {
|
|
328
|
+
name: fileData.name,
|
|
329
|
+
mimeType: fileData.mimeType,
|
|
330
|
+
buffer: fileData.buffer
|
|
331
|
+
};
|
|
332
|
+
}));
|
|
333
|
+
await locator.setInputFiles(fileBuffers, { timeout });
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
case "screenshot": {
|
|
337
|
+
const opts = actionArg;
|
|
338
|
+
const buffer = await locator.screenshot({
|
|
339
|
+
timeout,
|
|
340
|
+
type: opts?.type,
|
|
341
|
+
quality: opts?.quality
|
|
342
|
+
});
|
|
343
|
+
if (opts?.path) {
|
|
344
|
+
if (!fileIO?.writeFile) {
|
|
345
|
+
throw new Error("screenshot() with path option requires a writeFile callback in defaultPlaywrightHandler options. " + "Either omit the path option (screenshot returns base64 data), or " + "configure a writeFile callback to control file writing from the isolate.");
|
|
346
|
+
}
|
|
347
|
+
await fileIO.writeFile(opts.path, buffer);
|
|
348
|
+
}
|
|
349
|
+
return buffer.toString("base64");
|
|
350
|
+
}
|
|
351
|
+
case "dragTo": {
|
|
352
|
+
const targetInfo = actionArg;
|
|
353
|
+
const targetLocator = getLocator(locator.page(), targetInfo[0], targetInfo[1], targetInfo[2]);
|
|
354
|
+
await locator.dragTo(targetLocator, { timeout });
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
case "scrollIntoViewIfNeeded":
|
|
358
|
+
await locator.scrollIntoViewIfNeeded({ timeout });
|
|
359
|
+
return null;
|
|
360
|
+
case "highlight":
|
|
361
|
+
await locator.highlight();
|
|
362
|
+
return null;
|
|
363
|
+
case "evaluate": {
|
|
364
|
+
const [fnString, arg] = actionArg;
|
|
365
|
+
const fn = new Function("return (" + fnString + ")")();
|
|
366
|
+
return await locator.evaluate(fn, arg);
|
|
367
|
+
}
|
|
368
|
+
case "evaluateAll": {
|
|
369
|
+
const [fnString, arg] = actionArg;
|
|
370
|
+
const fn = new Function("return (" + fnString + ")")();
|
|
371
|
+
return await locator.evaluateAll(fn, arg);
|
|
372
|
+
}
|
|
167
373
|
default:
|
|
168
374
|
throw new Error(`Unknown action: ${action}`);
|
|
169
375
|
}
|
|
@@ -340,76 +546,364 @@ async function executeExpectAssertion(locator, matcher, expected, negated, timeo
|
|
|
340
546
|
}
|
|
341
547
|
break;
|
|
342
548
|
}
|
|
549
|
+
case "toBeAttached": {
|
|
550
|
+
const count = await locator.count();
|
|
551
|
+
const isAttached = count > 0;
|
|
552
|
+
if (negated) {
|
|
553
|
+
if (isAttached)
|
|
554
|
+
throw new Error("Expected element to not be attached to DOM, but it was");
|
|
555
|
+
} else {
|
|
556
|
+
if (!isAttached)
|
|
557
|
+
throw new Error("Expected element to be attached to DOM, but it was not");
|
|
558
|
+
}
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
case "toBeEditable": {
|
|
562
|
+
const isEditable = await locator.isEditable({ timeout });
|
|
563
|
+
if (negated) {
|
|
564
|
+
if (isEditable)
|
|
565
|
+
throw new Error("Expected element to not be editable, but it was");
|
|
566
|
+
} else {
|
|
567
|
+
if (!isEditable)
|
|
568
|
+
throw new Error("Expected element to be editable, but it was not");
|
|
569
|
+
}
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
case "toHaveClass": {
|
|
573
|
+
const classAttr = await locator.getAttribute("class", { timeout }) ?? "";
|
|
574
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
575
|
+
let matches;
|
|
576
|
+
let expectedDisplay;
|
|
577
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
578
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
579
|
+
matches = regex.test(classAttr);
|
|
580
|
+
expectedDisplay = String(regex);
|
|
581
|
+
} else if (Array.isArray(expected)) {
|
|
582
|
+
matches = expected.every((c) => classes.includes(c));
|
|
583
|
+
expectedDisplay = JSON.stringify(expected);
|
|
584
|
+
} else {
|
|
585
|
+
matches = classAttr === String(expected) || classes.includes(String(expected));
|
|
586
|
+
expectedDisplay = String(expected);
|
|
587
|
+
}
|
|
588
|
+
if (negated) {
|
|
589
|
+
if (matches)
|
|
590
|
+
throw new Error(`Expected class to not match ${expectedDisplay}, but got "${classAttr}"`);
|
|
591
|
+
} else {
|
|
592
|
+
if (!matches)
|
|
593
|
+
throw new Error(`Expected class to match ${expectedDisplay}, but got "${classAttr}"`);
|
|
594
|
+
}
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case "toContainClass": {
|
|
598
|
+
const classAttr = await locator.getAttribute("class", { timeout }) ?? "";
|
|
599
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
600
|
+
const expectedClass = String(expected);
|
|
601
|
+
const hasClass = classes.includes(expectedClass);
|
|
602
|
+
if (negated) {
|
|
603
|
+
if (hasClass)
|
|
604
|
+
throw new Error(`Expected element to not contain class "${expectedClass}", but it does`);
|
|
605
|
+
} else {
|
|
606
|
+
if (!hasClass)
|
|
607
|
+
throw new Error(`Expected element to contain class "${expectedClass}", but classes are "${classAttr}"`);
|
|
608
|
+
}
|
|
609
|
+
break;
|
|
610
|
+
}
|
|
611
|
+
case "toHaveId": {
|
|
612
|
+
const id = await locator.getAttribute("id", { timeout });
|
|
613
|
+
const matches = id === String(expected);
|
|
614
|
+
if (negated) {
|
|
615
|
+
if (matches)
|
|
616
|
+
throw new Error(`Expected id to not be "${expected}", but it was`);
|
|
617
|
+
} else {
|
|
618
|
+
if (!matches)
|
|
619
|
+
throw new Error(`Expected id to be "${expected}", but got "${id}"`);
|
|
620
|
+
}
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case "toBeInViewport": {
|
|
624
|
+
const isInViewport = await locator.evaluate((el) => {
|
|
625
|
+
const rect = el.getBoundingClientRect();
|
|
626
|
+
return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
|
|
627
|
+
});
|
|
628
|
+
if (negated) {
|
|
629
|
+
if (isInViewport)
|
|
630
|
+
throw new Error("Expected element to not be in viewport, but it was");
|
|
631
|
+
} else {
|
|
632
|
+
if (!isInViewport)
|
|
633
|
+
throw new Error("Expected element to be in viewport, but it was not");
|
|
634
|
+
}
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
case "toHaveCSS": {
|
|
638
|
+
const { name, value } = expected;
|
|
639
|
+
const actual = await locator.evaluate((el, propName) => {
|
|
640
|
+
return getComputedStyle(el).getPropertyValue(propName);
|
|
641
|
+
}, name);
|
|
642
|
+
let matches;
|
|
643
|
+
if (value && typeof value === "object" && value.$regex) {
|
|
644
|
+
const regex = new RegExp(value.$regex, value.$flags);
|
|
645
|
+
matches = regex.test(actual);
|
|
646
|
+
} else {
|
|
647
|
+
matches = actual === String(value);
|
|
648
|
+
}
|
|
649
|
+
if (negated) {
|
|
650
|
+
if (matches)
|
|
651
|
+
throw new Error(`Expected CSS "${name}" to not be "${value}", but it was`);
|
|
652
|
+
} else {
|
|
653
|
+
if (!matches)
|
|
654
|
+
throw new Error(`Expected CSS "${name}" to be "${value}", but got "${actual}"`);
|
|
655
|
+
}
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
case "toHaveJSProperty": {
|
|
659
|
+
const { name, value } = expected;
|
|
660
|
+
const actual = await locator.evaluate((el, propName) => {
|
|
661
|
+
return el[propName];
|
|
662
|
+
}, name);
|
|
663
|
+
const matches = JSON.stringify(actual) === JSON.stringify(value);
|
|
664
|
+
if (negated) {
|
|
665
|
+
if (matches)
|
|
666
|
+
throw new Error(`Expected JS property "${name}" to not be ${JSON.stringify(value)}, but it was`);
|
|
667
|
+
} else {
|
|
668
|
+
if (!matches)
|
|
669
|
+
throw new Error(`Expected JS property "${name}" to be ${JSON.stringify(value)}, but got ${JSON.stringify(actual)}`);
|
|
670
|
+
}
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
case "toHaveAccessibleName": {
|
|
674
|
+
const accessibleName = await locator.evaluate((el) => {
|
|
675
|
+
return el.getAttribute("aria-label") || el.getAttribute("aria-labelledby") || el.innerText || "";
|
|
676
|
+
});
|
|
677
|
+
let matches;
|
|
678
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
679
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
680
|
+
matches = regex.test(accessibleName);
|
|
681
|
+
} else {
|
|
682
|
+
matches = accessibleName === String(expected);
|
|
683
|
+
}
|
|
684
|
+
if (negated) {
|
|
685
|
+
if (matches)
|
|
686
|
+
throw new Error(`Expected accessible name to not be "${expected}", but it was`);
|
|
687
|
+
} else {
|
|
688
|
+
if (!matches)
|
|
689
|
+
throw new Error(`Expected accessible name to be "${expected}", but got "${accessibleName}"`);
|
|
690
|
+
}
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
case "toHaveAccessibleDescription": {
|
|
694
|
+
const accessibleDesc = await locator.evaluate((el) => {
|
|
695
|
+
const describedby = el.getAttribute("aria-describedby");
|
|
696
|
+
if (describedby) {
|
|
697
|
+
const descEl = document.getElementById(describedby);
|
|
698
|
+
return descEl?.textContent || "";
|
|
699
|
+
}
|
|
700
|
+
return el.getAttribute("aria-description") || "";
|
|
701
|
+
});
|
|
702
|
+
let matches;
|
|
703
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
704
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
705
|
+
matches = regex.test(accessibleDesc);
|
|
706
|
+
} else {
|
|
707
|
+
matches = accessibleDesc === String(expected);
|
|
708
|
+
}
|
|
709
|
+
if (negated) {
|
|
710
|
+
if (matches)
|
|
711
|
+
throw new Error(`Expected accessible description to not be "${expected}", but it was`);
|
|
712
|
+
} else {
|
|
713
|
+
if (!matches)
|
|
714
|
+
throw new Error(`Expected accessible description to be "${expected}", but got "${accessibleDesc}"`);
|
|
715
|
+
}
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
case "toHaveRole": {
|
|
719
|
+
const role = await locator.evaluate((el) => {
|
|
720
|
+
return el.getAttribute("role") || el.tagName.toLowerCase();
|
|
721
|
+
});
|
|
722
|
+
const matches = role === String(expected);
|
|
723
|
+
if (negated) {
|
|
724
|
+
if (matches)
|
|
725
|
+
throw new Error(`Expected role to not be "${expected}", but it was`);
|
|
726
|
+
} else {
|
|
727
|
+
if (!matches)
|
|
728
|
+
throw new Error(`Expected role to be "${expected}", but got "${role}"`);
|
|
729
|
+
}
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
343
732
|
default:
|
|
344
733
|
throw new Error(`Unknown matcher: ${matcher}`);
|
|
345
734
|
}
|
|
346
735
|
}
|
|
736
|
+
async function executePageExpectAssertion(page, matcher, expected, negated, timeout) {
|
|
737
|
+
let expectedValue = expected;
|
|
738
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
739
|
+
expectedValue = new RegExp(expected.$regex, expected.$flags);
|
|
740
|
+
}
|
|
741
|
+
switch (matcher) {
|
|
742
|
+
case "toHaveURL": {
|
|
743
|
+
const expectedUrl = expectedValue;
|
|
744
|
+
const startTime = Date.now();
|
|
745
|
+
let lastUrl = "";
|
|
746
|
+
while (Date.now() - startTime < timeout) {
|
|
747
|
+
lastUrl = page.url();
|
|
748
|
+
const matches = expectedUrl instanceof RegExp ? expectedUrl.test(lastUrl) : lastUrl === expectedUrl;
|
|
749
|
+
if (negated ? !matches : matches)
|
|
750
|
+
return;
|
|
751
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
752
|
+
}
|
|
753
|
+
if (negated) {
|
|
754
|
+
throw new Error(`Expected URL to not match "${expectedUrl}", but got "${lastUrl}"`);
|
|
755
|
+
} else {
|
|
756
|
+
throw new Error(`Expected URL to be "${expectedUrl}", but got "${lastUrl}"`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
case "toHaveTitle": {
|
|
760
|
+
const expectedTitle = expectedValue;
|
|
761
|
+
const startTime = Date.now();
|
|
762
|
+
let lastTitle = "";
|
|
763
|
+
while (Date.now() - startTime < timeout) {
|
|
764
|
+
lastTitle = await page.title();
|
|
765
|
+
const matches = expectedTitle instanceof RegExp ? expectedTitle.test(lastTitle) : lastTitle === expectedTitle;
|
|
766
|
+
if (negated ? !matches : matches)
|
|
767
|
+
return;
|
|
768
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
769
|
+
}
|
|
770
|
+
if (negated) {
|
|
771
|
+
throw new Error(`Expected title to not match "${expectedTitle}", but got "${lastTitle}"`);
|
|
772
|
+
} else {
|
|
773
|
+
throw new Error(`Expected title to be "${expectedTitle}", but got "${lastTitle}"`);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
default:
|
|
777
|
+
throw new Error(`Unknown page matcher: ${matcher}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
347
780
|
function createPlaywrightHandler(page, options) {
|
|
348
781
|
const timeout = options?.timeout ?? 30000;
|
|
349
|
-
const
|
|
782
|
+
const fileIO = {
|
|
783
|
+
readFile: options?.readFile,
|
|
784
|
+
writeFile: options?.writeFile
|
|
785
|
+
};
|
|
786
|
+
const registry = {
|
|
787
|
+
pages: new Map([["page_0", page]]),
|
|
788
|
+
contexts: new Map([["ctx_0", page.context()]]),
|
|
789
|
+
nextPageId: 1,
|
|
790
|
+
nextContextId: 1
|
|
791
|
+
};
|
|
350
792
|
return async (op) => {
|
|
351
793
|
try {
|
|
794
|
+
switch (op.type) {
|
|
795
|
+
case "newContext": {
|
|
796
|
+
if (!options?.createContext) {
|
|
797
|
+
return { ok: false, error: { name: "Error", message: "createContext callback not provided. Configure createContext in playwright options to enable browser.newContext()." } };
|
|
798
|
+
}
|
|
799
|
+
const [contextOptions] = op.args;
|
|
800
|
+
const newContext = await options.createContext(contextOptions);
|
|
801
|
+
const contextId2 = `ctx_${registry.nextContextId++}`;
|
|
802
|
+
registry.contexts.set(contextId2, newContext);
|
|
803
|
+
return { ok: true, value: { contextId: contextId2 } };
|
|
804
|
+
}
|
|
805
|
+
case "newPage": {
|
|
806
|
+
if (!options?.createPage) {
|
|
807
|
+
return { ok: false, error: { name: "Error", message: "createPage callback not provided. Configure createPage in playwright options to enable context.newPage()." } };
|
|
808
|
+
}
|
|
809
|
+
const contextId2 = op.contextId ?? "ctx_0";
|
|
810
|
+
const targetContext2 = registry.contexts.get(contextId2);
|
|
811
|
+
if (!targetContext2) {
|
|
812
|
+
return { ok: false, error: { name: "Error", message: `Context ${contextId2} not found` } };
|
|
813
|
+
}
|
|
814
|
+
const newPage = await options.createPage(targetContext2);
|
|
815
|
+
const pageId2 = `page_${registry.nextPageId++}`;
|
|
816
|
+
registry.pages.set(pageId2, newPage);
|
|
817
|
+
return { ok: true, value: { pageId: pageId2 } };
|
|
818
|
+
}
|
|
819
|
+
case "closeContext": {
|
|
820
|
+
const contextId2 = op.contextId ?? "ctx_0";
|
|
821
|
+
const context = registry.contexts.get(contextId2);
|
|
822
|
+
if (!context) {
|
|
823
|
+
return { ok: false, error: { name: "Error", message: `Context ${contextId2} not found` } };
|
|
824
|
+
}
|
|
825
|
+
await context.close();
|
|
826
|
+
registry.contexts.delete(contextId2);
|
|
827
|
+
for (const [pid, p] of registry.pages) {
|
|
828
|
+
if (p.context() === context) {
|
|
829
|
+
registry.pages.delete(pid);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return { ok: true };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
const pageId = op.pageId ?? "page_0";
|
|
836
|
+
const targetPage = registry.pages.get(pageId);
|
|
837
|
+
if (!targetPage) {
|
|
838
|
+
return { ok: false, error: { name: "Error", message: `Page ${pageId} not found` } };
|
|
839
|
+
}
|
|
840
|
+
const contextId = op.contextId ?? "ctx_0";
|
|
841
|
+
const targetContext = registry.contexts.get(contextId);
|
|
352
842
|
switch (op.type) {
|
|
353
843
|
case "goto": {
|
|
354
844
|
const [url, waitUntil] = op.args;
|
|
355
|
-
|
|
356
|
-
await page.goto(targetUrl, {
|
|
845
|
+
await targetPage.goto(url, {
|
|
357
846
|
timeout,
|
|
358
847
|
waitUntil: waitUntil ?? "load"
|
|
359
848
|
});
|
|
360
849
|
return { ok: true };
|
|
361
850
|
}
|
|
362
851
|
case "reload":
|
|
363
|
-
await
|
|
852
|
+
await targetPage.reload({ timeout });
|
|
364
853
|
return { ok: true };
|
|
365
854
|
case "url":
|
|
366
|
-
return { ok: true, value:
|
|
855
|
+
return { ok: true, value: targetPage.url() };
|
|
367
856
|
case "title":
|
|
368
|
-
return { ok: true, value: await
|
|
857
|
+
return { ok: true, value: await targetPage.title() };
|
|
369
858
|
case "content":
|
|
370
|
-
return { ok: true, value: await
|
|
859
|
+
return { ok: true, value: await targetPage.content() };
|
|
371
860
|
case "waitForSelector": {
|
|
372
861
|
const [selector, optionsJson] = op.args;
|
|
373
862
|
const opts = optionsJson ? JSON.parse(optionsJson) : {};
|
|
374
|
-
await
|
|
863
|
+
await targetPage.waitForSelector(selector, { timeout, ...opts });
|
|
375
864
|
return { ok: true };
|
|
376
865
|
}
|
|
377
866
|
case "waitForTimeout": {
|
|
378
867
|
const [ms] = op.args;
|
|
379
|
-
await
|
|
868
|
+
await targetPage.waitForTimeout(ms);
|
|
380
869
|
return { ok: true };
|
|
381
870
|
}
|
|
382
871
|
case "waitForLoadState": {
|
|
383
872
|
const [state] = op.args;
|
|
384
|
-
await
|
|
873
|
+
await targetPage.waitForLoadState(state ?? "load", { timeout });
|
|
385
874
|
return { ok: true };
|
|
386
875
|
}
|
|
387
876
|
case "evaluate": {
|
|
388
877
|
const [script, arg] = op.args;
|
|
389
878
|
if (op.args.length > 1) {
|
|
390
879
|
const fn = new Function("return (" + script + ")")();
|
|
391
|
-
const result2 = await
|
|
880
|
+
const result2 = await targetPage.evaluate(fn, arg);
|
|
392
881
|
return { ok: true, value: result2 };
|
|
393
882
|
}
|
|
394
|
-
const result = await
|
|
883
|
+
const result = await targetPage.evaluate(script);
|
|
395
884
|
return { ok: true, value: result };
|
|
396
885
|
}
|
|
397
886
|
case "locatorAction": {
|
|
398
887
|
const [selectorType, selectorValue, roleOptions, action, actionArg] = op.args;
|
|
399
|
-
const locator = getLocator(
|
|
400
|
-
const result = await executeLocatorAction(locator, action, actionArg, timeout);
|
|
888
|
+
const locator = getLocator(targetPage, selectorType, selectorValue, roleOptions);
|
|
889
|
+
const result = await executeLocatorAction(locator, action, actionArg, timeout, fileIO);
|
|
401
890
|
return { ok: true, value: result };
|
|
402
891
|
}
|
|
403
892
|
case "expectLocator": {
|
|
404
893
|
const [selectorType, selectorValue, roleOptions, matcher, expected, negated, customTimeout] = op.args;
|
|
405
|
-
const locator = getLocator(
|
|
894
|
+
const locator = getLocator(targetPage, selectorType, selectorValue, roleOptions);
|
|
406
895
|
const effectiveTimeout = customTimeout ?? timeout;
|
|
407
896
|
await executeExpectAssertion(locator, matcher, expected, negated ?? false, effectiveTimeout);
|
|
408
897
|
return { ok: true };
|
|
409
898
|
}
|
|
899
|
+
case "expectPage": {
|
|
900
|
+
const [matcher, expected, negated, customTimeout] = op.args;
|
|
901
|
+
const effectiveTimeout = customTimeout ?? timeout;
|
|
902
|
+
await executePageExpectAssertion(targetPage, matcher, expected, negated ?? false, effectiveTimeout);
|
|
903
|
+
return { ok: true };
|
|
904
|
+
}
|
|
410
905
|
case "request": {
|
|
411
906
|
const [url, method, data, headers] = op.args;
|
|
412
|
-
const targetUrl = baseUrl && !url.startsWith("http") ? `${baseUrl}${url}` : url;
|
|
413
907
|
const requestOptions = {
|
|
414
908
|
timeout
|
|
415
909
|
};
|
|
@@ -419,7 +913,7 @@ function createPlaywrightHandler(page, options) {
|
|
|
419
913
|
if (data !== undefined && data !== null) {
|
|
420
914
|
requestOptions.data = data;
|
|
421
915
|
}
|
|
422
|
-
const response = await
|
|
916
|
+
const response = await targetPage.request.fetch(url, {
|
|
423
917
|
method,
|
|
424
918
|
...requestOptions
|
|
425
919
|
});
|
|
@@ -442,7 +936,7 @@ function createPlaywrightHandler(page, options) {
|
|
|
442
936
|
}
|
|
443
937
|
case "goBack": {
|
|
444
938
|
const [waitUntil] = op.args;
|
|
445
|
-
await
|
|
939
|
+
await targetPage.goBack({
|
|
446
940
|
timeout,
|
|
447
941
|
waitUntil: waitUntil ?? "load"
|
|
448
942
|
});
|
|
@@ -450,22 +944,156 @@ function createPlaywrightHandler(page, options) {
|
|
|
450
944
|
}
|
|
451
945
|
case "goForward": {
|
|
452
946
|
const [waitUntil] = op.args;
|
|
453
|
-
await
|
|
947
|
+
await targetPage.goForward({
|
|
454
948
|
timeout,
|
|
455
949
|
waitUntil: waitUntil ?? "load"
|
|
456
950
|
});
|
|
457
951
|
return { ok: true };
|
|
458
952
|
}
|
|
459
953
|
case "waitForURL": {
|
|
460
|
-
const [
|
|
461
|
-
|
|
954
|
+
const [urlArg, customTimeout, waitUntil] = op.args;
|
|
955
|
+
const url = urlArg && typeof urlArg === "object" && "$regex" in urlArg ? new RegExp(urlArg.$regex, urlArg.$flags) : urlArg;
|
|
956
|
+
await targetPage.waitForURL(url, {
|
|
462
957
|
timeout: customTimeout ?? timeout,
|
|
463
958
|
waitUntil: waitUntil ?? undefined
|
|
464
959
|
});
|
|
465
960
|
return { ok: true };
|
|
466
961
|
}
|
|
467
962
|
case "clearCookies": {
|
|
468
|
-
|
|
963
|
+
const ctx = targetContext ?? targetPage.context();
|
|
964
|
+
await ctx.clearCookies();
|
|
965
|
+
return { ok: true };
|
|
966
|
+
}
|
|
967
|
+
case "screenshot": {
|
|
968
|
+
const [screenshotOptions] = op.args;
|
|
969
|
+
const buffer = await targetPage.screenshot({
|
|
970
|
+
type: screenshotOptions?.type,
|
|
971
|
+
quality: screenshotOptions?.quality,
|
|
972
|
+
fullPage: screenshotOptions?.fullPage,
|
|
973
|
+
clip: screenshotOptions?.clip
|
|
974
|
+
});
|
|
975
|
+
if (screenshotOptions?.path) {
|
|
976
|
+
if (!fileIO.writeFile) {
|
|
977
|
+
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.");
|
|
978
|
+
}
|
|
979
|
+
await fileIO.writeFile(screenshotOptions.path, buffer);
|
|
980
|
+
}
|
|
981
|
+
return { ok: true, value: buffer.toString("base64") };
|
|
982
|
+
}
|
|
983
|
+
case "setViewportSize": {
|
|
984
|
+
const [size] = op.args;
|
|
985
|
+
await targetPage.setViewportSize(size);
|
|
986
|
+
return { ok: true };
|
|
987
|
+
}
|
|
988
|
+
case "viewportSize": {
|
|
989
|
+
return { ok: true, value: targetPage.viewportSize() };
|
|
990
|
+
}
|
|
991
|
+
case "keyboardType": {
|
|
992
|
+
const [text, typeOptions] = op.args;
|
|
993
|
+
await targetPage.keyboard.type(text, typeOptions);
|
|
994
|
+
return { ok: true };
|
|
995
|
+
}
|
|
996
|
+
case "keyboardPress": {
|
|
997
|
+
const [key, pressOptions] = op.args;
|
|
998
|
+
await targetPage.keyboard.press(key, pressOptions);
|
|
999
|
+
return { ok: true };
|
|
1000
|
+
}
|
|
1001
|
+
case "keyboardDown": {
|
|
1002
|
+
const [key] = op.args;
|
|
1003
|
+
await targetPage.keyboard.down(key);
|
|
1004
|
+
return { ok: true };
|
|
1005
|
+
}
|
|
1006
|
+
case "keyboardUp": {
|
|
1007
|
+
const [key] = op.args;
|
|
1008
|
+
await targetPage.keyboard.up(key);
|
|
1009
|
+
return { ok: true };
|
|
1010
|
+
}
|
|
1011
|
+
case "keyboardInsertText": {
|
|
1012
|
+
const [text] = op.args;
|
|
1013
|
+
await targetPage.keyboard.insertText(text);
|
|
1014
|
+
return { ok: true };
|
|
1015
|
+
}
|
|
1016
|
+
case "mouseMove": {
|
|
1017
|
+
const [x, y, moveOptions] = op.args;
|
|
1018
|
+
await targetPage.mouse.move(x, y, moveOptions);
|
|
1019
|
+
return { ok: true };
|
|
1020
|
+
}
|
|
1021
|
+
case "mouseClick": {
|
|
1022
|
+
const [x, y, clickOptions] = op.args;
|
|
1023
|
+
await targetPage.mouse.click(x, y, clickOptions);
|
|
1024
|
+
return { ok: true };
|
|
1025
|
+
}
|
|
1026
|
+
case "mouseDown": {
|
|
1027
|
+
const [downOptions] = op.args;
|
|
1028
|
+
await targetPage.mouse.down(downOptions);
|
|
1029
|
+
return { ok: true };
|
|
1030
|
+
}
|
|
1031
|
+
case "mouseUp": {
|
|
1032
|
+
const [upOptions] = op.args;
|
|
1033
|
+
await targetPage.mouse.up(upOptions);
|
|
1034
|
+
return { ok: true };
|
|
1035
|
+
}
|
|
1036
|
+
case "mouseWheel": {
|
|
1037
|
+
const [deltaX, deltaY] = op.args;
|
|
1038
|
+
await targetPage.mouse.wheel(deltaX, deltaY);
|
|
1039
|
+
return { ok: true };
|
|
1040
|
+
}
|
|
1041
|
+
case "frames": {
|
|
1042
|
+
const frames = targetPage.frames();
|
|
1043
|
+
return { ok: true, value: frames.map((f) => ({ name: f.name(), url: f.url() })) };
|
|
1044
|
+
}
|
|
1045
|
+
case "mainFrame": {
|
|
1046
|
+
const mainFrame = targetPage.mainFrame();
|
|
1047
|
+
return { ok: true, value: { name: mainFrame.name(), url: mainFrame.url() } };
|
|
1048
|
+
}
|
|
1049
|
+
case "bringToFront": {
|
|
1050
|
+
await targetPage.bringToFront();
|
|
1051
|
+
return { ok: true };
|
|
1052
|
+
}
|
|
1053
|
+
case "close": {
|
|
1054
|
+
await targetPage.close();
|
|
1055
|
+
registry.pages.delete(pageId);
|
|
1056
|
+
return { ok: true };
|
|
1057
|
+
}
|
|
1058
|
+
case "isClosed": {
|
|
1059
|
+
return { ok: true, value: targetPage.isClosed() };
|
|
1060
|
+
}
|
|
1061
|
+
case "pdf": {
|
|
1062
|
+
const [pdfOptions] = op.args;
|
|
1063
|
+
const { path: pdfPath, ...restPdfOptions } = pdfOptions ?? {};
|
|
1064
|
+
const buffer = await targetPage.pdf(restPdfOptions);
|
|
1065
|
+
if (pdfPath) {
|
|
1066
|
+
if (!fileIO.writeFile) {
|
|
1067
|
+
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.");
|
|
1068
|
+
}
|
|
1069
|
+
await fileIO.writeFile(pdfPath, buffer);
|
|
1070
|
+
}
|
|
1071
|
+
return { ok: true, value: buffer.toString("base64") };
|
|
1072
|
+
}
|
|
1073
|
+
case "emulateMedia": {
|
|
1074
|
+
const [mediaOptions] = op.args;
|
|
1075
|
+
await targetPage.emulateMedia(mediaOptions);
|
|
1076
|
+
return { ok: true };
|
|
1077
|
+
}
|
|
1078
|
+
case "addCookies": {
|
|
1079
|
+
const [cookies] = op.args;
|
|
1080
|
+
const ctx = targetContext ?? targetPage.context();
|
|
1081
|
+
await ctx.addCookies(cookies);
|
|
1082
|
+
return { ok: true };
|
|
1083
|
+
}
|
|
1084
|
+
case "cookies": {
|
|
1085
|
+
const [urls] = op.args;
|
|
1086
|
+
const ctx = targetContext ?? targetPage.context();
|
|
1087
|
+
const cookies = await ctx.cookies(urls);
|
|
1088
|
+
return { ok: true, value: cookies };
|
|
1089
|
+
}
|
|
1090
|
+
case "setExtraHTTPHeaders": {
|
|
1091
|
+
const [headers] = op.args;
|
|
1092
|
+
await targetPage.setExtraHTTPHeaders(headers);
|
|
1093
|
+
return { ok: true };
|
|
1094
|
+
}
|
|
1095
|
+
case "pause": {
|
|
1096
|
+
await targetPage.pause();
|
|
469
1097
|
return { ok: true };
|
|
470
1098
|
}
|
|
471
1099
|
default:
|
|
@@ -477,12 +1105,27 @@ function createPlaywrightHandler(page, options) {
|
|
|
477
1105
|
}
|
|
478
1106
|
};
|
|
479
1107
|
}
|
|
1108
|
+
function defaultPlaywrightHandler(page, options) {
|
|
1109
|
+
const handler = createPlaywrightHandler(page, options);
|
|
1110
|
+
handler[import_types.DEFAULT_PLAYWRIGHT_HANDLER_META] = { page, options };
|
|
1111
|
+
return handler;
|
|
1112
|
+
}
|
|
1113
|
+
function getDefaultPlaywrightHandlerMetadata(handler) {
|
|
1114
|
+
return handler[import_types.DEFAULT_PLAYWRIGHT_HANDLER_META];
|
|
1115
|
+
}
|
|
480
1116
|
async function setupPlaywright(context, options) {
|
|
481
1117
|
const timeout = options.timeout ?? 30000;
|
|
482
|
-
const
|
|
483
|
-
const page = "page" in options ? options.page : undefined;
|
|
1118
|
+
const explicitPage = "page" in options ? options.page : undefined;
|
|
484
1119
|
const handler = "handler" in options ? options.handler : undefined;
|
|
485
|
-
const
|
|
1120
|
+
const handlerMetadata = handler ? getDefaultPlaywrightHandlerMetadata(handler) : undefined;
|
|
1121
|
+
const page = explicitPage ?? handlerMetadata?.page;
|
|
1122
|
+
const createPage = "createPage" in options ? options.createPage : undefined;
|
|
1123
|
+
const createContext = "createContext" in options ? options.createContext : undefined;
|
|
1124
|
+
const effectiveHandler = handler ?? (page ? createPlaywrightHandler(page, {
|
|
1125
|
+
timeout,
|
|
1126
|
+
createPage,
|
|
1127
|
+
createContext
|
|
1128
|
+
}) : undefined);
|
|
486
1129
|
if (!effectiveHandler) {
|
|
487
1130
|
throw new Error("Either page or handler must be provided to setupPlaywright");
|
|
488
1131
|
}
|
|
@@ -569,8 +1212,8 @@ async function setupPlaywright(context, options) {
|
|
|
569
1212
|
}));
|
|
570
1213
|
context.evalSync(`
|
|
571
1214
|
(function() {
|
|
572
|
-
globalThis.__pw_invoke = async function(type, args) {
|
|
573
|
-
const op = JSON.stringify({ type, args });
|
|
1215
|
+
globalThis.__pw_invoke = async function(type, args, options) {
|
|
1216
|
+
const op = JSON.stringify({ type, args, pageId: options?.pageId, contextId: options?.contextId });
|
|
574
1217
|
const resultJson = __Playwright_handler_ref.applySyncPromise(undefined, [op]);
|
|
575
1218
|
const result = JSON.parse(resultJson);
|
|
576
1219
|
if (result.ok) {
|
|
@@ -585,209 +1228,362 @@ async function setupPlaywright(context, options) {
|
|
|
585
1228
|
`);
|
|
586
1229
|
context.evalSync(`
|
|
587
1230
|
(function() {
|
|
588
|
-
|
|
589
|
-
|
|
1231
|
+
// IsolatePage class - represents a page with a specific pageId
|
|
1232
|
+
class IsolatePage {
|
|
1233
|
+
#pageId; #contextId; #currentUrl = '';
|
|
1234
|
+
constructor(pageId, contextId) {
|
|
1235
|
+
this.#pageId = pageId;
|
|
1236
|
+
this.#contextId = contextId;
|
|
1237
|
+
}
|
|
1238
|
+
get __isPage() { return true; }
|
|
1239
|
+
get __pageId() { return this.#pageId; }
|
|
1240
|
+
get __contextId() { return this.#contextId; }
|
|
1241
|
+
|
|
590
1242
|
async goto(url, options) {
|
|
591
|
-
|
|
592
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
},
|
|
1243
|
+
await __pw_invoke("goto", [url, options?.waitUntil || null], { pageId: this.#pageId });
|
|
1244
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1245
|
+
this.#currentUrl = resolvedUrl || url;
|
|
1246
|
+
}
|
|
596
1247
|
async reload() {
|
|
597
|
-
|
|
598
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
599
|
-
if (resolvedUrl)
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
},
|
|
605
|
-
async title() {
|
|
606
|
-
return __pw_invoke("title", []);
|
|
607
|
-
},
|
|
608
|
-
async content() {
|
|
609
|
-
return __pw_invoke("content", []);
|
|
610
|
-
},
|
|
1248
|
+
await __pw_invoke("reload", [], { pageId: this.#pageId });
|
|
1249
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1250
|
+
if (resolvedUrl) this.#currentUrl = resolvedUrl;
|
|
1251
|
+
}
|
|
1252
|
+
url() { return this.#currentUrl; }
|
|
1253
|
+
async title() { return __pw_invoke("title", [], { pageId: this.#pageId }); }
|
|
1254
|
+
async content() { return __pw_invoke("content", [], { pageId: this.#pageId }); }
|
|
611
1255
|
async waitForSelector(selector, options) {
|
|
612
|
-
return __pw_invoke("waitForSelector", [selector, options ? JSON.stringify(options) : null]);
|
|
613
|
-
}
|
|
614
|
-
async waitForTimeout(ms) {
|
|
615
|
-
|
|
616
|
-
},
|
|
617
|
-
async waitForLoadState(state) {
|
|
618
|
-
return __pw_invoke("waitForLoadState", [state || null]);
|
|
619
|
-
},
|
|
1256
|
+
return __pw_invoke("waitForSelector", [selector, options ? JSON.stringify(options) : null], { pageId: this.#pageId });
|
|
1257
|
+
}
|
|
1258
|
+
async waitForTimeout(ms) { return __pw_invoke("waitForTimeout", [ms], { pageId: this.#pageId }); }
|
|
1259
|
+
async waitForLoadState(state) { return __pw_invoke("waitForLoadState", [state || null], { pageId: this.#pageId }); }
|
|
620
1260
|
async evaluate(script, arg) {
|
|
621
1261
|
const hasArg = arguments.length > 1;
|
|
622
1262
|
if (hasArg) {
|
|
623
1263
|
const serialized = typeof script === "function" ? script.toString() : script;
|
|
624
|
-
return __pw_invoke("evaluate", [serialized, arg]);
|
|
1264
|
+
return __pw_invoke("evaluate", [serialized, arg], { pageId: this.#pageId });
|
|
625
1265
|
}
|
|
626
1266
|
const serialized = typeof script === "function" ? "(" + script.toString() + ")()" : script;
|
|
627
|
-
return __pw_invoke("evaluate", [serialized]);
|
|
628
|
-
}
|
|
629
|
-
locator(selector) { return new Locator("css", selector, null); }
|
|
1267
|
+
return __pw_invoke("evaluate", [serialized], { pageId: this.#pageId });
|
|
1268
|
+
}
|
|
1269
|
+
locator(selector) { return new Locator("css", selector, null, this.#pageId); }
|
|
630
1270
|
getByRole(role, options) {
|
|
631
1271
|
if (options) {
|
|
632
1272
|
const serialized = { ...options };
|
|
633
|
-
|
|
634
|
-
|
|
1273
|
+
const name = options.name;
|
|
1274
|
+
if (name && typeof name === 'object' && typeof name.source === 'string' && typeof name.flags === 'string') {
|
|
1275
|
+
serialized.name = { $regex: name.source, $flags: name.flags };
|
|
635
1276
|
}
|
|
636
|
-
return new Locator("role", role, JSON.stringify(serialized));
|
|
1277
|
+
return new Locator("role", role, JSON.stringify(serialized), this.#pageId);
|
|
637
1278
|
}
|
|
638
|
-
return new Locator("role", role, null);
|
|
639
|
-
}
|
|
640
|
-
getByText(text) { return new Locator("text", text, null); }
|
|
641
|
-
getByLabel(label) { return new Locator("label", label, null); }
|
|
642
|
-
getByPlaceholder(p) { return new Locator("placeholder", p, null); }
|
|
643
|
-
getByTestId(id) { return new Locator("testId", id, null); }
|
|
1279
|
+
return new Locator("role", role, null, this.#pageId);
|
|
1280
|
+
}
|
|
1281
|
+
getByText(text) { return new Locator("text", text, null, this.#pageId); }
|
|
1282
|
+
getByLabel(label) { return new Locator("label", label, null, this.#pageId); }
|
|
1283
|
+
getByPlaceholder(p) { return new Locator("placeholder", p, null, this.#pageId); }
|
|
1284
|
+
getByTestId(id) { return new Locator("testId", id, null, this.#pageId); }
|
|
1285
|
+
getByAltText(alt) { return new Locator("altText", alt, null, this.#pageId); }
|
|
1286
|
+
getByTitle(title) { return new Locator("title", title, null, this.#pageId); }
|
|
1287
|
+
frameLocator(selector) {
|
|
1288
|
+
const pageId = this.#pageId;
|
|
1289
|
+
return {
|
|
1290
|
+
locator(innerSelector) { return new Locator("frame", JSON.stringify([["css", selector, null], ["css", innerSelector, null]]), null, pageId); },
|
|
1291
|
+
getByRole(role, options) { return new Locator("frame", JSON.stringify([["css", selector, null], ["role", role, options ? JSON.stringify(options) : null]]), null, pageId); },
|
|
1292
|
+
getByText(text) { return new Locator("frame", JSON.stringify([["css", selector, null], ["text", text, null]]), null, pageId); },
|
|
1293
|
+
getByLabel(label) { return new Locator("frame", JSON.stringify([["css", selector, null], ["label", label, null]]), null, pageId); },
|
|
1294
|
+
getByPlaceholder(placeholder) { return new Locator("frame", JSON.stringify([["css", selector, null], ["placeholder", placeholder, null]]), null, pageId); },
|
|
1295
|
+
getByTestId(testId) { return new Locator("frame", JSON.stringify([["css", selector, null], ["testId", testId, null]]), null, pageId); },
|
|
1296
|
+
getByAltText(alt) { return new Locator("frame", JSON.stringify([["css", selector, null], ["altText", alt, null]]), null, pageId); },
|
|
1297
|
+
getByTitle(title) { return new Locator("frame", JSON.stringify([["css", selector, null], ["title", title, null]]), null, pageId); },
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
644
1300
|
async goBack(options) {
|
|
645
|
-
await __pw_invoke("goBack", [options?.waitUntil || null]);
|
|
646
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
647
|
-
if (resolvedUrl)
|
|
648
|
-
}
|
|
1301
|
+
await __pw_invoke("goBack", [options?.waitUntil || null], { pageId: this.#pageId });
|
|
1302
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1303
|
+
if (resolvedUrl) this.#currentUrl = resolvedUrl;
|
|
1304
|
+
}
|
|
649
1305
|
async goForward(options) {
|
|
650
|
-
await __pw_invoke("goForward", [options?.waitUntil || null]);
|
|
651
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
652
|
-
if (resolvedUrl)
|
|
653
|
-
}
|
|
1306
|
+
await __pw_invoke("goForward", [options?.waitUntil || null], { pageId: this.#pageId });
|
|
1307
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1308
|
+
if (resolvedUrl) this.#currentUrl = resolvedUrl;
|
|
1309
|
+
}
|
|
654
1310
|
async waitForURL(url, options) {
|
|
655
|
-
|
|
656
|
-
|
|
1311
|
+
let serializedUrl = url;
|
|
1312
|
+
if (url && typeof url === 'object' && typeof url.source === 'string' && typeof url.flags === 'string') {
|
|
1313
|
+
serializedUrl = { $regex: url.source, $flags: url.flags };
|
|
1314
|
+
}
|
|
1315
|
+
return __pw_invoke("waitForURL", [serializedUrl, options?.timeout || null, options?.waitUntil || null], { pageId: this.#pageId });
|
|
1316
|
+
}
|
|
657
1317
|
context() {
|
|
1318
|
+
const contextId = this.#contextId;
|
|
1319
|
+
return new IsolateContext(contextId);
|
|
1320
|
+
}
|
|
1321
|
+
async click(selector) { return this.locator(selector).click(); }
|
|
1322
|
+
async fill(selector, value) { return this.locator(selector).fill(value); }
|
|
1323
|
+
async textContent(selector) { return this.locator(selector).textContent(); }
|
|
1324
|
+
async innerText(selector) { return this.locator(selector).innerText(); }
|
|
1325
|
+
async innerHTML(selector) { return this.locator(selector).innerHTML(); }
|
|
1326
|
+
async getAttribute(selector, name) { return this.locator(selector).getAttribute(name); }
|
|
1327
|
+
async inputValue(selector) { return this.locator(selector).inputValue(); }
|
|
1328
|
+
async isVisible(selector) { return this.locator(selector).isVisible(); }
|
|
1329
|
+
async isEnabled(selector) { return this.locator(selector).isEnabled(); }
|
|
1330
|
+
async isChecked(selector) { return this.locator(selector).isChecked(); }
|
|
1331
|
+
async isHidden(selector) { return this.locator(selector).isHidden(); }
|
|
1332
|
+
async isDisabled(selector) { return this.locator(selector).isDisabled(); }
|
|
1333
|
+
async screenshot(options) { return __pw_invoke("screenshot", [options || {}], { pageId: this.#pageId }); }
|
|
1334
|
+
async setViewportSize(size) { return __pw_invoke("setViewportSize", [size], { pageId: this.#pageId }); }
|
|
1335
|
+
async viewportSize() { return __pw_invoke("viewportSize", [], { pageId: this.#pageId }); }
|
|
1336
|
+
async emulateMedia(options) { return __pw_invoke("emulateMedia", [options], { pageId: this.#pageId }); }
|
|
1337
|
+
async setExtraHTTPHeaders(headers) { return __pw_invoke("setExtraHTTPHeaders", [headers], { pageId: this.#pageId }); }
|
|
1338
|
+
async bringToFront() { return __pw_invoke("bringToFront", [], { pageId: this.#pageId }); }
|
|
1339
|
+
async close() { return __pw_invoke("close", [], { pageId: this.#pageId }); }
|
|
1340
|
+
async isClosed() { return __pw_invoke("isClosed", [], { pageId: this.#pageId }); }
|
|
1341
|
+
async pdf(options) { return __pw_invoke("pdf", [options || {}], { pageId: this.#pageId }); }
|
|
1342
|
+
async pause() { return __pw_invoke("pause", [], { pageId: this.#pageId }); }
|
|
1343
|
+
async frames() { return __pw_invoke("frames", [], { pageId: this.#pageId }); }
|
|
1344
|
+
async mainFrame() { return __pw_invoke("mainFrame", [], { pageId: this.#pageId }); }
|
|
1345
|
+
get keyboard() {
|
|
1346
|
+
const pageId = this.#pageId;
|
|
658
1347
|
return {
|
|
659
|
-
async
|
|
660
|
-
|
|
661
|
-
}
|
|
1348
|
+
async type(text, options) { return __pw_invoke("keyboardType", [text, options], { pageId }); },
|
|
1349
|
+
async press(key, options) { return __pw_invoke("keyboardPress", [key, options], { pageId }); },
|
|
1350
|
+
async down(key) { return __pw_invoke("keyboardDown", [key], { pageId }); },
|
|
1351
|
+
async up(key) { return __pw_invoke("keyboardUp", [key], { pageId }); },
|
|
1352
|
+
async insertText(text) { return __pw_invoke("keyboardInsertText", [text], { pageId }); }
|
|
662
1353
|
};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
return {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
return this.fetch(url, { ...options, method: "
|
|
689
|
-
|
|
690
|
-
|
|
1354
|
+
}
|
|
1355
|
+
get mouse() {
|
|
1356
|
+
const pageId = this.#pageId;
|
|
1357
|
+
return {
|
|
1358
|
+
async move(x, y, options) { return __pw_invoke("mouseMove", [x, y, options], { pageId }); },
|
|
1359
|
+
async click(x, y, options) { return __pw_invoke("mouseClick", [x, y, options], { pageId }); },
|
|
1360
|
+
async down(options) { return __pw_invoke("mouseDown", [options], { pageId }); },
|
|
1361
|
+
async up(options) { return __pw_invoke("mouseUp", [options], { pageId }); },
|
|
1362
|
+
async wheel(deltaX, deltaY) { return __pw_invoke("mouseWheel", [deltaX, deltaY], { pageId }); }
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
get request() {
|
|
1366
|
+
const pageId = this.#pageId;
|
|
1367
|
+
return {
|
|
1368
|
+
async fetch(url, options) {
|
|
1369
|
+
const result = await __pw_invoke("request", [url, options?.method || "GET", options?.data, options?.headers], { pageId });
|
|
1370
|
+
return {
|
|
1371
|
+
status: () => result.status,
|
|
1372
|
+
ok: () => result.ok,
|
|
1373
|
+
headers: () => result.headers,
|
|
1374
|
+
json: async () => result.json,
|
|
1375
|
+
text: async () => result.text,
|
|
1376
|
+
body: async () => result.body,
|
|
1377
|
+
};
|
|
1378
|
+
},
|
|
1379
|
+
async get(url, options) { return this.fetch(url, { ...options, method: "GET" }); },
|
|
1380
|
+
async post(url, options) { return this.fetch(url, { ...options, method: "POST" }); },
|
|
1381
|
+
async put(url, options) { return this.fetch(url, { ...options, method: "PUT" }); },
|
|
1382
|
+
async delete(url, options) { return this.fetch(url, { ...options, method: "DELETE" }); },
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
globalThis.IsolatePage = IsolatePage;
|
|
1387
|
+
|
|
1388
|
+
// IsolateContext class - represents a browser context with a specific contextId
|
|
1389
|
+
class IsolateContext {
|
|
1390
|
+
#contextId;
|
|
1391
|
+
constructor(contextId) { this.#contextId = contextId; }
|
|
1392
|
+
get __contextId() { return this.#contextId; }
|
|
1393
|
+
|
|
1394
|
+
async newPage() {
|
|
1395
|
+
const result = await __pw_invoke("newPage", [], { contextId: this.#contextId });
|
|
1396
|
+
return new IsolatePage(result.pageId, this.#contextId);
|
|
1397
|
+
}
|
|
1398
|
+
async close() { return __pw_invoke("closeContext", [], { contextId: this.#contextId }); }
|
|
1399
|
+
async clearCookies() { return __pw_invoke("clearCookies", [], { contextId: this.#contextId }); }
|
|
1400
|
+
async addCookies(cookies) { return __pw_invoke("addCookies", [cookies], { contextId: this.#contextId }); }
|
|
1401
|
+
async cookies(urls) { return __pw_invoke("cookies", [urls], { contextId: this.#contextId }); }
|
|
1402
|
+
}
|
|
1403
|
+
globalThis.IsolateContext = IsolateContext;
|
|
1404
|
+
|
|
1405
|
+
// browser global - for creating new contexts
|
|
1406
|
+
globalThis.browser = {
|
|
1407
|
+
async newContext(options) {
|
|
1408
|
+
const result = await __pw_invoke("newContext", [options || null]);
|
|
1409
|
+
return new IsolateContext(result.contextId);
|
|
1410
|
+
}
|
|
691
1411
|
};
|
|
1412
|
+
|
|
1413
|
+
// context global - represents the default context
|
|
1414
|
+
globalThis.context = new IsolateContext("ctx_0");
|
|
1415
|
+
|
|
1416
|
+
// page global - represents the default page
|
|
1417
|
+
globalThis.page = new IsolatePage("page_0", "ctx_0");
|
|
692
1418
|
})();
|
|
693
1419
|
`);
|
|
694
1420
|
context.evalSync(`
|
|
695
1421
|
(function() {
|
|
1422
|
+
// Helper to serialize options including RegExp
|
|
1423
|
+
function serializeOptions(options) {
|
|
1424
|
+
if (!options) return null;
|
|
1425
|
+
const serialized = { ...options };
|
|
1426
|
+
if (options.name && typeof options.name === 'object' && typeof options.name.source === 'string' && typeof options.name.flags === 'string') {
|
|
1427
|
+
serialized.name = { $regex: options.name.source, $flags: options.name.flags };
|
|
1428
|
+
}
|
|
1429
|
+
return JSON.stringify(serialized);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
696
1432
|
class Locator {
|
|
697
|
-
#type; #value; #options;
|
|
698
|
-
constructor(type, value, options) {
|
|
1433
|
+
#type; #value; #options; #pageId;
|
|
1434
|
+
constructor(type, value, options, pageId) {
|
|
699
1435
|
this.#type = type;
|
|
700
1436
|
this.#value = value;
|
|
701
1437
|
this.#options = options;
|
|
1438
|
+
this.#pageId = pageId || "page_0";
|
|
702
1439
|
}
|
|
703
1440
|
|
|
704
1441
|
_getInfo() { return [this.#type, this.#value, this.#options]; }
|
|
1442
|
+
_getPageId() { return this.#pageId; }
|
|
1443
|
+
|
|
1444
|
+
// Helper to create a chained locator
|
|
1445
|
+
_chain(childType, childValue, childOptions) {
|
|
1446
|
+
const parentInfo = this._getInfo();
|
|
1447
|
+
const childInfo = [childType, childValue, childOptions];
|
|
1448
|
+
return new Locator("chained", JSON.stringify([parentInfo, childInfo]), null, this.#pageId);
|
|
1449
|
+
}
|
|
705
1450
|
|
|
706
1451
|
async click() {
|
|
707
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "click", null]);
|
|
1452
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "click", null], { pageId: this.#pageId });
|
|
708
1453
|
}
|
|
709
1454
|
async dblclick() {
|
|
710
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "dblclick", null]);
|
|
1455
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "dblclick", null], { pageId: this.#pageId });
|
|
711
1456
|
}
|
|
712
1457
|
async fill(text) {
|
|
713
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "fill", text]);
|
|
1458
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "fill", text], { pageId: this.#pageId });
|
|
714
1459
|
}
|
|
715
1460
|
async type(text) {
|
|
716
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "type", text]);
|
|
1461
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "type", text], { pageId: this.#pageId });
|
|
717
1462
|
}
|
|
718
1463
|
async check() {
|
|
719
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "check", null]);
|
|
1464
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "check", null], { pageId: this.#pageId });
|
|
720
1465
|
}
|
|
721
1466
|
async uncheck() {
|
|
722
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "uncheck", null]);
|
|
1467
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "uncheck", null], { pageId: this.#pageId });
|
|
723
1468
|
}
|
|
724
1469
|
async selectOption(value) {
|
|
725
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "selectOption", value]);
|
|
1470
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "selectOption", value], { pageId: this.#pageId });
|
|
726
1471
|
}
|
|
727
1472
|
async clear() {
|
|
728
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "clear", null]);
|
|
1473
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "clear", null], { pageId: this.#pageId });
|
|
729
1474
|
}
|
|
730
1475
|
async press(key) {
|
|
731
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "press", key]);
|
|
1476
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "press", key], { pageId: this.#pageId });
|
|
732
1477
|
}
|
|
733
1478
|
async hover() {
|
|
734
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "hover", null]);
|
|
1479
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "hover", null], { pageId: this.#pageId });
|
|
735
1480
|
}
|
|
736
1481
|
async focus() {
|
|
737
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "focus", null]);
|
|
1482
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "focus", null], { pageId: this.#pageId });
|
|
738
1483
|
}
|
|
739
1484
|
async textContent() {
|
|
740
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getText", null]);
|
|
1485
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getText", null], { pageId: this.#pageId });
|
|
741
1486
|
}
|
|
742
1487
|
async inputValue() {
|
|
743
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getValue", null]);
|
|
1488
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getValue", null], { pageId: this.#pageId });
|
|
744
1489
|
}
|
|
745
1490
|
async isVisible() {
|
|
746
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isVisible", null]);
|
|
1491
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isVisible", null], { pageId: this.#pageId });
|
|
747
1492
|
}
|
|
748
1493
|
async isEnabled() {
|
|
749
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isEnabled", null]);
|
|
1494
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isEnabled", null], { pageId: this.#pageId });
|
|
750
1495
|
}
|
|
751
1496
|
async isChecked() {
|
|
752
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isChecked", null]);
|
|
1497
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isChecked", null], { pageId: this.#pageId });
|
|
753
1498
|
}
|
|
754
1499
|
async count() {
|
|
755
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "count", null]);
|
|
1500
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "count", null], { pageId: this.#pageId });
|
|
756
1501
|
}
|
|
757
1502
|
async getAttribute(name) {
|
|
758
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name]);
|
|
1503
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name], { pageId: this.#pageId });
|
|
759
1504
|
}
|
|
760
1505
|
async isDisabled() {
|
|
761
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null]);
|
|
1506
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null], { pageId: this.#pageId });
|
|
762
1507
|
}
|
|
763
1508
|
async isHidden() {
|
|
764
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null]);
|
|
1509
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null], { pageId: this.#pageId });
|
|
765
1510
|
}
|
|
766
1511
|
async innerHTML() {
|
|
767
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null]);
|
|
1512
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null], { pageId: this.#pageId });
|
|
768
1513
|
}
|
|
769
1514
|
async innerText() {
|
|
770
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null]);
|
|
1515
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null], { pageId: this.#pageId });
|
|
771
1516
|
}
|
|
772
1517
|
async allTextContents() {
|
|
773
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null]);
|
|
1518
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null], { pageId: this.#pageId });
|
|
774
1519
|
}
|
|
775
1520
|
async allInnerTexts() {
|
|
776
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null]);
|
|
1521
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null], { pageId: this.#pageId });
|
|
777
1522
|
}
|
|
778
1523
|
async waitFor(options) {
|
|
779
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}]);
|
|
1524
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}], { pageId: this.#pageId });
|
|
780
1525
|
}
|
|
781
1526
|
async boundingBox() {
|
|
782
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null]);
|
|
1527
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null], { pageId: this.#pageId });
|
|
783
1528
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1529
|
+
async setInputFiles(files) {
|
|
1530
|
+
// Serialize files - if they have buffers, convert to base64
|
|
1531
|
+
let serializedFiles = files;
|
|
1532
|
+
if (Array.isArray(files) && files.length > 0 && typeof files[0] === 'object' && files[0].buffer) {
|
|
1533
|
+
serializedFiles = files.map(f => ({
|
|
1534
|
+
name: f.name,
|
|
1535
|
+
mimeType: f.mimeType,
|
|
1536
|
+
buffer: typeof f.buffer === 'string' ? f.buffer : btoa(String.fromCharCode(...new Uint8Array(f.buffer)))
|
|
1537
|
+
}));
|
|
788
1538
|
}
|
|
789
|
-
|
|
790
|
-
|
|
1539
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "setInputFiles", serializedFiles], { pageId: this.#pageId });
|
|
1540
|
+
}
|
|
1541
|
+
async screenshot(options) {
|
|
1542
|
+
const base64 = await __pw_invoke("locatorAction", [...this._getInfo(), "screenshot", options || {}], { pageId: this.#pageId });
|
|
1543
|
+
return base64;
|
|
1544
|
+
}
|
|
1545
|
+
async dragTo(target) {
|
|
1546
|
+
const targetInfo = target._getInfo();
|
|
1547
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "dragTo", targetInfo], { pageId: this.#pageId });
|
|
1548
|
+
}
|
|
1549
|
+
async scrollIntoViewIfNeeded() {
|
|
1550
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "scrollIntoViewIfNeeded", null], { pageId: this.#pageId });
|
|
1551
|
+
}
|
|
1552
|
+
async highlight() {
|
|
1553
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "highlight", null], { pageId: this.#pageId });
|
|
1554
|
+
}
|
|
1555
|
+
async evaluate(fn, arg) {
|
|
1556
|
+
const fnString = typeof fn === 'function' ? fn.toString() : fn;
|
|
1557
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "evaluate", [fnString, arg]], { pageId: this.#pageId });
|
|
1558
|
+
}
|
|
1559
|
+
async evaluateAll(fn, arg) {
|
|
1560
|
+
const fnString = typeof fn === 'function' ? fn.toString() : fn;
|
|
1561
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "evaluateAll", [fnString, arg]], { pageId: this.#pageId });
|
|
1562
|
+
}
|
|
1563
|
+
locator(selector) {
|
|
1564
|
+
return this._chain("css", selector, null);
|
|
1565
|
+
}
|
|
1566
|
+
// Chaining: getBy* methods within a locator
|
|
1567
|
+
getByRole(role, options) {
|
|
1568
|
+
return this._chain("role", role, serializeOptions(options));
|
|
1569
|
+
}
|
|
1570
|
+
getByText(text) {
|
|
1571
|
+
return this._chain("text", text, null);
|
|
1572
|
+
}
|
|
1573
|
+
getByLabel(label) {
|
|
1574
|
+
return this._chain("label", label, null);
|
|
1575
|
+
}
|
|
1576
|
+
getByPlaceholder(placeholder) {
|
|
1577
|
+
return this._chain("placeholder", placeholder, null);
|
|
1578
|
+
}
|
|
1579
|
+
getByTestId(testId) {
|
|
1580
|
+
return this._chain("testId", testId, null);
|
|
1581
|
+
}
|
|
1582
|
+
getByAltText(altText) {
|
|
1583
|
+
return this._chain("altText", altText, null);
|
|
1584
|
+
}
|
|
1585
|
+
getByTitle(title) {
|
|
1586
|
+
return this._chain("title", title, null);
|
|
791
1587
|
}
|
|
792
1588
|
async all() {
|
|
793
1589
|
const n = await this.count();
|
|
@@ -799,7 +1595,7 @@ async function setupPlaywright(context, options) {
|
|
|
799
1595
|
}
|
|
800
1596
|
nth(index) {
|
|
801
1597
|
const existingOpts = this.#options ? JSON.parse(this.#options) : {};
|
|
802
|
-
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }));
|
|
1598
|
+
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }), this.#pageId);
|
|
803
1599
|
}
|
|
804
1600
|
first() {
|
|
805
1601
|
return this.nth(0);
|
|
@@ -810,13 +1606,37 @@ async function setupPlaywright(context, options) {
|
|
|
810
1606
|
filter(options) {
|
|
811
1607
|
const existingOpts = this.#options ? JSON.parse(this.#options) : {};
|
|
812
1608
|
const serializedFilter = { ...options };
|
|
813
|
-
|
|
814
|
-
|
|
1609
|
+
// Use duck-typing RegExp detection (instanceof fails across isolated-vm boundary)
|
|
1610
|
+
const hasText = options.hasText;
|
|
1611
|
+
if (hasText && typeof hasText === 'object' && typeof hasText.source === 'string' && typeof hasText.flags === 'string') {
|
|
1612
|
+
serializedFilter.hasText = { $regex: hasText.source, $flags: hasText.flags };
|
|
815
1613
|
}
|
|
816
|
-
|
|
817
|
-
|
|
1614
|
+
const hasNotText = options.hasNotText;
|
|
1615
|
+
if (hasNotText && typeof hasNotText === 'object' && typeof hasNotText.source === 'string' && typeof hasNotText.flags === 'string') {
|
|
1616
|
+
serializedFilter.hasNotText = { $regex: hasNotText.source, $flags: hasNotText.flags };
|
|
818
1617
|
}
|
|
819
|
-
|
|
1618
|
+
// Serialize has/hasNot locators using duck-typing
|
|
1619
|
+
const has = options.has;
|
|
1620
|
+
if (has && typeof has === 'object' && typeof has._getInfo === 'function') {
|
|
1621
|
+
serializedFilter.has = { $locator: has._getInfo() };
|
|
1622
|
+
}
|
|
1623
|
+
const hasNot = options.hasNot;
|
|
1624
|
+
if (hasNot && typeof hasNot === 'object' && typeof hasNot._getInfo === 'function') {
|
|
1625
|
+
serializedFilter.hasNot = { $locator: hasNot._getInfo() };
|
|
1626
|
+
}
|
|
1627
|
+
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, filter: serializedFilter }), this.#pageId);
|
|
1628
|
+
}
|
|
1629
|
+
or(other) {
|
|
1630
|
+
// Create a composite locator that matches either this or other
|
|
1631
|
+
const thisInfo = this._getInfo();
|
|
1632
|
+
const otherInfo = other._getInfo();
|
|
1633
|
+
return new Locator("or", JSON.stringify([thisInfo, otherInfo]), null, this.#pageId);
|
|
1634
|
+
}
|
|
1635
|
+
and(other) {
|
|
1636
|
+
// Create a composite locator that matches both this and other
|
|
1637
|
+
const thisInfo = this._getInfo();
|
|
1638
|
+
const otherInfo = other._getInfo();
|
|
1639
|
+
return new Locator("and", JSON.stringify([thisInfo, otherInfo]), null, this.#pageId);
|
|
820
1640
|
}
|
|
821
1641
|
}
|
|
822
1642
|
globalThis.Locator = Locator;
|
|
@@ -827,84 +1647,157 @@ async function setupPlaywright(context, options) {
|
|
|
827
1647
|
// Helper to create locator matchers
|
|
828
1648
|
function createLocatorMatchers(locator, baseMatchers) {
|
|
829
1649
|
const info = locator._getInfo();
|
|
1650
|
+
const pageId = locator._getPageId ? locator._getPageId() : "page_0";
|
|
1651
|
+
|
|
1652
|
+
// Helper for serializing regex values
|
|
1653
|
+
function serializeExpected(expected) {
|
|
1654
|
+
if (expected instanceof RegExp) {
|
|
1655
|
+
return { $regex: expected.source, $flags: expected.flags };
|
|
1656
|
+
}
|
|
1657
|
+
return expected;
|
|
1658
|
+
}
|
|
830
1659
|
|
|
831
1660
|
const locatorMatchers = {
|
|
832
1661
|
async toBeVisible(options) {
|
|
833
|
-
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout]);
|
|
1662
|
+
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout], { pageId });
|
|
834
1663
|
},
|
|
835
1664
|
async toContainText(expected, options) {
|
|
836
|
-
|
|
837
|
-
return __pw_invoke("expectLocator", [...info, "toContainText", serialized, false, options?.timeout]);
|
|
1665
|
+
return __pw_invoke("expectLocator", [...info, "toContainText", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
838
1666
|
},
|
|
839
1667
|
async toHaveValue(expected, options) {
|
|
840
|
-
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout]);
|
|
1668
|
+
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout], { pageId });
|
|
841
1669
|
},
|
|
842
1670
|
async toBeEnabled(options) {
|
|
843
|
-
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, false, options?.timeout]);
|
|
1671
|
+
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, false, options?.timeout], { pageId });
|
|
844
1672
|
},
|
|
845
1673
|
async toBeChecked(options) {
|
|
846
|
-
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout]);
|
|
1674
|
+
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout], { pageId });
|
|
847
1675
|
},
|
|
848
1676
|
async toHaveAttribute(name, value, options) {
|
|
849
|
-
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, false, options?.timeout]);
|
|
1677
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value: serializeExpected(value) }, false, options?.timeout], { pageId });
|
|
850
1678
|
},
|
|
851
1679
|
async toHaveText(expected, options) {
|
|
852
|
-
|
|
853
|
-
return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, false, options?.timeout]);
|
|
1680
|
+
return __pw_invoke("expectLocator", [...info, "toHaveText", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
854
1681
|
},
|
|
855
1682
|
async toHaveCount(count, options) {
|
|
856
|
-
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout]);
|
|
1683
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout], { pageId });
|
|
857
1684
|
},
|
|
858
1685
|
async toBeHidden(options) {
|
|
859
|
-
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout]);
|
|
1686
|
+
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout], { pageId });
|
|
860
1687
|
},
|
|
861
1688
|
async toBeDisabled(options) {
|
|
862
|
-
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout]);
|
|
1689
|
+
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout], { pageId });
|
|
863
1690
|
},
|
|
864
1691
|
async toBeFocused(options) {
|
|
865
|
-
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout]);
|
|
1692
|
+
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout], { pageId });
|
|
866
1693
|
},
|
|
867
1694
|
async toBeEmpty(options) {
|
|
868
|
-
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout]);
|
|
1695
|
+
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout], { pageId });
|
|
1696
|
+
},
|
|
1697
|
+
// New matchers
|
|
1698
|
+
async toBeAttached(options) {
|
|
1699
|
+
return __pw_invoke("expectLocator", [...info, "toBeAttached", null, false, options?.timeout], { pageId });
|
|
1700
|
+
},
|
|
1701
|
+
async toBeEditable(options) {
|
|
1702
|
+
return __pw_invoke("expectLocator", [...info, "toBeEditable", null, false, options?.timeout], { pageId });
|
|
1703
|
+
},
|
|
1704
|
+
async toHaveClass(expected, options) {
|
|
1705
|
+
return __pw_invoke("expectLocator", [...info, "toHaveClass", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1706
|
+
},
|
|
1707
|
+
async toContainClass(expected, options) {
|
|
1708
|
+
return __pw_invoke("expectLocator", [...info, "toContainClass", expected, false, options?.timeout], { pageId });
|
|
1709
|
+
},
|
|
1710
|
+
async toHaveId(expected, options) {
|
|
1711
|
+
return __pw_invoke("expectLocator", [...info, "toHaveId", expected, false, options?.timeout], { pageId });
|
|
1712
|
+
},
|
|
1713
|
+
async toBeInViewport(options) {
|
|
1714
|
+
return __pw_invoke("expectLocator", [...info, "toBeInViewport", null, false, options?.timeout], { pageId });
|
|
1715
|
+
},
|
|
1716
|
+
async toHaveCSS(name, value, options) {
|
|
1717
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCSS", { name, value: serializeExpected(value) }, false, options?.timeout], { pageId });
|
|
1718
|
+
},
|
|
1719
|
+
async toHaveJSProperty(name, value, options) {
|
|
1720
|
+
return __pw_invoke("expectLocator", [...info, "toHaveJSProperty", { name, value }, false, options?.timeout], { pageId });
|
|
1721
|
+
},
|
|
1722
|
+
async toHaveAccessibleName(expected, options) {
|
|
1723
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleName", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1724
|
+
},
|
|
1725
|
+
async toHaveAccessibleDescription(expected, options) {
|
|
1726
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleDescription", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1727
|
+
},
|
|
1728
|
+
async toHaveRole(expected, options) {
|
|
1729
|
+
return __pw_invoke("expectLocator", [...info, "toHaveRole", expected, false, options?.timeout], { pageId });
|
|
869
1730
|
},
|
|
870
1731
|
not: {
|
|
871
1732
|
async toBeVisible(options) {
|
|
872
|
-
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout]);
|
|
1733
|
+
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout], { pageId });
|
|
873
1734
|
},
|
|
874
1735
|
async toContainText(expected, options) {
|
|
875
|
-
|
|
876
|
-
return __pw_invoke("expectLocator", [...info, "toContainText", serialized, true, options?.timeout]);
|
|
1736
|
+
return __pw_invoke("expectLocator", [...info, "toContainText", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
877
1737
|
},
|
|
878
1738
|
async toHaveValue(expected, options) {
|
|
879
|
-
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout]);
|
|
1739
|
+
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout], { pageId });
|
|
880
1740
|
},
|
|
881
1741
|
async toBeEnabled(options) {
|
|
882
|
-
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, true, options?.timeout]);
|
|
1742
|
+
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, true, options?.timeout], { pageId });
|
|
883
1743
|
},
|
|
884
1744
|
async toBeChecked(options) {
|
|
885
|
-
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout]);
|
|
1745
|
+
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout], { pageId });
|
|
886
1746
|
},
|
|
887
1747
|
async toHaveAttribute(name, value, options) {
|
|
888
|
-
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, true, options?.timeout]);
|
|
1748
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value: serializeExpected(value) }, true, options?.timeout], { pageId });
|
|
889
1749
|
},
|
|
890
1750
|
async toHaveText(expected, options) {
|
|
891
|
-
|
|
892
|
-
return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, true, options?.timeout]);
|
|
1751
|
+
return __pw_invoke("expectLocator", [...info, "toHaveText", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
893
1752
|
},
|
|
894
1753
|
async toHaveCount(count, options) {
|
|
895
|
-
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout]);
|
|
1754
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout], { pageId });
|
|
896
1755
|
},
|
|
897
1756
|
async toBeHidden(options) {
|
|
898
|
-
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout]);
|
|
1757
|
+
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout], { pageId });
|
|
899
1758
|
},
|
|
900
1759
|
async toBeDisabled(options) {
|
|
901
|
-
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout]);
|
|
1760
|
+
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout], { pageId });
|
|
902
1761
|
},
|
|
903
1762
|
async toBeFocused(options) {
|
|
904
|
-
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout]);
|
|
1763
|
+
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout], { pageId });
|
|
905
1764
|
},
|
|
906
1765
|
async toBeEmpty(options) {
|
|
907
|
-
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout]);
|
|
1766
|
+
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout], { pageId });
|
|
1767
|
+
},
|
|
1768
|
+
// New negated matchers
|
|
1769
|
+
async toBeAttached(options) {
|
|
1770
|
+
return __pw_invoke("expectLocator", [...info, "toBeAttached", null, true, options?.timeout], { pageId });
|
|
1771
|
+
},
|
|
1772
|
+
async toBeEditable(options) {
|
|
1773
|
+
return __pw_invoke("expectLocator", [...info, "toBeEditable", null, true, options?.timeout], { pageId });
|
|
1774
|
+
},
|
|
1775
|
+
async toHaveClass(expected, options) {
|
|
1776
|
+
return __pw_invoke("expectLocator", [...info, "toHaveClass", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1777
|
+
},
|
|
1778
|
+
async toContainClass(expected, options) {
|
|
1779
|
+
return __pw_invoke("expectLocator", [...info, "toContainClass", expected, true, options?.timeout], { pageId });
|
|
1780
|
+
},
|
|
1781
|
+
async toHaveId(expected, options) {
|
|
1782
|
+
return __pw_invoke("expectLocator", [...info, "toHaveId", expected, true, options?.timeout], { pageId });
|
|
1783
|
+
},
|
|
1784
|
+
async toBeInViewport(options) {
|
|
1785
|
+
return __pw_invoke("expectLocator", [...info, "toBeInViewport", null, true, options?.timeout], { pageId });
|
|
1786
|
+
},
|
|
1787
|
+
async toHaveCSS(name, value, options) {
|
|
1788
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCSS", { name, value: serializeExpected(value) }, true, options?.timeout], { pageId });
|
|
1789
|
+
},
|
|
1790
|
+
async toHaveJSProperty(name, value, options) {
|
|
1791
|
+
return __pw_invoke("expectLocator", [...info, "toHaveJSProperty", { name, value }, true, options?.timeout], { pageId });
|
|
1792
|
+
},
|
|
1793
|
+
async toHaveAccessibleName(expected, options) {
|
|
1794
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleName", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1795
|
+
},
|
|
1796
|
+
async toHaveAccessibleDescription(expected, options) {
|
|
1797
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleDescription", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1798
|
+
},
|
|
1799
|
+
async toHaveRole(expected, options) {
|
|
1800
|
+
return __pw_invoke("expectLocator", [...info, "toHaveRole", expected, true, options?.timeout], { pageId });
|
|
908
1801
|
},
|
|
909
1802
|
}
|
|
910
1803
|
};
|
|
@@ -920,6 +1813,44 @@ async function setupPlaywright(context, options) {
|
|
|
920
1813
|
return locatorMatchers;
|
|
921
1814
|
}
|
|
922
1815
|
|
|
1816
|
+
// Helper to create page matchers
|
|
1817
|
+
function createPageMatchers(page, baseMatchers) {
|
|
1818
|
+
const pageId = page.__pageId || "page_0";
|
|
1819
|
+
|
|
1820
|
+
function serializeExpected(expected) {
|
|
1821
|
+
if (expected instanceof RegExp) {
|
|
1822
|
+
return { $regex: expected.source, $flags: expected.flags };
|
|
1823
|
+
}
|
|
1824
|
+
return expected;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
const pageMatchers = {
|
|
1828
|
+
async toHaveURL(expected, options) {
|
|
1829
|
+
return __pw_invoke("expectPage", ["toHaveURL", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1830
|
+
},
|
|
1831
|
+
async toHaveTitle(expected, options) {
|
|
1832
|
+
return __pw_invoke("expectPage", ["toHaveTitle", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1833
|
+
},
|
|
1834
|
+
not: {
|
|
1835
|
+
async toHaveURL(expected, options) {
|
|
1836
|
+
return __pw_invoke("expectPage", ["toHaveURL", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1837
|
+
},
|
|
1838
|
+
async toHaveTitle(expected, options) {
|
|
1839
|
+
return __pw_invoke("expectPage", ["toHaveTitle", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1840
|
+
},
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
|
|
1844
|
+
if (baseMatchers) {
|
|
1845
|
+
return {
|
|
1846
|
+
...baseMatchers,
|
|
1847
|
+
...pageMatchers,
|
|
1848
|
+
not: { ...baseMatchers.not, ...pageMatchers.not }
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
return pageMatchers;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
923
1854
|
// Only extend expect if test-environment already defined it
|
|
924
1855
|
if (typeof globalThis.expect === 'function') {
|
|
925
1856
|
const originalExpect = globalThis.expect;
|
|
@@ -929,6 +1860,10 @@ async function setupPlaywright(context, options) {
|
|
|
929
1860
|
if (actual && actual.constructor && actual.constructor.name === 'Locator') {
|
|
930
1861
|
return createLocatorMatchers(actual, baseMatchers);
|
|
931
1862
|
}
|
|
1863
|
+
// If actual is the page object (IsolatePage), add page-specific matchers
|
|
1864
|
+
if (actual && actual.__isPage === true) {
|
|
1865
|
+
return createPageMatchers(actual, baseMatchers);
|
|
1866
|
+
}
|
|
932
1867
|
return baseMatchers;
|
|
933
1868
|
};
|
|
934
1869
|
}
|
|
@@ -963,4 +1898,4 @@ async function setupPlaywright(context, options) {
|
|
|
963
1898
|
};
|
|
964
1899
|
}
|
|
965
1900
|
|
|
966
|
-
//# debugId=
|
|
1901
|
+
//# debugId=EC7671328EAA005364756E2164756E21
|