@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/mjs/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// packages/playwright/src/index.ts
|
|
2
2
|
import ivm from "isolated-vm";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_PLAYWRIGHT_HANDLER_META
|
|
5
|
+
} from "./types.mjs";
|
|
3
6
|
function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
4
7
|
const options = optionsJson ? JSON.parse(optionsJson) : undefined;
|
|
5
8
|
const nthIndex = options?.nth;
|
|
@@ -31,6 +34,145 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
|
31
34
|
case "testId":
|
|
32
35
|
locator = page.getByTestId(selectorValue);
|
|
33
36
|
break;
|
|
37
|
+
case "or": {
|
|
38
|
+
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
39
|
+
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
40
|
+
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
41
|
+
locator = first.or(second);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case "and": {
|
|
45
|
+
const [firstInfo, secondInfo] = JSON.parse(selectorValue);
|
|
46
|
+
const first = getLocator(page, firstInfo[0], firstInfo[1], firstInfo[2]);
|
|
47
|
+
const second = getLocator(page, secondInfo[0], secondInfo[1], secondInfo[2]);
|
|
48
|
+
locator = first.and(second);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "chained": {
|
|
52
|
+
const [parentInfo, childInfo] = JSON.parse(selectorValue);
|
|
53
|
+
const parent = getLocator(page, parentInfo[0], parentInfo[1], parentInfo[2]);
|
|
54
|
+
const childType = childInfo[0];
|
|
55
|
+
const childValue = childInfo[1];
|
|
56
|
+
const childOptionsJson = childInfo[2];
|
|
57
|
+
const childOptions = childOptionsJson ? JSON.parse(childOptionsJson) : undefined;
|
|
58
|
+
switch (childType) {
|
|
59
|
+
case "css":
|
|
60
|
+
locator = parent.locator(childValue);
|
|
61
|
+
break;
|
|
62
|
+
case "role": {
|
|
63
|
+
const roleOpts = childOptions ? { ...childOptions } : undefined;
|
|
64
|
+
if (roleOpts) {
|
|
65
|
+
delete roleOpts.nth;
|
|
66
|
+
delete roleOpts.filter;
|
|
67
|
+
if (roleOpts.name && typeof roleOpts.name === "object" && roleOpts.name.$regex) {
|
|
68
|
+
roleOpts.name = new RegExp(roleOpts.name.$regex, roleOpts.name.$flags);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
locator = parent.getByRole(childValue, roleOpts && Object.keys(roleOpts).length > 0 ? roleOpts : undefined);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "text":
|
|
75
|
+
locator = parent.getByText(childValue);
|
|
76
|
+
break;
|
|
77
|
+
case "label":
|
|
78
|
+
locator = parent.getByLabel(childValue);
|
|
79
|
+
break;
|
|
80
|
+
case "placeholder":
|
|
81
|
+
locator = parent.getByPlaceholder(childValue);
|
|
82
|
+
break;
|
|
83
|
+
case "testId":
|
|
84
|
+
locator = parent.getByTestId(childValue);
|
|
85
|
+
break;
|
|
86
|
+
case "altText":
|
|
87
|
+
locator = parent.getByAltText(childValue);
|
|
88
|
+
break;
|
|
89
|
+
case "title":
|
|
90
|
+
locator = parent.getByTitle(childValue);
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
locator = parent.locator(childValue);
|
|
94
|
+
}
|
|
95
|
+
if (childOptions?.nth !== undefined) {
|
|
96
|
+
locator = locator.nth(childOptions.nth);
|
|
97
|
+
}
|
|
98
|
+
if (childOptions?.filter) {
|
|
99
|
+
const filterOpts = { ...childOptions.filter };
|
|
100
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
101
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
102
|
+
}
|
|
103
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
104
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
105
|
+
}
|
|
106
|
+
locator = locator.filter(filterOpts);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case "altText":
|
|
111
|
+
locator = page.getByAltText(selectorValue);
|
|
112
|
+
break;
|
|
113
|
+
case "title":
|
|
114
|
+
locator = page.getByTitle(selectorValue);
|
|
115
|
+
break;
|
|
116
|
+
case "frame": {
|
|
117
|
+
const [frameSelectorInfo, innerLocatorInfo] = JSON.parse(selectorValue);
|
|
118
|
+
const frameSelector = frameSelectorInfo[1];
|
|
119
|
+
const frame = page.frameLocator(frameSelector);
|
|
120
|
+
const innerType = innerLocatorInfo[0];
|
|
121
|
+
const innerValue = innerLocatorInfo[1];
|
|
122
|
+
const innerOptionsJson = innerLocatorInfo[2];
|
|
123
|
+
const innerOptions = innerOptionsJson ? JSON.parse(innerOptionsJson) : undefined;
|
|
124
|
+
switch (innerType) {
|
|
125
|
+
case "css":
|
|
126
|
+
locator = frame.locator(innerValue);
|
|
127
|
+
break;
|
|
128
|
+
case "role": {
|
|
129
|
+
const roleOpts = innerOptions ? { ...innerOptions } : undefined;
|
|
130
|
+
if (roleOpts) {
|
|
131
|
+
delete roleOpts.nth;
|
|
132
|
+
delete roleOpts.filter;
|
|
133
|
+
if (roleOpts.name && typeof roleOpts.name === "object" && roleOpts.name.$regex) {
|
|
134
|
+
roleOpts.name = new RegExp(roleOpts.name.$regex, roleOpts.name.$flags);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
locator = frame.getByRole(innerValue, roleOpts && Object.keys(roleOpts).length > 0 ? roleOpts : undefined);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case "text":
|
|
141
|
+
locator = frame.getByText(innerValue);
|
|
142
|
+
break;
|
|
143
|
+
case "label":
|
|
144
|
+
locator = frame.getByLabel(innerValue);
|
|
145
|
+
break;
|
|
146
|
+
case "placeholder":
|
|
147
|
+
locator = frame.getByPlaceholder(innerValue);
|
|
148
|
+
break;
|
|
149
|
+
case "testId":
|
|
150
|
+
locator = frame.getByTestId(innerValue);
|
|
151
|
+
break;
|
|
152
|
+
case "altText":
|
|
153
|
+
locator = frame.getByAltText(innerValue);
|
|
154
|
+
break;
|
|
155
|
+
case "title":
|
|
156
|
+
locator = frame.getByTitle(innerValue);
|
|
157
|
+
break;
|
|
158
|
+
default:
|
|
159
|
+
locator = frame.locator(innerValue);
|
|
160
|
+
}
|
|
161
|
+
if (innerOptions?.nth !== undefined) {
|
|
162
|
+
locator = locator.nth(innerOptions.nth);
|
|
163
|
+
}
|
|
164
|
+
if (innerOptions?.filter) {
|
|
165
|
+
const filterOpts = { ...innerOptions.filter };
|
|
166
|
+
if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
|
|
167
|
+
filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
|
|
168
|
+
}
|
|
169
|
+
if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
|
|
170
|
+
filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
|
|
171
|
+
}
|
|
172
|
+
locator = locator.filter(filterOpts);
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
34
176
|
default:
|
|
35
177
|
locator = page.locator(selectorValue);
|
|
36
178
|
}
|
|
@@ -49,7 +191,7 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
|
|
|
49
191
|
}
|
|
50
192
|
return locator;
|
|
51
193
|
}
|
|
52
|
-
async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
194
|
+
async function executeLocatorAction(locator, action, actionArg, timeout, fileIO) {
|
|
53
195
|
switch (action) {
|
|
54
196
|
case "click":
|
|
55
197
|
await locator.click({ timeout });
|
|
@@ -117,6 +259,69 @@ async function executeLocatorAction(locator, action, actionArg, timeout) {
|
|
|
117
259
|
}
|
|
118
260
|
case "boundingBox":
|
|
119
261
|
return await locator.boundingBox({ timeout });
|
|
262
|
+
case "setInputFiles": {
|
|
263
|
+
const files = actionArg;
|
|
264
|
+
if (Array.isArray(files) && files.length > 0 && typeof files[0] === "object" && "buffer" in files[0]) {
|
|
265
|
+
const fileBuffers2 = files.map((f) => ({
|
|
266
|
+
name: f.name,
|
|
267
|
+
mimeType: f.mimeType,
|
|
268
|
+
buffer: Buffer.from(f.buffer, "base64")
|
|
269
|
+
}));
|
|
270
|
+
await locator.setInputFiles(fileBuffers2, { timeout });
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const filePaths = Array.isArray(files) ? files : [files];
|
|
274
|
+
if (!fileIO?.readFile) {
|
|
275
|
+
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.");
|
|
276
|
+
}
|
|
277
|
+
const fileBuffers = await Promise.all(filePaths.map(async (filePath) => {
|
|
278
|
+
const fileData = await fileIO.readFile(filePath);
|
|
279
|
+
return {
|
|
280
|
+
name: fileData.name,
|
|
281
|
+
mimeType: fileData.mimeType,
|
|
282
|
+
buffer: fileData.buffer
|
|
283
|
+
};
|
|
284
|
+
}));
|
|
285
|
+
await locator.setInputFiles(fileBuffers, { timeout });
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
case "screenshot": {
|
|
289
|
+
const opts = actionArg;
|
|
290
|
+
const buffer = await locator.screenshot({
|
|
291
|
+
timeout,
|
|
292
|
+
type: opts?.type,
|
|
293
|
+
quality: opts?.quality
|
|
294
|
+
});
|
|
295
|
+
if (opts?.path) {
|
|
296
|
+
if (!fileIO?.writeFile) {
|
|
297
|
+
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.");
|
|
298
|
+
}
|
|
299
|
+
await fileIO.writeFile(opts.path, buffer);
|
|
300
|
+
}
|
|
301
|
+
return buffer.toString("base64");
|
|
302
|
+
}
|
|
303
|
+
case "dragTo": {
|
|
304
|
+
const targetInfo = actionArg;
|
|
305
|
+
const targetLocator = getLocator(locator.page(), targetInfo[0], targetInfo[1], targetInfo[2]);
|
|
306
|
+
await locator.dragTo(targetLocator, { timeout });
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
case "scrollIntoViewIfNeeded":
|
|
310
|
+
await locator.scrollIntoViewIfNeeded({ timeout });
|
|
311
|
+
return null;
|
|
312
|
+
case "highlight":
|
|
313
|
+
await locator.highlight();
|
|
314
|
+
return null;
|
|
315
|
+
case "evaluate": {
|
|
316
|
+
const [fnString, arg] = actionArg;
|
|
317
|
+
const fn = new Function("return (" + fnString + ")")();
|
|
318
|
+
return await locator.evaluate(fn, arg);
|
|
319
|
+
}
|
|
320
|
+
case "evaluateAll": {
|
|
321
|
+
const [fnString, arg] = actionArg;
|
|
322
|
+
const fn = new Function("return (" + fnString + ")")();
|
|
323
|
+
return await locator.evaluateAll(fn, arg);
|
|
324
|
+
}
|
|
120
325
|
default:
|
|
121
326
|
throw new Error(`Unknown action: ${action}`);
|
|
122
327
|
}
|
|
@@ -293,76 +498,364 @@ async function executeExpectAssertion(locator, matcher, expected, negated, timeo
|
|
|
293
498
|
}
|
|
294
499
|
break;
|
|
295
500
|
}
|
|
501
|
+
case "toBeAttached": {
|
|
502
|
+
const count = await locator.count();
|
|
503
|
+
const isAttached = count > 0;
|
|
504
|
+
if (negated) {
|
|
505
|
+
if (isAttached)
|
|
506
|
+
throw new Error("Expected element to not be attached to DOM, but it was");
|
|
507
|
+
} else {
|
|
508
|
+
if (!isAttached)
|
|
509
|
+
throw new Error("Expected element to be attached to DOM, but it was not");
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case "toBeEditable": {
|
|
514
|
+
const isEditable = await locator.isEditable({ timeout });
|
|
515
|
+
if (negated) {
|
|
516
|
+
if (isEditable)
|
|
517
|
+
throw new Error("Expected element to not be editable, but it was");
|
|
518
|
+
} else {
|
|
519
|
+
if (!isEditable)
|
|
520
|
+
throw new Error("Expected element to be editable, but it was not");
|
|
521
|
+
}
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
case "toHaveClass": {
|
|
525
|
+
const classAttr = await locator.getAttribute("class", { timeout }) ?? "";
|
|
526
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
527
|
+
let matches;
|
|
528
|
+
let expectedDisplay;
|
|
529
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
530
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
531
|
+
matches = regex.test(classAttr);
|
|
532
|
+
expectedDisplay = String(regex);
|
|
533
|
+
} else if (Array.isArray(expected)) {
|
|
534
|
+
matches = expected.every((c) => classes.includes(c));
|
|
535
|
+
expectedDisplay = JSON.stringify(expected);
|
|
536
|
+
} else {
|
|
537
|
+
matches = classAttr === String(expected) || classes.includes(String(expected));
|
|
538
|
+
expectedDisplay = String(expected);
|
|
539
|
+
}
|
|
540
|
+
if (negated) {
|
|
541
|
+
if (matches)
|
|
542
|
+
throw new Error(`Expected class to not match ${expectedDisplay}, but got "${classAttr}"`);
|
|
543
|
+
} else {
|
|
544
|
+
if (!matches)
|
|
545
|
+
throw new Error(`Expected class to match ${expectedDisplay}, but got "${classAttr}"`);
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
case "toContainClass": {
|
|
550
|
+
const classAttr = await locator.getAttribute("class", { timeout }) ?? "";
|
|
551
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
552
|
+
const expectedClass = String(expected);
|
|
553
|
+
const hasClass = classes.includes(expectedClass);
|
|
554
|
+
if (negated) {
|
|
555
|
+
if (hasClass)
|
|
556
|
+
throw new Error(`Expected element to not contain class "${expectedClass}", but it does`);
|
|
557
|
+
} else {
|
|
558
|
+
if (!hasClass)
|
|
559
|
+
throw new Error(`Expected element to contain class "${expectedClass}", but classes are "${classAttr}"`);
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
case "toHaveId": {
|
|
564
|
+
const id = await locator.getAttribute("id", { timeout });
|
|
565
|
+
const matches = id === String(expected);
|
|
566
|
+
if (negated) {
|
|
567
|
+
if (matches)
|
|
568
|
+
throw new Error(`Expected id to not be "${expected}", but it was`);
|
|
569
|
+
} else {
|
|
570
|
+
if (!matches)
|
|
571
|
+
throw new Error(`Expected id to be "${expected}", but got "${id}"`);
|
|
572
|
+
}
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
case "toBeInViewport": {
|
|
576
|
+
const isInViewport = await locator.evaluate((el) => {
|
|
577
|
+
const rect = el.getBoundingClientRect();
|
|
578
|
+
return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
|
|
579
|
+
});
|
|
580
|
+
if (negated) {
|
|
581
|
+
if (isInViewport)
|
|
582
|
+
throw new Error("Expected element to not be in viewport, but it was");
|
|
583
|
+
} else {
|
|
584
|
+
if (!isInViewport)
|
|
585
|
+
throw new Error("Expected element to be in viewport, but it was not");
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
case "toHaveCSS": {
|
|
590
|
+
const { name, value } = expected;
|
|
591
|
+
const actual = await locator.evaluate((el, propName) => {
|
|
592
|
+
return getComputedStyle(el).getPropertyValue(propName);
|
|
593
|
+
}, name);
|
|
594
|
+
let matches;
|
|
595
|
+
if (value && typeof value === "object" && value.$regex) {
|
|
596
|
+
const regex = new RegExp(value.$regex, value.$flags);
|
|
597
|
+
matches = regex.test(actual);
|
|
598
|
+
} else {
|
|
599
|
+
matches = actual === String(value);
|
|
600
|
+
}
|
|
601
|
+
if (negated) {
|
|
602
|
+
if (matches)
|
|
603
|
+
throw new Error(`Expected CSS "${name}" to not be "${value}", but it was`);
|
|
604
|
+
} else {
|
|
605
|
+
if (!matches)
|
|
606
|
+
throw new Error(`Expected CSS "${name}" to be "${value}", but got "${actual}"`);
|
|
607
|
+
}
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
case "toHaveJSProperty": {
|
|
611
|
+
const { name, value } = expected;
|
|
612
|
+
const actual = await locator.evaluate((el, propName) => {
|
|
613
|
+
return el[propName];
|
|
614
|
+
}, name);
|
|
615
|
+
const matches = JSON.stringify(actual) === JSON.stringify(value);
|
|
616
|
+
if (negated) {
|
|
617
|
+
if (matches)
|
|
618
|
+
throw new Error(`Expected JS property "${name}" to not be ${JSON.stringify(value)}, but it was`);
|
|
619
|
+
} else {
|
|
620
|
+
if (!matches)
|
|
621
|
+
throw new Error(`Expected JS property "${name}" to be ${JSON.stringify(value)}, but got ${JSON.stringify(actual)}`);
|
|
622
|
+
}
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
case "toHaveAccessibleName": {
|
|
626
|
+
const accessibleName = await locator.evaluate((el) => {
|
|
627
|
+
return el.getAttribute("aria-label") || el.getAttribute("aria-labelledby") || el.innerText || "";
|
|
628
|
+
});
|
|
629
|
+
let matches;
|
|
630
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
631
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
632
|
+
matches = regex.test(accessibleName);
|
|
633
|
+
} else {
|
|
634
|
+
matches = accessibleName === String(expected);
|
|
635
|
+
}
|
|
636
|
+
if (negated) {
|
|
637
|
+
if (matches)
|
|
638
|
+
throw new Error(`Expected accessible name to not be "${expected}", but it was`);
|
|
639
|
+
} else {
|
|
640
|
+
if (!matches)
|
|
641
|
+
throw new Error(`Expected accessible name to be "${expected}", but got "${accessibleName}"`);
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
case "toHaveAccessibleDescription": {
|
|
646
|
+
const accessibleDesc = await locator.evaluate((el) => {
|
|
647
|
+
const describedby = el.getAttribute("aria-describedby");
|
|
648
|
+
if (describedby) {
|
|
649
|
+
const descEl = document.getElementById(describedby);
|
|
650
|
+
return descEl?.textContent || "";
|
|
651
|
+
}
|
|
652
|
+
return el.getAttribute("aria-description") || "";
|
|
653
|
+
});
|
|
654
|
+
let matches;
|
|
655
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
656
|
+
const regex = new RegExp(expected.$regex, expected.$flags);
|
|
657
|
+
matches = regex.test(accessibleDesc);
|
|
658
|
+
} else {
|
|
659
|
+
matches = accessibleDesc === String(expected);
|
|
660
|
+
}
|
|
661
|
+
if (negated) {
|
|
662
|
+
if (matches)
|
|
663
|
+
throw new Error(`Expected accessible description to not be "${expected}", but it was`);
|
|
664
|
+
} else {
|
|
665
|
+
if (!matches)
|
|
666
|
+
throw new Error(`Expected accessible description to be "${expected}", but got "${accessibleDesc}"`);
|
|
667
|
+
}
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
case "toHaveRole": {
|
|
671
|
+
const role = await locator.evaluate((el) => {
|
|
672
|
+
return el.getAttribute("role") || el.tagName.toLowerCase();
|
|
673
|
+
});
|
|
674
|
+
const matches = role === String(expected);
|
|
675
|
+
if (negated) {
|
|
676
|
+
if (matches)
|
|
677
|
+
throw new Error(`Expected role to not be "${expected}", but it was`);
|
|
678
|
+
} else {
|
|
679
|
+
if (!matches)
|
|
680
|
+
throw new Error(`Expected role to be "${expected}", but got "${role}"`);
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
296
684
|
default:
|
|
297
685
|
throw new Error(`Unknown matcher: ${matcher}`);
|
|
298
686
|
}
|
|
299
687
|
}
|
|
688
|
+
async function executePageExpectAssertion(page, matcher, expected, negated, timeout) {
|
|
689
|
+
let expectedValue = expected;
|
|
690
|
+
if (expected && typeof expected === "object" && expected.$regex) {
|
|
691
|
+
expectedValue = new RegExp(expected.$regex, expected.$flags);
|
|
692
|
+
}
|
|
693
|
+
switch (matcher) {
|
|
694
|
+
case "toHaveURL": {
|
|
695
|
+
const expectedUrl = expectedValue;
|
|
696
|
+
const startTime = Date.now();
|
|
697
|
+
let lastUrl = "";
|
|
698
|
+
while (Date.now() - startTime < timeout) {
|
|
699
|
+
lastUrl = page.url();
|
|
700
|
+
const matches = expectedUrl instanceof RegExp ? expectedUrl.test(lastUrl) : lastUrl === expectedUrl;
|
|
701
|
+
if (negated ? !matches : matches)
|
|
702
|
+
return;
|
|
703
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
704
|
+
}
|
|
705
|
+
if (negated) {
|
|
706
|
+
throw new Error(`Expected URL to not match "${expectedUrl}", but got "${lastUrl}"`);
|
|
707
|
+
} else {
|
|
708
|
+
throw new Error(`Expected URL to be "${expectedUrl}", but got "${lastUrl}"`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
case "toHaveTitle": {
|
|
712
|
+
const expectedTitle = expectedValue;
|
|
713
|
+
const startTime = Date.now();
|
|
714
|
+
let lastTitle = "";
|
|
715
|
+
while (Date.now() - startTime < timeout) {
|
|
716
|
+
lastTitle = await page.title();
|
|
717
|
+
const matches = expectedTitle instanceof RegExp ? expectedTitle.test(lastTitle) : lastTitle === expectedTitle;
|
|
718
|
+
if (negated ? !matches : matches)
|
|
719
|
+
return;
|
|
720
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
721
|
+
}
|
|
722
|
+
if (negated) {
|
|
723
|
+
throw new Error(`Expected title to not match "${expectedTitle}", but got "${lastTitle}"`);
|
|
724
|
+
} else {
|
|
725
|
+
throw new Error(`Expected title to be "${expectedTitle}", but got "${lastTitle}"`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
default:
|
|
729
|
+
throw new Error(`Unknown page matcher: ${matcher}`);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
300
732
|
function createPlaywrightHandler(page, options) {
|
|
301
733
|
const timeout = options?.timeout ?? 30000;
|
|
302
|
-
const
|
|
734
|
+
const fileIO = {
|
|
735
|
+
readFile: options?.readFile,
|
|
736
|
+
writeFile: options?.writeFile
|
|
737
|
+
};
|
|
738
|
+
const registry = {
|
|
739
|
+
pages: new Map([["page_0", page]]),
|
|
740
|
+
contexts: new Map([["ctx_0", page.context()]]),
|
|
741
|
+
nextPageId: 1,
|
|
742
|
+
nextContextId: 1
|
|
743
|
+
};
|
|
303
744
|
return async (op) => {
|
|
304
745
|
try {
|
|
746
|
+
switch (op.type) {
|
|
747
|
+
case "newContext": {
|
|
748
|
+
if (!options?.createContext) {
|
|
749
|
+
return { ok: false, error: { name: "Error", message: "createContext callback not provided. Configure createContext in playwright options to enable browser.newContext()." } };
|
|
750
|
+
}
|
|
751
|
+
const [contextOptions] = op.args;
|
|
752
|
+
const newContext = await options.createContext(contextOptions);
|
|
753
|
+
const contextId2 = `ctx_${registry.nextContextId++}`;
|
|
754
|
+
registry.contexts.set(contextId2, newContext);
|
|
755
|
+
return { ok: true, value: { contextId: contextId2 } };
|
|
756
|
+
}
|
|
757
|
+
case "newPage": {
|
|
758
|
+
if (!options?.createPage) {
|
|
759
|
+
return { ok: false, error: { name: "Error", message: "createPage callback not provided. Configure createPage in playwright options to enable context.newPage()." } };
|
|
760
|
+
}
|
|
761
|
+
const contextId2 = op.contextId ?? "ctx_0";
|
|
762
|
+
const targetContext2 = registry.contexts.get(contextId2);
|
|
763
|
+
if (!targetContext2) {
|
|
764
|
+
return { ok: false, error: { name: "Error", message: `Context ${contextId2} not found` } };
|
|
765
|
+
}
|
|
766
|
+
const newPage = await options.createPage(targetContext2);
|
|
767
|
+
const pageId2 = `page_${registry.nextPageId++}`;
|
|
768
|
+
registry.pages.set(pageId2, newPage);
|
|
769
|
+
return { ok: true, value: { pageId: pageId2 } };
|
|
770
|
+
}
|
|
771
|
+
case "closeContext": {
|
|
772
|
+
const contextId2 = op.contextId ?? "ctx_0";
|
|
773
|
+
const context = registry.contexts.get(contextId2);
|
|
774
|
+
if (!context) {
|
|
775
|
+
return { ok: false, error: { name: "Error", message: `Context ${contextId2} not found` } };
|
|
776
|
+
}
|
|
777
|
+
await context.close();
|
|
778
|
+
registry.contexts.delete(contextId2);
|
|
779
|
+
for (const [pid, p] of registry.pages) {
|
|
780
|
+
if (p.context() === context) {
|
|
781
|
+
registry.pages.delete(pid);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return { ok: true };
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
const pageId = op.pageId ?? "page_0";
|
|
788
|
+
const targetPage = registry.pages.get(pageId);
|
|
789
|
+
if (!targetPage) {
|
|
790
|
+
return { ok: false, error: { name: "Error", message: `Page ${pageId} not found` } };
|
|
791
|
+
}
|
|
792
|
+
const contextId = op.contextId ?? "ctx_0";
|
|
793
|
+
const targetContext = registry.contexts.get(contextId);
|
|
305
794
|
switch (op.type) {
|
|
306
795
|
case "goto": {
|
|
307
796
|
const [url, waitUntil] = op.args;
|
|
308
|
-
|
|
309
|
-
await page.goto(targetUrl, {
|
|
797
|
+
await targetPage.goto(url, {
|
|
310
798
|
timeout,
|
|
311
799
|
waitUntil: waitUntil ?? "load"
|
|
312
800
|
});
|
|
313
801
|
return { ok: true };
|
|
314
802
|
}
|
|
315
803
|
case "reload":
|
|
316
|
-
await
|
|
804
|
+
await targetPage.reload({ timeout });
|
|
317
805
|
return { ok: true };
|
|
318
806
|
case "url":
|
|
319
|
-
return { ok: true, value:
|
|
807
|
+
return { ok: true, value: targetPage.url() };
|
|
320
808
|
case "title":
|
|
321
|
-
return { ok: true, value: await
|
|
809
|
+
return { ok: true, value: await targetPage.title() };
|
|
322
810
|
case "content":
|
|
323
|
-
return { ok: true, value: await
|
|
811
|
+
return { ok: true, value: await targetPage.content() };
|
|
324
812
|
case "waitForSelector": {
|
|
325
813
|
const [selector, optionsJson] = op.args;
|
|
326
814
|
const opts = optionsJson ? JSON.parse(optionsJson) : {};
|
|
327
|
-
await
|
|
815
|
+
await targetPage.waitForSelector(selector, { timeout, ...opts });
|
|
328
816
|
return { ok: true };
|
|
329
817
|
}
|
|
330
818
|
case "waitForTimeout": {
|
|
331
819
|
const [ms] = op.args;
|
|
332
|
-
await
|
|
820
|
+
await targetPage.waitForTimeout(ms);
|
|
333
821
|
return { ok: true };
|
|
334
822
|
}
|
|
335
823
|
case "waitForLoadState": {
|
|
336
824
|
const [state] = op.args;
|
|
337
|
-
await
|
|
825
|
+
await targetPage.waitForLoadState(state ?? "load", { timeout });
|
|
338
826
|
return { ok: true };
|
|
339
827
|
}
|
|
340
828
|
case "evaluate": {
|
|
341
829
|
const [script, arg] = op.args;
|
|
342
830
|
if (op.args.length > 1) {
|
|
343
831
|
const fn = new Function("return (" + script + ")")();
|
|
344
|
-
const result2 = await
|
|
832
|
+
const result2 = await targetPage.evaluate(fn, arg);
|
|
345
833
|
return { ok: true, value: result2 };
|
|
346
834
|
}
|
|
347
|
-
const result = await
|
|
835
|
+
const result = await targetPage.evaluate(script);
|
|
348
836
|
return { ok: true, value: result };
|
|
349
837
|
}
|
|
350
838
|
case "locatorAction": {
|
|
351
839
|
const [selectorType, selectorValue, roleOptions, action, actionArg] = op.args;
|
|
352
|
-
const locator = getLocator(
|
|
353
|
-
const result = await executeLocatorAction(locator, action, actionArg, timeout);
|
|
840
|
+
const locator = getLocator(targetPage, selectorType, selectorValue, roleOptions);
|
|
841
|
+
const result = await executeLocatorAction(locator, action, actionArg, timeout, fileIO);
|
|
354
842
|
return { ok: true, value: result };
|
|
355
843
|
}
|
|
356
844
|
case "expectLocator": {
|
|
357
845
|
const [selectorType, selectorValue, roleOptions, matcher, expected, negated, customTimeout] = op.args;
|
|
358
|
-
const locator = getLocator(
|
|
846
|
+
const locator = getLocator(targetPage, selectorType, selectorValue, roleOptions);
|
|
359
847
|
const effectiveTimeout = customTimeout ?? timeout;
|
|
360
848
|
await executeExpectAssertion(locator, matcher, expected, negated ?? false, effectiveTimeout);
|
|
361
849
|
return { ok: true };
|
|
362
850
|
}
|
|
851
|
+
case "expectPage": {
|
|
852
|
+
const [matcher, expected, negated, customTimeout] = op.args;
|
|
853
|
+
const effectiveTimeout = customTimeout ?? timeout;
|
|
854
|
+
await executePageExpectAssertion(targetPage, matcher, expected, negated ?? false, effectiveTimeout);
|
|
855
|
+
return { ok: true };
|
|
856
|
+
}
|
|
363
857
|
case "request": {
|
|
364
858
|
const [url, method, data, headers] = op.args;
|
|
365
|
-
const targetUrl = baseUrl && !url.startsWith("http") ? `${baseUrl}${url}` : url;
|
|
366
859
|
const requestOptions = {
|
|
367
860
|
timeout
|
|
368
861
|
};
|
|
@@ -372,7 +865,7 @@ function createPlaywrightHandler(page, options) {
|
|
|
372
865
|
if (data !== undefined && data !== null) {
|
|
373
866
|
requestOptions.data = data;
|
|
374
867
|
}
|
|
375
|
-
const response = await
|
|
868
|
+
const response = await targetPage.request.fetch(url, {
|
|
376
869
|
method,
|
|
377
870
|
...requestOptions
|
|
378
871
|
});
|
|
@@ -395,7 +888,7 @@ function createPlaywrightHandler(page, options) {
|
|
|
395
888
|
}
|
|
396
889
|
case "goBack": {
|
|
397
890
|
const [waitUntil] = op.args;
|
|
398
|
-
await
|
|
891
|
+
await targetPage.goBack({
|
|
399
892
|
timeout,
|
|
400
893
|
waitUntil: waitUntil ?? "load"
|
|
401
894
|
});
|
|
@@ -403,22 +896,156 @@ function createPlaywrightHandler(page, options) {
|
|
|
403
896
|
}
|
|
404
897
|
case "goForward": {
|
|
405
898
|
const [waitUntil] = op.args;
|
|
406
|
-
await
|
|
899
|
+
await targetPage.goForward({
|
|
407
900
|
timeout,
|
|
408
901
|
waitUntil: waitUntil ?? "load"
|
|
409
902
|
});
|
|
410
903
|
return { ok: true };
|
|
411
904
|
}
|
|
412
905
|
case "waitForURL": {
|
|
413
|
-
const [
|
|
414
|
-
|
|
906
|
+
const [urlArg, customTimeout, waitUntil] = op.args;
|
|
907
|
+
const url = urlArg && typeof urlArg === "object" && "$regex" in urlArg ? new RegExp(urlArg.$regex, urlArg.$flags) : urlArg;
|
|
908
|
+
await targetPage.waitForURL(url, {
|
|
415
909
|
timeout: customTimeout ?? timeout,
|
|
416
910
|
waitUntil: waitUntil ?? undefined
|
|
417
911
|
});
|
|
418
912
|
return { ok: true };
|
|
419
913
|
}
|
|
420
914
|
case "clearCookies": {
|
|
421
|
-
|
|
915
|
+
const ctx = targetContext ?? targetPage.context();
|
|
916
|
+
await ctx.clearCookies();
|
|
917
|
+
return { ok: true };
|
|
918
|
+
}
|
|
919
|
+
case "screenshot": {
|
|
920
|
+
const [screenshotOptions] = op.args;
|
|
921
|
+
const buffer = await targetPage.screenshot({
|
|
922
|
+
type: screenshotOptions?.type,
|
|
923
|
+
quality: screenshotOptions?.quality,
|
|
924
|
+
fullPage: screenshotOptions?.fullPage,
|
|
925
|
+
clip: screenshotOptions?.clip
|
|
926
|
+
});
|
|
927
|
+
if (screenshotOptions?.path) {
|
|
928
|
+
if (!fileIO.writeFile) {
|
|
929
|
+
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.");
|
|
930
|
+
}
|
|
931
|
+
await fileIO.writeFile(screenshotOptions.path, buffer);
|
|
932
|
+
}
|
|
933
|
+
return { ok: true, value: buffer.toString("base64") };
|
|
934
|
+
}
|
|
935
|
+
case "setViewportSize": {
|
|
936
|
+
const [size] = op.args;
|
|
937
|
+
await targetPage.setViewportSize(size);
|
|
938
|
+
return { ok: true };
|
|
939
|
+
}
|
|
940
|
+
case "viewportSize": {
|
|
941
|
+
return { ok: true, value: targetPage.viewportSize() };
|
|
942
|
+
}
|
|
943
|
+
case "keyboardType": {
|
|
944
|
+
const [text, typeOptions] = op.args;
|
|
945
|
+
await targetPage.keyboard.type(text, typeOptions);
|
|
946
|
+
return { ok: true };
|
|
947
|
+
}
|
|
948
|
+
case "keyboardPress": {
|
|
949
|
+
const [key, pressOptions] = op.args;
|
|
950
|
+
await targetPage.keyboard.press(key, pressOptions);
|
|
951
|
+
return { ok: true };
|
|
952
|
+
}
|
|
953
|
+
case "keyboardDown": {
|
|
954
|
+
const [key] = op.args;
|
|
955
|
+
await targetPage.keyboard.down(key);
|
|
956
|
+
return { ok: true };
|
|
957
|
+
}
|
|
958
|
+
case "keyboardUp": {
|
|
959
|
+
const [key] = op.args;
|
|
960
|
+
await targetPage.keyboard.up(key);
|
|
961
|
+
return { ok: true };
|
|
962
|
+
}
|
|
963
|
+
case "keyboardInsertText": {
|
|
964
|
+
const [text] = op.args;
|
|
965
|
+
await targetPage.keyboard.insertText(text);
|
|
966
|
+
return { ok: true };
|
|
967
|
+
}
|
|
968
|
+
case "mouseMove": {
|
|
969
|
+
const [x, y, moveOptions] = op.args;
|
|
970
|
+
await targetPage.mouse.move(x, y, moveOptions);
|
|
971
|
+
return { ok: true };
|
|
972
|
+
}
|
|
973
|
+
case "mouseClick": {
|
|
974
|
+
const [x, y, clickOptions] = op.args;
|
|
975
|
+
await targetPage.mouse.click(x, y, clickOptions);
|
|
976
|
+
return { ok: true };
|
|
977
|
+
}
|
|
978
|
+
case "mouseDown": {
|
|
979
|
+
const [downOptions] = op.args;
|
|
980
|
+
await targetPage.mouse.down(downOptions);
|
|
981
|
+
return { ok: true };
|
|
982
|
+
}
|
|
983
|
+
case "mouseUp": {
|
|
984
|
+
const [upOptions] = op.args;
|
|
985
|
+
await targetPage.mouse.up(upOptions);
|
|
986
|
+
return { ok: true };
|
|
987
|
+
}
|
|
988
|
+
case "mouseWheel": {
|
|
989
|
+
const [deltaX, deltaY] = op.args;
|
|
990
|
+
await targetPage.mouse.wheel(deltaX, deltaY);
|
|
991
|
+
return { ok: true };
|
|
992
|
+
}
|
|
993
|
+
case "frames": {
|
|
994
|
+
const frames = targetPage.frames();
|
|
995
|
+
return { ok: true, value: frames.map((f) => ({ name: f.name(), url: f.url() })) };
|
|
996
|
+
}
|
|
997
|
+
case "mainFrame": {
|
|
998
|
+
const mainFrame = targetPage.mainFrame();
|
|
999
|
+
return { ok: true, value: { name: mainFrame.name(), url: mainFrame.url() } };
|
|
1000
|
+
}
|
|
1001
|
+
case "bringToFront": {
|
|
1002
|
+
await targetPage.bringToFront();
|
|
1003
|
+
return { ok: true };
|
|
1004
|
+
}
|
|
1005
|
+
case "close": {
|
|
1006
|
+
await targetPage.close();
|
|
1007
|
+
registry.pages.delete(pageId);
|
|
1008
|
+
return { ok: true };
|
|
1009
|
+
}
|
|
1010
|
+
case "isClosed": {
|
|
1011
|
+
return { ok: true, value: targetPage.isClosed() };
|
|
1012
|
+
}
|
|
1013
|
+
case "pdf": {
|
|
1014
|
+
const [pdfOptions] = op.args;
|
|
1015
|
+
const { path: pdfPath, ...restPdfOptions } = pdfOptions ?? {};
|
|
1016
|
+
const buffer = await targetPage.pdf(restPdfOptions);
|
|
1017
|
+
if (pdfPath) {
|
|
1018
|
+
if (!fileIO.writeFile) {
|
|
1019
|
+
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.");
|
|
1020
|
+
}
|
|
1021
|
+
await fileIO.writeFile(pdfPath, buffer);
|
|
1022
|
+
}
|
|
1023
|
+
return { ok: true, value: buffer.toString("base64") };
|
|
1024
|
+
}
|
|
1025
|
+
case "emulateMedia": {
|
|
1026
|
+
const [mediaOptions] = op.args;
|
|
1027
|
+
await targetPage.emulateMedia(mediaOptions);
|
|
1028
|
+
return { ok: true };
|
|
1029
|
+
}
|
|
1030
|
+
case "addCookies": {
|
|
1031
|
+
const [cookies] = op.args;
|
|
1032
|
+
const ctx = targetContext ?? targetPage.context();
|
|
1033
|
+
await ctx.addCookies(cookies);
|
|
1034
|
+
return { ok: true };
|
|
1035
|
+
}
|
|
1036
|
+
case "cookies": {
|
|
1037
|
+
const [urls] = op.args;
|
|
1038
|
+
const ctx = targetContext ?? targetPage.context();
|
|
1039
|
+
const cookies = await ctx.cookies(urls);
|
|
1040
|
+
return { ok: true, value: cookies };
|
|
1041
|
+
}
|
|
1042
|
+
case "setExtraHTTPHeaders": {
|
|
1043
|
+
const [headers] = op.args;
|
|
1044
|
+
await targetPage.setExtraHTTPHeaders(headers);
|
|
1045
|
+
return { ok: true };
|
|
1046
|
+
}
|
|
1047
|
+
case "pause": {
|
|
1048
|
+
await targetPage.pause();
|
|
422
1049
|
return { ok: true };
|
|
423
1050
|
}
|
|
424
1051
|
default:
|
|
@@ -430,12 +1057,27 @@ function createPlaywrightHandler(page, options) {
|
|
|
430
1057
|
}
|
|
431
1058
|
};
|
|
432
1059
|
}
|
|
1060
|
+
function defaultPlaywrightHandler(page, options) {
|
|
1061
|
+
const handler = createPlaywrightHandler(page, options);
|
|
1062
|
+
handler[DEFAULT_PLAYWRIGHT_HANDLER_META] = { page, options };
|
|
1063
|
+
return handler;
|
|
1064
|
+
}
|
|
1065
|
+
function getDefaultPlaywrightHandlerMetadata(handler) {
|
|
1066
|
+
return handler[DEFAULT_PLAYWRIGHT_HANDLER_META];
|
|
1067
|
+
}
|
|
433
1068
|
async function setupPlaywright(context, options) {
|
|
434
1069
|
const timeout = options.timeout ?? 30000;
|
|
435
|
-
const
|
|
436
|
-
const page = "page" in options ? options.page : undefined;
|
|
1070
|
+
const explicitPage = "page" in options ? options.page : undefined;
|
|
437
1071
|
const handler = "handler" in options ? options.handler : undefined;
|
|
438
|
-
const
|
|
1072
|
+
const handlerMetadata = handler ? getDefaultPlaywrightHandlerMetadata(handler) : undefined;
|
|
1073
|
+
const page = explicitPage ?? handlerMetadata?.page;
|
|
1074
|
+
const createPage = "createPage" in options ? options.createPage : undefined;
|
|
1075
|
+
const createContext = "createContext" in options ? options.createContext : undefined;
|
|
1076
|
+
const effectiveHandler = handler ?? (page ? createPlaywrightHandler(page, {
|
|
1077
|
+
timeout,
|
|
1078
|
+
createPage,
|
|
1079
|
+
createContext
|
|
1080
|
+
}) : undefined);
|
|
439
1081
|
if (!effectiveHandler) {
|
|
440
1082
|
throw new Error("Either page or handler must be provided to setupPlaywright");
|
|
441
1083
|
}
|
|
@@ -522,8 +1164,8 @@ async function setupPlaywright(context, options) {
|
|
|
522
1164
|
}));
|
|
523
1165
|
context.evalSync(`
|
|
524
1166
|
(function() {
|
|
525
|
-
globalThis.__pw_invoke = async function(type, args) {
|
|
526
|
-
const op = JSON.stringify({ type, args });
|
|
1167
|
+
globalThis.__pw_invoke = async function(type, args, options) {
|
|
1168
|
+
const op = JSON.stringify({ type, args, pageId: options?.pageId, contextId: options?.contextId });
|
|
527
1169
|
const resultJson = __Playwright_handler_ref.applySyncPromise(undefined, [op]);
|
|
528
1170
|
const result = JSON.parse(resultJson);
|
|
529
1171
|
if (result.ok) {
|
|
@@ -538,209 +1180,362 @@ async function setupPlaywright(context, options) {
|
|
|
538
1180
|
`);
|
|
539
1181
|
context.evalSync(`
|
|
540
1182
|
(function() {
|
|
541
|
-
|
|
542
|
-
|
|
1183
|
+
// IsolatePage class - represents a page with a specific pageId
|
|
1184
|
+
class IsolatePage {
|
|
1185
|
+
#pageId; #contextId; #currentUrl = '';
|
|
1186
|
+
constructor(pageId, contextId) {
|
|
1187
|
+
this.#pageId = pageId;
|
|
1188
|
+
this.#contextId = contextId;
|
|
1189
|
+
}
|
|
1190
|
+
get __isPage() { return true; }
|
|
1191
|
+
get __pageId() { return this.#pageId; }
|
|
1192
|
+
get __contextId() { return this.#contextId; }
|
|
1193
|
+
|
|
543
1194
|
async goto(url, options) {
|
|
544
|
-
|
|
545
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
},
|
|
1195
|
+
await __pw_invoke("goto", [url, options?.waitUntil || null], { pageId: this.#pageId });
|
|
1196
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1197
|
+
this.#currentUrl = resolvedUrl || url;
|
|
1198
|
+
}
|
|
549
1199
|
async reload() {
|
|
550
|
-
|
|
551
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
552
|
-
if (resolvedUrl)
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
},
|
|
558
|
-
async title() {
|
|
559
|
-
return __pw_invoke("title", []);
|
|
560
|
-
},
|
|
561
|
-
async content() {
|
|
562
|
-
return __pw_invoke("content", []);
|
|
563
|
-
},
|
|
1200
|
+
await __pw_invoke("reload", [], { pageId: this.#pageId });
|
|
1201
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1202
|
+
if (resolvedUrl) this.#currentUrl = resolvedUrl;
|
|
1203
|
+
}
|
|
1204
|
+
url() { return this.#currentUrl; }
|
|
1205
|
+
async title() { return __pw_invoke("title", [], { pageId: this.#pageId }); }
|
|
1206
|
+
async content() { return __pw_invoke("content", [], { pageId: this.#pageId }); }
|
|
564
1207
|
async waitForSelector(selector, options) {
|
|
565
|
-
return __pw_invoke("waitForSelector", [selector, options ? JSON.stringify(options) : null]);
|
|
566
|
-
}
|
|
567
|
-
async waitForTimeout(ms) {
|
|
568
|
-
|
|
569
|
-
},
|
|
570
|
-
async waitForLoadState(state) {
|
|
571
|
-
return __pw_invoke("waitForLoadState", [state || null]);
|
|
572
|
-
},
|
|
1208
|
+
return __pw_invoke("waitForSelector", [selector, options ? JSON.stringify(options) : null], { pageId: this.#pageId });
|
|
1209
|
+
}
|
|
1210
|
+
async waitForTimeout(ms) { return __pw_invoke("waitForTimeout", [ms], { pageId: this.#pageId }); }
|
|
1211
|
+
async waitForLoadState(state) { return __pw_invoke("waitForLoadState", [state || null], { pageId: this.#pageId }); }
|
|
573
1212
|
async evaluate(script, arg) {
|
|
574
1213
|
const hasArg = arguments.length > 1;
|
|
575
1214
|
if (hasArg) {
|
|
576
1215
|
const serialized = typeof script === "function" ? script.toString() : script;
|
|
577
|
-
return __pw_invoke("evaluate", [serialized, arg]);
|
|
1216
|
+
return __pw_invoke("evaluate", [serialized, arg], { pageId: this.#pageId });
|
|
578
1217
|
}
|
|
579
1218
|
const serialized = typeof script === "function" ? "(" + script.toString() + ")()" : script;
|
|
580
|
-
return __pw_invoke("evaluate", [serialized]);
|
|
581
|
-
}
|
|
582
|
-
locator(selector) { return new Locator("css", selector, null); }
|
|
1219
|
+
return __pw_invoke("evaluate", [serialized], { pageId: this.#pageId });
|
|
1220
|
+
}
|
|
1221
|
+
locator(selector) { return new Locator("css", selector, null, this.#pageId); }
|
|
583
1222
|
getByRole(role, options) {
|
|
584
1223
|
if (options) {
|
|
585
1224
|
const serialized = { ...options };
|
|
586
|
-
|
|
587
|
-
|
|
1225
|
+
const name = options.name;
|
|
1226
|
+
if (name && typeof name === 'object' && typeof name.source === 'string' && typeof name.flags === 'string') {
|
|
1227
|
+
serialized.name = { $regex: name.source, $flags: name.flags };
|
|
588
1228
|
}
|
|
589
|
-
return new Locator("role", role, JSON.stringify(serialized));
|
|
1229
|
+
return new Locator("role", role, JSON.stringify(serialized), this.#pageId);
|
|
590
1230
|
}
|
|
591
|
-
return new Locator("role", role, null);
|
|
592
|
-
}
|
|
593
|
-
getByText(text) { return new Locator("text", text, null); }
|
|
594
|
-
getByLabel(label) { return new Locator("label", label, null); }
|
|
595
|
-
getByPlaceholder(p) { return new Locator("placeholder", p, null); }
|
|
596
|
-
getByTestId(id) { return new Locator("testId", id, null); }
|
|
1231
|
+
return new Locator("role", role, null, this.#pageId);
|
|
1232
|
+
}
|
|
1233
|
+
getByText(text) { return new Locator("text", text, null, this.#pageId); }
|
|
1234
|
+
getByLabel(label) { return new Locator("label", label, null, this.#pageId); }
|
|
1235
|
+
getByPlaceholder(p) { return new Locator("placeholder", p, null, this.#pageId); }
|
|
1236
|
+
getByTestId(id) { return new Locator("testId", id, null, this.#pageId); }
|
|
1237
|
+
getByAltText(alt) { return new Locator("altText", alt, null, this.#pageId); }
|
|
1238
|
+
getByTitle(title) { return new Locator("title", title, null, this.#pageId); }
|
|
1239
|
+
frameLocator(selector) {
|
|
1240
|
+
const pageId = this.#pageId;
|
|
1241
|
+
return {
|
|
1242
|
+
locator(innerSelector) { return new Locator("frame", JSON.stringify([["css", selector, null], ["css", innerSelector, null]]), null, pageId); },
|
|
1243
|
+
getByRole(role, options) { return new Locator("frame", JSON.stringify([["css", selector, null], ["role", role, options ? JSON.stringify(options) : null]]), null, pageId); },
|
|
1244
|
+
getByText(text) { return new Locator("frame", JSON.stringify([["css", selector, null], ["text", text, null]]), null, pageId); },
|
|
1245
|
+
getByLabel(label) { return new Locator("frame", JSON.stringify([["css", selector, null], ["label", label, null]]), null, pageId); },
|
|
1246
|
+
getByPlaceholder(placeholder) { return new Locator("frame", JSON.stringify([["css", selector, null], ["placeholder", placeholder, null]]), null, pageId); },
|
|
1247
|
+
getByTestId(testId) { return new Locator("frame", JSON.stringify([["css", selector, null], ["testId", testId, null]]), null, pageId); },
|
|
1248
|
+
getByAltText(alt) { return new Locator("frame", JSON.stringify([["css", selector, null], ["altText", alt, null]]), null, pageId); },
|
|
1249
|
+
getByTitle(title) { return new Locator("frame", JSON.stringify([["css", selector, null], ["title", title, null]]), null, pageId); },
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
597
1252
|
async goBack(options) {
|
|
598
|
-
await __pw_invoke("goBack", [options?.waitUntil || null]);
|
|
599
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
600
|
-
if (resolvedUrl)
|
|
601
|
-
}
|
|
1253
|
+
await __pw_invoke("goBack", [options?.waitUntil || null], { pageId: this.#pageId });
|
|
1254
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1255
|
+
if (resolvedUrl) this.#currentUrl = resolvedUrl;
|
|
1256
|
+
}
|
|
602
1257
|
async goForward(options) {
|
|
603
|
-
await __pw_invoke("goForward", [options?.waitUntil || null]);
|
|
604
|
-
const resolvedUrl = await __pw_invoke("url", []);
|
|
605
|
-
if (resolvedUrl)
|
|
606
|
-
}
|
|
1258
|
+
await __pw_invoke("goForward", [options?.waitUntil || null], { pageId: this.#pageId });
|
|
1259
|
+
const resolvedUrl = await __pw_invoke("url", [], { pageId: this.#pageId });
|
|
1260
|
+
if (resolvedUrl) this.#currentUrl = resolvedUrl;
|
|
1261
|
+
}
|
|
607
1262
|
async waitForURL(url, options) {
|
|
608
|
-
|
|
609
|
-
|
|
1263
|
+
let serializedUrl = url;
|
|
1264
|
+
if (url && typeof url === 'object' && typeof url.source === 'string' && typeof url.flags === 'string') {
|
|
1265
|
+
serializedUrl = { $regex: url.source, $flags: url.flags };
|
|
1266
|
+
}
|
|
1267
|
+
return __pw_invoke("waitForURL", [serializedUrl, options?.timeout || null, options?.waitUntil || null], { pageId: this.#pageId });
|
|
1268
|
+
}
|
|
610
1269
|
context() {
|
|
1270
|
+
const contextId = this.#contextId;
|
|
1271
|
+
return new IsolateContext(contextId);
|
|
1272
|
+
}
|
|
1273
|
+
async click(selector) { return this.locator(selector).click(); }
|
|
1274
|
+
async fill(selector, value) { return this.locator(selector).fill(value); }
|
|
1275
|
+
async textContent(selector) { return this.locator(selector).textContent(); }
|
|
1276
|
+
async innerText(selector) { return this.locator(selector).innerText(); }
|
|
1277
|
+
async innerHTML(selector) { return this.locator(selector).innerHTML(); }
|
|
1278
|
+
async getAttribute(selector, name) { return this.locator(selector).getAttribute(name); }
|
|
1279
|
+
async inputValue(selector) { return this.locator(selector).inputValue(); }
|
|
1280
|
+
async isVisible(selector) { return this.locator(selector).isVisible(); }
|
|
1281
|
+
async isEnabled(selector) { return this.locator(selector).isEnabled(); }
|
|
1282
|
+
async isChecked(selector) { return this.locator(selector).isChecked(); }
|
|
1283
|
+
async isHidden(selector) { return this.locator(selector).isHidden(); }
|
|
1284
|
+
async isDisabled(selector) { return this.locator(selector).isDisabled(); }
|
|
1285
|
+
async screenshot(options) { return __pw_invoke("screenshot", [options || {}], { pageId: this.#pageId }); }
|
|
1286
|
+
async setViewportSize(size) { return __pw_invoke("setViewportSize", [size], { pageId: this.#pageId }); }
|
|
1287
|
+
async viewportSize() { return __pw_invoke("viewportSize", [], { pageId: this.#pageId }); }
|
|
1288
|
+
async emulateMedia(options) { return __pw_invoke("emulateMedia", [options], { pageId: this.#pageId }); }
|
|
1289
|
+
async setExtraHTTPHeaders(headers) { return __pw_invoke("setExtraHTTPHeaders", [headers], { pageId: this.#pageId }); }
|
|
1290
|
+
async bringToFront() { return __pw_invoke("bringToFront", [], { pageId: this.#pageId }); }
|
|
1291
|
+
async close() { return __pw_invoke("close", [], { pageId: this.#pageId }); }
|
|
1292
|
+
async isClosed() { return __pw_invoke("isClosed", [], { pageId: this.#pageId }); }
|
|
1293
|
+
async pdf(options) { return __pw_invoke("pdf", [options || {}], { pageId: this.#pageId }); }
|
|
1294
|
+
async pause() { return __pw_invoke("pause", [], { pageId: this.#pageId }); }
|
|
1295
|
+
async frames() { return __pw_invoke("frames", [], { pageId: this.#pageId }); }
|
|
1296
|
+
async mainFrame() { return __pw_invoke("mainFrame", [], { pageId: this.#pageId }); }
|
|
1297
|
+
get keyboard() {
|
|
1298
|
+
const pageId = this.#pageId;
|
|
611
1299
|
return {
|
|
612
|
-
async
|
|
613
|
-
|
|
614
|
-
}
|
|
1300
|
+
async type(text, options) { return __pw_invoke("keyboardType", [text, options], { pageId }); },
|
|
1301
|
+
async press(key, options) { return __pw_invoke("keyboardPress", [key, options], { pageId }); },
|
|
1302
|
+
async down(key) { return __pw_invoke("keyboardDown", [key], { pageId }); },
|
|
1303
|
+
async up(key) { return __pw_invoke("keyboardUp", [key], { pageId }); },
|
|
1304
|
+
async insertText(text) { return __pw_invoke("keyboardInsertText", [text], { pageId }); }
|
|
615
1305
|
};
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return this.fetch(url, { ...options, method: "
|
|
642
|
-
|
|
643
|
-
|
|
1306
|
+
}
|
|
1307
|
+
get mouse() {
|
|
1308
|
+
const pageId = this.#pageId;
|
|
1309
|
+
return {
|
|
1310
|
+
async move(x, y, options) { return __pw_invoke("mouseMove", [x, y, options], { pageId }); },
|
|
1311
|
+
async click(x, y, options) { return __pw_invoke("mouseClick", [x, y, options], { pageId }); },
|
|
1312
|
+
async down(options) { return __pw_invoke("mouseDown", [options], { pageId }); },
|
|
1313
|
+
async up(options) { return __pw_invoke("mouseUp", [options], { pageId }); },
|
|
1314
|
+
async wheel(deltaX, deltaY) { return __pw_invoke("mouseWheel", [deltaX, deltaY], { pageId }); }
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
get request() {
|
|
1318
|
+
const pageId = this.#pageId;
|
|
1319
|
+
return {
|
|
1320
|
+
async fetch(url, options) {
|
|
1321
|
+
const result = await __pw_invoke("request", [url, options?.method || "GET", options?.data, options?.headers], { pageId });
|
|
1322
|
+
return {
|
|
1323
|
+
status: () => result.status,
|
|
1324
|
+
ok: () => result.ok,
|
|
1325
|
+
headers: () => result.headers,
|
|
1326
|
+
json: async () => result.json,
|
|
1327
|
+
text: async () => result.text,
|
|
1328
|
+
body: async () => result.body,
|
|
1329
|
+
};
|
|
1330
|
+
},
|
|
1331
|
+
async get(url, options) { return this.fetch(url, { ...options, method: "GET" }); },
|
|
1332
|
+
async post(url, options) { return this.fetch(url, { ...options, method: "POST" }); },
|
|
1333
|
+
async put(url, options) { return this.fetch(url, { ...options, method: "PUT" }); },
|
|
1334
|
+
async delete(url, options) { return this.fetch(url, { ...options, method: "DELETE" }); },
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
globalThis.IsolatePage = IsolatePage;
|
|
1339
|
+
|
|
1340
|
+
// IsolateContext class - represents a browser context with a specific contextId
|
|
1341
|
+
class IsolateContext {
|
|
1342
|
+
#contextId;
|
|
1343
|
+
constructor(contextId) { this.#contextId = contextId; }
|
|
1344
|
+
get __contextId() { return this.#contextId; }
|
|
1345
|
+
|
|
1346
|
+
async newPage() {
|
|
1347
|
+
const result = await __pw_invoke("newPage", [], { contextId: this.#contextId });
|
|
1348
|
+
return new IsolatePage(result.pageId, this.#contextId);
|
|
1349
|
+
}
|
|
1350
|
+
async close() { return __pw_invoke("closeContext", [], { contextId: this.#contextId }); }
|
|
1351
|
+
async clearCookies() { return __pw_invoke("clearCookies", [], { contextId: this.#contextId }); }
|
|
1352
|
+
async addCookies(cookies) { return __pw_invoke("addCookies", [cookies], { contextId: this.#contextId }); }
|
|
1353
|
+
async cookies(urls) { return __pw_invoke("cookies", [urls], { contextId: this.#contextId }); }
|
|
1354
|
+
}
|
|
1355
|
+
globalThis.IsolateContext = IsolateContext;
|
|
1356
|
+
|
|
1357
|
+
// browser global - for creating new contexts
|
|
1358
|
+
globalThis.browser = {
|
|
1359
|
+
async newContext(options) {
|
|
1360
|
+
const result = await __pw_invoke("newContext", [options || null]);
|
|
1361
|
+
return new IsolateContext(result.contextId);
|
|
1362
|
+
}
|
|
644
1363
|
};
|
|
1364
|
+
|
|
1365
|
+
// context global - represents the default context
|
|
1366
|
+
globalThis.context = new IsolateContext("ctx_0");
|
|
1367
|
+
|
|
1368
|
+
// page global - represents the default page
|
|
1369
|
+
globalThis.page = new IsolatePage("page_0", "ctx_0");
|
|
645
1370
|
})();
|
|
646
1371
|
`);
|
|
647
1372
|
context.evalSync(`
|
|
648
1373
|
(function() {
|
|
1374
|
+
// Helper to serialize options including RegExp
|
|
1375
|
+
function serializeOptions(options) {
|
|
1376
|
+
if (!options) return null;
|
|
1377
|
+
const serialized = { ...options };
|
|
1378
|
+
if (options.name && typeof options.name === 'object' && typeof options.name.source === 'string' && typeof options.name.flags === 'string') {
|
|
1379
|
+
serialized.name = { $regex: options.name.source, $flags: options.name.flags };
|
|
1380
|
+
}
|
|
1381
|
+
return JSON.stringify(serialized);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
649
1384
|
class Locator {
|
|
650
|
-
#type; #value; #options;
|
|
651
|
-
constructor(type, value, options) {
|
|
1385
|
+
#type; #value; #options; #pageId;
|
|
1386
|
+
constructor(type, value, options, pageId) {
|
|
652
1387
|
this.#type = type;
|
|
653
1388
|
this.#value = value;
|
|
654
1389
|
this.#options = options;
|
|
1390
|
+
this.#pageId = pageId || "page_0";
|
|
655
1391
|
}
|
|
656
1392
|
|
|
657
1393
|
_getInfo() { return [this.#type, this.#value, this.#options]; }
|
|
1394
|
+
_getPageId() { return this.#pageId; }
|
|
1395
|
+
|
|
1396
|
+
// Helper to create a chained locator
|
|
1397
|
+
_chain(childType, childValue, childOptions) {
|
|
1398
|
+
const parentInfo = this._getInfo();
|
|
1399
|
+
const childInfo = [childType, childValue, childOptions];
|
|
1400
|
+
return new Locator("chained", JSON.stringify([parentInfo, childInfo]), null, this.#pageId);
|
|
1401
|
+
}
|
|
658
1402
|
|
|
659
1403
|
async click() {
|
|
660
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "click", null]);
|
|
1404
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "click", null], { pageId: this.#pageId });
|
|
661
1405
|
}
|
|
662
1406
|
async dblclick() {
|
|
663
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "dblclick", null]);
|
|
1407
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "dblclick", null], { pageId: this.#pageId });
|
|
664
1408
|
}
|
|
665
1409
|
async fill(text) {
|
|
666
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "fill", text]);
|
|
1410
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "fill", text], { pageId: this.#pageId });
|
|
667
1411
|
}
|
|
668
1412
|
async type(text) {
|
|
669
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "type", text]);
|
|
1413
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "type", text], { pageId: this.#pageId });
|
|
670
1414
|
}
|
|
671
1415
|
async check() {
|
|
672
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "check", null]);
|
|
1416
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "check", null], { pageId: this.#pageId });
|
|
673
1417
|
}
|
|
674
1418
|
async uncheck() {
|
|
675
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "uncheck", null]);
|
|
1419
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "uncheck", null], { pageId: this.#pageId });
|
|
676
1420
|
}
|
|
677
1421
|
async selectOption(value) {
|
|
678
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "selectOption", value]);
|
|
1422
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "selectOption", value], { pageId: this.#pageId });
|
|
679
1423
|
}
|
|
680
1424
|
async clear() {
|
|
681
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "clear", null]);
|
|
1425
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "clear", null], { pageId: this.#pageId });
|
|
682
1426
|
}
|
|
683
1427
|
async press(key) {
|
|
684
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "press", key]);
|
|
1428
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "press", key], { pageId: this.#pageId });
|
|
685
1429
|
}
|
|
686
1430
|
async hover() {
|
|
687
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "hover", null]);
|
|
1431
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "hover", null], { pageId: this.#pageId });
|
|
688
1432
|
}
|
|
689
1433
|
async focus() {
|
|
690
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "focus", null]);
|
|
1434
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "focus", null], { pageId: this.#pageId });
|
|
691
1435
|
}
|
|
692
1436
|
async textContent() {
|
|
693
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getText", null]);
|
|
1437
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getText", null], { pageId: this.#pageId });
|
|
694
1438
|
}
|
|
695
1439
|
async inputValue() {
|
|
696
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getValue", null]);
|
|
1440
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getValue", null], { pageId: this.#pageId });
|
|
697
1441
|
}
|
|
698
1442
|
async isVisible() {
|
|
699
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isVisible", null]);
|
|
1443
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isVisible", null], { pageId: this.#pageId });
|
|
700
1444
|
}
|
|
701
1445
|
async isEnabled() {
|
|
702
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isEnabled", null]);
|
|
1446
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isEnabled", null], { pageId: this.#pageId });
|
|
703
1447
|
}
|
|
704
1448
|
async isChecked() {
|
|
705
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isChecked", null]);
|
|
1449
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isChecked", null], { pageId: this.#pageId });
|
|
706
1450
|
}
|
|
707
1451
|
async count() {
|
|
708
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "count", null]);
|
|
1452
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "count", null], { pageId: this.#pageId });
|
|
709
1453
|
}
|
|
710
1454
|
async getAttribute(name) {
|
|
711
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name]);
|
|
1455
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name], { pageId: this.#pageId });
|
|
712
1456
|
}
|
|
713
1457
|
async isDisabled() {
|
|
714
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null]);
|
|
1458
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null], { pageId: this.#pageId });
|
|
715
1459
|
}
|
|
716
1460
|
async isHidden() {
|
|
717
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null]);
|
|
1461
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null], { pageId: this.#pageId });
|
|
718
1462
|
}
|
|
719
1463
|
async innerHTML() {
|
|
720
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null]);
|
|
1464
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null], { pageId: this.#pageId });
|
|
721
1465
|
}
|
|
722
1466
|
async innerText() {
|
|
723
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null]);
|
|
1467
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null], { pageId: this.#pageId });
|
|
724
1468
|
}
|
|
725
1469
|
async allTextContents() {
|
|
726
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null]);
|
|
1470
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null], { pageId: this.#pageId });
|
|
727
1471
|
}
|
|
728
1472
|
async allInnerTexts() {
|
|
729
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null]);
|
|
1473
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null], { pageId: this.#pageId });
|
|
730
1474
|
}
|
|
731
1475
|
async waitFor(options) {
|
|
732
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}]);
|
|
1476
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}], { pageId: this.#pageId });
|
|
733
1477
|
}
|
|
734
1478
|
async boundingBox() {
|
|
735
|
-
return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null]);
|
|
1479
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null], { pageId: this.#pageId });
|
|
736
1480
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
1481
|
+
async setInputFiles(files) {
|
|
1482
|
+
// Serialize files - if they have buffers, convert to base64
|
|
1483
|
+
let serializedFiles = files;
|
|
1484
|
+
if (Array.isArray(files) && files.length > 0 && typeof files[0] === 'object' && files[0].buffer) {
|
|
1485
|
+
serializedFiles = files.map(f => ({
|
|
1486
|
+
name: f.name,
|
|
1487
|
+
mimeType: f.mimeType,
|
|
1488
|
+
buffer: typeof f.buffer === 'string' ? f.buffer : btoa(String.fromCharCode(...new Uint8Array(f.buffer)))
|
|
1489
|
+
}));
|
|
741
1490
|
}
|
|
742
|
-
|
|
743
|
-
|
|
1491
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "setInputFiles", serializedFiles], { pageId: this.#pageId });
|
|
1492
|
+
}
|
|
1493
|
+
async screenshot(options) {
|
|
1494
|
+
const base64 = await __pw_invoke("locatorAction", [...this._getInfo(), "screenshot", options || {}], { pageId: this.#pageId });
|
|
1495
|
+
return base64;
|
|
1496
|
+
}
|
|
1497
|
+
async dragTo(target) {
|
|
1498
|
+
const targetInfo = target._getInfo();
|
|
1499
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "dragTo", targetInfo], { pageId: this.#pageId });
|
|
1500
|
+
}
|
|
1501
|
+
async scrollIntoViewIfNeeded() {
|
|
1502
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "scrollIntoViewIfNeeded", null], { pageId: this.#pageId });
|
|
1503
|
+
}
|
|
1504
|
+
async highlight() {
|
|
1505
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "highlight", null], { pageId: this.#pageId });
|
|
1506
|
+
}
|
|
1507
|
+
async evaluate(fn, arg) {
|
|
1508
|
+
const fnString = typeof fn === 'function' ? fn.toString() : fn;
|
|
1509
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "evaluate", [fnString, arg]], { pageId: this.#pageId });
|
|
1510
|
+
}
|
|
1511
|
+
async evaluateAll(fn, arg) {
|
|
1512
|
+
const fnString = typeof fn === 'function' ? fn.toString() : fn;
|
|
1513
|
+
return __pw_invoke("locatorAction", [...this._getInfo(), "evaluateAll", [fnString, arg]], { pageId: this.#pageId });
|
|
1514
|
+
}
|
|
1515
|
+
locator(selector) {
|
|
1516
|
+
return this._chain("css", selector, null);
|
|
1517
|
+
}
|
|
1518
|
+
// Chaining: getBy* methods within a locator
|
|
1519
|
+
getByRole(role, options) {
|
|
1520
|
+
return this._chain("role", role, serializeOptions(options));
|
|
1521
|
+
}
|
|
1522
|
+
getByText(text) {
|
|
1523
|
+
return this._chain("text", text, null);
|
|
1524
|
+
}
|
|
1525
|
+
getByLabel(label) {
|
|
1526
|
+
return this._chain("label", label, null);
|
|
1527
|
+
}
|
|
1528
|
+
getByPlaceholder(placeholder) {
|
|
1529
|
+
return this._chain("placeholder", placeholder, null);
|
|
1530
|
+
}
|
|
1531
|
+
getByTestId(testId) {
|
|
1532
|
+
return this._chain("testId", testId, null);
|
|
1533
|
+
}
|
|
1534
|
+
getByAltText(altText) {
|
|
1535
|
+
return this._chain("altText", altText, null);
|
|
1536
|
+
}
|
|
1537
|
+
getByTitle(title) {
|
|
1538
|
+
return this._chain("title", title, null);
|
|
744
1539
|
}
|
|
745
1540
|
async all() {
|
|
746
1541
|
const n = await this.count();
|
|
@@ -752,7 +1547,7 @@ async function setupPlaywright(context, options) {
|
|
|
752
1547
|
}
|
|
753
1548
|
nth(index) {
|
|
754
1549
|
const existingOpts = this.#options ? JSON.parse(this.#options) : {};
|
|
755
|
-
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }));
|
|
1550
|
+
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }), this.#pageId);
|
|
756
1551
|
}
|
|
757
1552
|
first() {
|
|
758
1553
|
return this.nth(0);
|
|
@@ -763,13 +1558,37 @@ async function setupPlaywright(context, options) {
|
|
|
763
1558
|
filter(options) {
|
|
764
1559
|
const existingOpts = this.#options ? JSON.parse(this.#options) : {};
|
|
765
1560
|
const serializedFilter = { ...options };
|
|
766
|
-
|
|
767
|
-
|
|
1561
|
+
// Use duck-typing RegExp detection (instanceof fails across isolated-vm boundary)
|
|
1562
|
+
const hasText = options.hasText;
|
|
1563
|
+
if (hasText && typeof hasText === 'object' && typeof hasText.source === 'string' && typeof hasText.flags === 'string') {
|
|
1564
|
+
serializedFilter.hasText = { $regex: hasText.source, $flags: hasText.flags };
|
|
1565
|
+
}
|
|
1566
|
+
const hasNotText = options.hasNotText;
|
|
1567
|
+
if (hasNotText && typeof hasNotText === 'object' && typeof hasNotText.source === 'string' && typeof hasNotText.flags === 'string') {
|
|
1568
|
+
serializedFilter.hasNotText = { $regex: hasNotText.source, $flags: hasNotText.flags };
|
|
768
1569
|
}
|
|
769
|
-
|
|
770
|
-
|
|
1570
|
+
// Serialize has/hasNot locators using duck-typing
|
|
1571
|
+
const has = options.has;
|
|
1572
|
+
if (has && typeof has === 'object' && typeof has._getInfo === 'function') {
|
|
1573
|
+
serializedFilter.has = { $locator: has._getInfo() };
|
|
771
1574
|
}
|
|
772
|
-
|
|
1575
|
+
const hasNot = options.hasNot;
|
|
1576
|
+
if (hasNot && typeof hasNot === 'object' && typeof hasNot._getInfo === 'function') {
|
|
1577
|
+
serializedFilter.hasNot = { $locator: hasNot._getInfo() };
|
|
1578
|
+
}
|
|
1579
|
+
return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, filter: serializedFilter }), this.#pageId);
|
|
1580
|
+
}
|
|
1581
|
+
or(other) {
|
|
1582
|
+
// Create a composite locator that matches either this or other
|
|
1583
|
+
const thisInfo = this._getInfo();
|
|
1584
|
+
const otherInfo = other._getInfo();
|
|
1585
|
+
return new Locator("or", JSON.stringify([thisInfo, otherInfo]), null, this.#pageId);
|
|
1586
|
+
}
|
|
1587
|
+
and(other) {
|
|
1588
|
+
// Create a composite locator that matches both this and other
|
|
1589
|
+
const thisInfo = this._getInfo();
|
|
1590
|
+
const otherInfo = other._getInfo();
|
|
1591
|
+
return new Locator("and", JSON.stringify([thisInfo, otherInfo]), null, this.#pageId);
|
|
773
1592
|
}
|
|
774
1593
|
}
|
|
775
1594
|
globalThis.Locator = Locator;
|
|
@@ -780,84 +1599,157 @@ async function setupPlaywright(context, options) {
|
|
|
780
1599
|
// Helper to create locator matchers
|
|
781
1600
|
function createLocatorMatchers(locator, baseMatchers) {
|
|
782
1601
|
const info = locator._getInfo();
|
|
1602
|
+
const pageId = locator._getPageId ? locator._getPageId() : "page_0";
|
|
1603
|
+
|
|
1604
|
+
// Helper for serializing regex values
|
|
1605
|
+
function serializeExpected(expected) {
|
|
1606
|
+
if (expected instanceof RegExp) {
|
|
1607
|
+
return { $regex: expected.source, $flags: expected.flags };
|
|
1608
|
+
}
|
|
1609
|
+
return expected;
|
|
1610
|
+
}
|
|
783
1611
|
|
|
784
1612
|
const locatorMatchers = {
|
|
785
1613
|
async toBeVisible(options) {
|
|
786
|
-
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout]);
|
|
1614
|
+
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout], { pageId });
|
|
787
1615
|
},
|
|
788
1616
|
async toContainText(expected, options) {
|
|
789
|
-
|
|
790
|
-
return __pw_invoke("expectLocator", [...info, "toContainText", serialized, false, options?.timeout]);
|
|
1617
|
+
return __pw_invoke("expectLocator", [...info, "toContainText", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
791
1618
|
},
|
|
792
1619
|
async toHaveValue(expected, options) {
|
|
793
|
-
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout]);
|
|
1620
|
+
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout], { pageId });
|
|
794
1621
|
},
|
|
795
1622
|
async toBeEnabled(options) {
|
|
796
|
-
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, false, options?.timeout]);
|
|
1623
|
+
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, false, options?.timeout], { pageId });
|
|
797
1624
|
},
|
|
798
1625
|
async toBeChecked(options) {
|
|
799
|
-
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout]);
|
|
1626
|
+
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout], { pageId });
|
|
800
1627
|
},
|
|
801
1628
|
async toHaveAttribute(name, value, options) {
|
|
802
|
-
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, false, options?.timeout]);
|
|
1629
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value: serializeExpected(value) }, false, options?.timeout], { pageId });
|
|
803
1630
|
},
|
|
804
1631
|
async toHaveText(expected, options) {
|
|
805
|
-
|
|
806
|
-
return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, false, options?.timeout]);
|
|
1632
|
+
return __pw_invoke("expectLocator", [...info, "toHaveText", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
807
1633
|
},
|
|
808
1634
|
async toHaveCount(count, options) {
|
|
809
|
-
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout]);
|
|
1635
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout], { pageId });
|
|
810
1636
|
},
|
|
811
1637
|
async toBeHidden(options) {
|
|
812
|
-
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout]);
|
|
1638
|
+
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout], { pageId });
|
|
813
1639
|
},
|
|
814
1640
|
async toBeDisabled(options) {
|
|
815
|
-
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout]);
|
|
1641
|
+
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout], { pageId });
|
|
816
1642
|
},
|
|
817
1643
|
async toBeFocused(options) {
|
|
818
|
-
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout]);
|
|
1644
|
+
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout], { pageId });
|
|
819
1645
|
},
|
|
820
1646
|
async toBeEmpty(options) {
|
|
821
|
-
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout]);
|
|
1647
|
+
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout], { pageId });
|
|
1648
|
+
},
|
|
1649
|
+
// New matchers
|
|
1650
|
+
async toBeAttached(options) {
|
|
1651
|
+
return __pw_invoke("expectLocator", [...info, "toBeAttached", null, false, options?.timeout], { pageId });
|
|
1652
|
+
},
|
|
1653
|
+
async toBeEditable(options) {
|
|
1654
|
+
return __pw_invoke("expectLocator", [...info, "toBeEditable", null, false, options?.timeout], { pageId });
|
|
1655
|
+
},
|
|
1656
|
+
async toHaveClass(expected, options) {
|
|
1657
|
+
return __pw_invoke("expectLocator", [...info, "toHaveClass", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1658
|
+
},
|
|
1659
|
+
async toContainClass(expected, options) {
|
|
1660
|
+
return __pw_invoke("expectLocator", [...info, "toContainClass", expected, false, options?.timeout], { pageId });
|
|
1661
|
+
},
|
|
1662
|
+
async toHaveId(expected, options) {
|
|
1663
|
+
return __pw_invoke("expectLocator", [...info, "toHaveId", expected, false, options?.timeout], { pageId });
|
|
1664
|
+
},
|
|
1665
|
+
async toBeInViewport(options) {
|
|
1666
|
+
return __pw_invoke("expectLocator", [...info, "toBeInViewport", null, false, options?.timeout], { pageId });
|
|
1667
|
+
},
|
|
1668
|
+
async toHaveCSS(name, value, options) {
|
|
1669
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCSS", { name, value: serializeExpected(value) }, false, options?.timeout], { pageId });
|
|
1670
|
+
},
|
|
1671
|
+
async toHaveJSProperty(name, value, options) {
|
|
1672
|
+
return __pw_invoke("expectLocator", [...info, "toHaveJSProperty", { name, value }, false, options?.timeout], { pageId });
|
|
1673
|
+
},
|
|
1674
|
+
async toHaveAccessibleName(expected, options) {
|
|
1675
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleName", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1676
|
+
},
|
|
1677
|
+
async toHaveAccessibleDescription(expected, options) {
|
|
1678
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleDescription", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1679
|
+
},
|
|
1680
|
+
async toHaveRole(expected, options) {
|
|
1681
|
+
return __pw_invoke("expectLocator", [...info, "toHaveRole", expected, false, options?.timeout], { pageId });
|
|
822
1682
|
},
|
|
823
1683
|
not: {
|
|
824
1684
|
async toBeVisible(options) {
|
|
825
|
-
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout]);
|
|
1685
|
+
return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout], { pageId });
|
|
826
1686
|
},
|
|
827
1687
|
async toContainText(expected, options) {
|
|
828
|
-
|
|
829
|
-
return __pw_invoke("expectLocator", [...info, "toContainText", serialized, true, options?.timeout]);
|
|
1688
|
+
return __pw_invoke("expectLocator", [...info, "toContainText", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
830
1689
|
},
|
|
831
1690
|
async toHaveValue(expected, options) {
|
|
832
|
-
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout]);
|
|
1691
|
+
return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout], { pageId });
|
|
833
1692
|
},
|
|
834
1693
|
async toBeEnabled(options) {
|
|
835
|
-
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, true, options?.timeout]);
|
|
1694
|
+
return __pw_invoke("expectLocator", [...info, "toBeEnabled", null, true, options?.timeout], { pageId });
|
|
836
1695
|
},
|
|
837
1696
|
async toBeChecked(options) {
|
|
838
|
-
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout]);
|
|
1697
|
+
return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout], { pageId });
|
|
839
1698
|
},
|
|
840
1699
|
async toHaveAttribute(name, value, options) {
|
|
841
|
-
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, true, options?.timeout]);
|
|
1700
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value: serializeExpected(value) }, true, options?.timeout], { pageId });
|
|
842
1701
|
},
|
|
843
1702
|
async toHaveText(expected, options) {
|
|
844
|
-
|
|
845
|
-
return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, true, options?.timeout]);
|
|
1703
|
+
return __pw_invoke("expectLocator", [...info, "toHaveText", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
846
1704
|
},
|
|
847
1705
|
async toHaveCount(count, options) {
|
|
848
|
-
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout]);
|
|
1706
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout], { pageId });
|
|
849
1707
|
},
|
|
850
1708
|
async toBeHidden(options) {
|
|
851
|
-
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout]);
|
|
1709
|
+
return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout], { pageId });
|
|
852
1710
|
},
|
|
853
1711
|
async toBeDisabled(options) {
|
|
854
|
-
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout]);
|
|
1712
|
+
return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout], { pageId });
|
|
855
1713
|
},
|
|
856
1714
|
async toBeFocused(options) {
|
|
857
|
-
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout]);
|
|
1715
|
+
return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout], { pageId });
|
|
858
1716
|
},
|
|
859
1717
|
async toBeEmpty(options) {
|
|
860
|
-
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout]);
|
|
1718
|
+
return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout], { pageId });
|
|
1719
|
+
},
|
|
1720
|
+
// New negated matchers
|
|
1721
|
+
async toBeAttached(options) {
|
|
1722
|
+
return __pw_invoke("expectLocator", [...info, "toBeAttached", null, true, options?.timeout], { pageId });
|
|
1723
|
+
},
|
|
1724
|
+
async toBeEditable(options) {
|
|
1725
|
+
return __pw_invoke("expectLocator", [...info, "toBeEditable", null, true, options?.timeout], { pageId });
|
|
1726
|
+
},
|
|
1727
|
+
async toHaveClass(expected, options) {
|
|
1728
|
+
return __pw_invoke("expectLocator", [...info, "toHaveClass", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1729
|
+
},
|
|
1730
|
+
async toContainClass(expected, options) {
|
|
1731
|
+
return __pw_invoke("expectLocator", [...info, "toContainClass", expected, true, options?.timeout], { pageId });
|
|
1732
|
+
},
|
|
1733
|
+
async toHaveId(expected, options) {
|
|
1734
|
+
return __pw_invoke("expectLocator", [...info, "toHaveId", expected, true, options?.timeout], { pageId });
|
|
1735
|
+
},
|
|
1736
|
+
async toBeInViewport(options) {
|
|
1737
|
+
return __pw_invoke("expectLocator", [...info, "toBeInViewport", null, true, options?.timeout], { pageId });
|
|
1738
|
+
},
|
|
1739
|
+
async toHaveCSS(name, value, options) {
|
|
1740
|
+
return __pw_invoke("expectLocator", [...info, "toHaveCSS", { name, value: serializeExpected(value) }, true, options?.timeout], { pageId });
|
|
1741
|
+
},
|
|
1742
|
+
async toHaveJSProperty(name, value, options) {
|
|
1743
|
+
return __pw_invoke("expectLocator", [...info, "toHaveJSProperty", { name, value }, true, options?.timeout], { pageId });
|
|
1744
|
+
},
|
|
1745
|
+
async toHaveAccessibleName(expected, options) {
|
|
1746
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleName", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1747
|
+
},
|
|
1748
|
+
async toHaveAccessibleDescription(expected, options) {
|
|
1749
|
+
return __pw_invoke("expectLocator", [...info, "toHaveAccessibleDescription", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1750
|
+
},
|
|
1751
|
+
async toHaveRole(expected, options) {
|
|
1752
|
+
return __pw_invoke("expectLocator", [...info, "toHaveRole", expected, true, options?.timeout], { pageId });
|
|
861
1753
|
},
|
|
862
1754
|
}
|
|
863
1755
|
};
|
|
@@ -873,6 +1765,44 @@ async function setupPlaywright(context, options) {
|
|
|
873
1765
|
return locatorMatchers;
|
|
874
1766
|
}
|
|
875
1767
|
|
|
1768
|
+
// Helper to create page matchers
|
|
1769
|
+
function createPageMatchers(page, baseMatchers) {
|
|
1770
|
+
const pageId = page.__pageId || "page_0";
|
|
1771
|
+
|
|
1772
|
+
function serializeExpected(expected) {
|
|
1773
|
+
if (expected instanceof RegExp) {
|
|
1774
|
+
return { $regex: expected.source, $flags: expected.flags };
|
|
1775
|
+
}
|
|
1776
|
+
return expected;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
const pageMatchers = {
|
|
1780
|
+
async toHaveURL(expected, options) {
|
|
1781
|
+
return __pw_invoke("expectPage", ["toHaveURL", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1782
|
+
},
|
|
1783
|
+
async toHaveTitle(expected, options) {
|
|
1784
|
+
return __pw_invoke("expectPage", ["toHaveTitle", serializeExpected(expected), false, options?.timeout], { pageId });
|
|
1785
|
+
},
|
|
1786
|
+
not: {
|
|
1787
|
+
async toHaveURL(expected, options) {
|
|
1788
|
+
return __pw_invoke("expectPage", ["toHaveURL", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1789
|
+
},
|
|
1790
|
+
async toHaveTitle(expected, options) {
|
|
1791
|
+
return __pw_invoke("expectPage", ["toHaveTitle", serializeExpected(expected), true, options?.timeout], { pageId });
|
|
1792
|
+
},
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
|
|
1796
|
+
if (baseMatchers) {
|
|
1797
|
+
return {
|
|
1798
|
+
...baseMatchers,
|
|
1799
|
+
...pageMatchers,
|
|
1800
|
+
not: { ...baseMatchers.not, ...pageMatchers.not }
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
return pageMatchers;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
876
1806
|
// Only extend expect if test-environment already defined it
|
|
877
1807
|
if (typeof globalThis.expect === 'function') {
|
|
878
1808
|
const originalExpect = globalThis.expect;
|
|
@@ -882,6 +1812,10 @@ async function setupPlaywright(context, options) {
|
|
|
882
1812
|
if (actual && actual.constructor && actual.constructor.name === 'Locator') {
|
|
883
1813
|
return createLocatorMatchers(actual, baseMatchers);
|
|
884
1814
|
}
|
|
1815
|
+
// If actual is the page object (IsolatePage), add page-specific matchers
|
|
1816
|
+
if (actual && actual.__isPage === true) {
|
|
1817
|
+
return createPageMatchers(actual, baseMatchers);
|
|
1818
|
+
}
|
|
885
1819
|
return baseMatchers;
|
|
886
1820
|
};
|
|
887
1821
|
}
|
|
@@ -917,7 +1851,10 @@ async function setupPlaywright(context, options) {
|
|
|
917
1851
|
}
|
|
918
1852
|
export {
|
|
919
1853
|
setupPlaywright,
|
|
920
|
-
|
|
1854
|
+
getDefaultPlaywrightHandlerMetadata,
|
|
1855
|
+
defaultPlaywrightHandler,
|
|
1856
|
+
createPlaywrightHandler,
|
|
1857
|
+
DEFAULT_PLAYWRIGHT_HANDLER_META
|
|
921
1858
|
};
|
|
922
1859
|
|
|
923
|
-
//# debugId=
|
|
1860
|
+
//# debugId=AFE0563DA4EAC06264756E2164756E21
|