@alumnium/playwright 0.0.0-stage → 0.24.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +2 -2
- package/package.json +37 -4
- package/src/Alumni.d.ts +16 -0
- package/src/drivers/PlaywrightDriver.d.ts +75 -0
- package/src/index.cjs +1005 -0
- package/src/index.cjs.map +11 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +943 -0
- package/src/index.js.map +11 -0
package/src/index.cjs
ADDED
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap()).get(from),
|
|
10
|
+
desc;
|
|
11
|
+
if (entry) return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if ((from && typeof from === "object") || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name),
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
var __legacyDecorateClassTS = function (decorators, target, key, desc) {
|
|
39
|
+
var c = arguments.length,
|
|
40
|
+
r =
|
|
41
|
+
c < 3
|
|
42
|
+
? target
|
|
43
|
+
: desc === null
|
|
44
|
+
? (desc = Object.getOwnPropertyDescriptor(target, key))
|
|
45
|
+
: desc,
|
|
46
|
+
d;
|
|
47
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
48
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
49
|
+
else
|
|
50
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
51
|
+
if ((d = decorators[i]))
|
|
52
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
53
|
+
return (c > 3 && r && Object.defineProperty(target, key, r), r);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// playwright/src/index.ts
|
|
57
|
+
var exports_src = {};
|
|
58
|
+
__export(exports_src, {
|
|
59
|
+
Alumni: () => Alumni,
|
|
60
|
+
PlaywrightDriver: () => PlaywrightDriver,
|
|
61
|
+
});
|
|
62
|
+
module.exports = __toCommonJS(exports_src);
|
|
63
|
+
|
|
64
|
+
// playwright/src/Alumni.ts
|
|
65
|
+
var import_core = require("@alumnium/core");
|
|
66
|
+
|
|
67
|
+
// playwright/src/drivers/PlaywrightDriver.ts
|
|
68
|
+
var import_alwaysly = require("alwaysly");
|
|
69
|
+
var import_internal = require("@alumnium/core/internal");
|
|
70
|
+
var { tracer, logger } = import_internal.Telemetry.get(
|
|
71
|
+
"packages/typescript/playwright/src/drivers/PlaywrightDriver.ts",
|
|
72
|
+
);
|
|
73
|
+
var { span } = tracer.dec();
|
|
74
|
+
var stateful = import_internal.BaseDriver.stateful;
|
|
75
|
+
var CONTEXT_WAS_DESTROYED_ERROR = "Execution context was destroyed";
|
|
76
|
+
var NEW_TAB_DELAY = 200;
|
|
77
|
+
var WAITER_SCRIPT = import_internal.waiterScriptSource;
|
|
78
|
+
var RETRY_OPTIONS = {
|
|
79
|
+
maxAttempts: 2,
|
|
80
|
+
backOff: 500,
|
|
81
|
+
doRetry: (error) => error.message.includes(CONTEXT_WAS_DESTROYED_ERROR),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
class PlaywrightDriver extends import_internal.BaseDriver {
|
|
85
|
+
client;
|
|
86
|
+
cdpReady;
|
|
87
|
+
#pageSessions = new Map();
|
|
88
|
+
#pageOopifFrameIds = new WeakMap();
|
|
89
|
+
cdpGeneration = 0;
|
|
90
|
+
page;
|
|
91
|
+
trackedPages = new Set();
|
|
92
|
+
#previousPage;
|
|
93
|
+
newTabAction;
|
|
94
|
+
oopifFrameIds = new Map();
|
|
95
|
+
oopifFrames = new Set();
|
|
96
|
+
kind = "playwright";
|
|
97
|
+
platform = "chromium";
|
|
98
|
+
supportedTools = new Set([
|
|
99
|
+
import_internal.ClickTool,
|
|
100
|
+
import_internal.DragAndDropTool,
|
|
101
|
+
import_internal.HoverTool,
|
|
102
|
+
import_internal.PressKeyTool,
|
|
103
|
+
import_internal.TypeTool,
|
|
104
|
+
import_internal.UploadTool,
|
|
105
|
+
]);
|
|
106
|
+
newTabTimeout = 1e4;
|
|
107
|
+
autoswitchToNewTab = true;
|
|
108
|
+
fullPageScreenshot = import_internal.Env.ALUMNIUM_FULL_PAGE_SCREENSHOT;
|
|
109
|
+
constructor(page) {
|
|
110
|
+
super();
|
|
111
|
+
this.page = page;
|
|
112
|
+
this.cdpReady = Promise.resolve(
|
|
113
|
+
this.page.context().addInitScript({ content: WAITER_SCRIPT }),
|
|
114
|
+
).then(() => this.initCDPSession());
|
|
115
|
+
this.page.context().on("page", (opened) => this.onPageOpened(opened));
|
|
116
|
+
this.trackPage(page);
|
|
117
|
+
}
|
|
118
|
+
trackPage(page) {
|
|
119
|
+
if (this.trackedPages.has(page)) return;
|
|
120
|
+
this.trackedPages.add(page);
|
|
121
|
+
page.on("close", () => this.onPageClose(page));
|
|
122
|
+
}
|
|
123
|
+
onPageOpened(page) {
|
|
124
|
+
logger.debug(`New tab opened: ${page.url()}`);
|
|
125
|
+
this.trackPage(page);
|
|
126
|
+
this.newTabAction?.pages.push(page);
|
|
127
|
+
this.#initPageSession(page).catch((error) => {
|
|
128
|
+
logger.debug(
|
|
129
|
+
`Could not initialize new tab CDP session: ${String(error)}`,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
onPageClose(page) {
|
|
134
|
+
logger.debug(`Page closed: ${page.url()}`);
|
|
135
|
+
this.trackedPages.delete(page);
|
|
136
|
+
const session = this.#pageSessions.get(page);
|
|
137
|
+
this.#pageSessions.delete(page);
|
|
138
|
+
this.#pageOopifFrameIds.delete(page);
|
|
139
|
+
session
|
|
140
|
+
?.then((client) => client.detach())
|
|
141
|
+
.catch(() => {
|
|
142
|
+
return;
|
|
143
|
+
});
|
|
144
|
+
if (this.newTabAction)
|
|
145
|
+
this.newTabAction.pages = this.newTabAction.pages.filter(
|
|
146
|
+
(opened) => opened !== page,
|
|
147
|
+
);
|
|
148
|
+
if (page !== this.page) return;
|
|
149
|
+
const previous = this.#previousPage;
|
|
150
|
+
if (!previous || previous.isClosed()) return;
|
|
151
|
+
this.page = previous;
|
|
152
|
+
this.#previousPage = undefined;
|
|
153
|
+
this.resetAccessibilityTree();
|
|
154
|
+
this.cdpReady = this.initCDPSession().catch((error) => {
|
|
155
|
+
logger.info(`Failed to initialize CDP session: ${String(error)}`);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async initCDPSession() {
|
|
159
|
+
const generation = ++this.cdpGeneration;
|
|
160
|
+
const page = this.page;
|
|
161
|
+
this.oopifFrames.clear();
|
|
162
|
+
const client = await this.#initPageSession(page);
|
|
163
|
+
if (generation !== this.cdpGeneration || page !== this.page) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.client = client;
|
|
167
|
+
this.oopifFrameIds = this.#pageOopifFrameIds.get(page);
|
|
168
|
+
}
|
|
169
|
+
#initPageSession(page) {
|
|
170
|
+
let session = this.#pageSessions.get(page);
|
|
171
|
+
if (!session) {
|
|
172
|
+
session = this.#createPageSession(page).catch((error) => {
|
|
173
|
+
this.#pageSessions.delete(page);
|
|
174
|
+
throw error;
|
|
175
|
+
});
|
|
176
|
+
this.#pageSessions.set(page, session);
|
|
177
|
+
}
|
|
178
|
+
return session;
|
|
179
|
+
}
|
|
180
|
+
async #createPageSession(page) {
|
|
181
|
+
const frameIds = new Map();
|
|
182
|
+
this.#pageOopifFrameIds.set(page, frameIds);
|
|
183
|
+
const session = await page.context().newCDPSession(page);
|
|
184
|
+
try {
|
|
185
|
+
await this.configurePageSession(session, page);
|
|
186
|
+
await this.enableTargetAutoAttach(session, frameIds);
|
|
187
|
+
return session;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
await session.detach().catch(() => {
|
|
190
|
+
return;
|
|
191
|
+
});
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async configurePageSession(session, page) {
|
|
196
|
+
await session.send("Page.enable");
|
|
197
|
+
await session.send("Page.addScriptToEvaluateOnNewDocument", {
|
|
198
|
+
source: WAITER_SCRIPT,
|
|
199
|
+
runImmediately: true,
|
|
200
|
+
});
|
|
201
|
+
session.on("Page.windowOpen", () => {
|
|
202
|
+
if (page === this.page && this.newTabAction)
|
|
203
|
+
this.newTabAction.announced = true;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async enableTargetAutoAttach(client, frameIds) {
|
|
207
|
+
try {
|
|
208
|
+
client.on("Target.attachedToTarget", (event) => {
|
|
209
|
+
if (event.targetInfo.type === "iframe") {
|
|
210
|
+
logger.debug(
|
|
211
|
+
`OOPIF attached: frameId=${event.targetInfo.targetId} url=${event.targetInfo.url || "(empty)"}`,
|
|
212
|
+
);
|
|
213
|
+
frameIds.set(event.targetInfo.targetId, event.targetInfo.url);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
client.on("Target.detachedFromTarget", (event) => {
|
|
217
|
+
if (event.targetId && frameIds.has(event.targetId)) {
|
|
218
|
+
logger.debug(`OOPIF detached: frameId=${event.targetId}`);
|
|
219
|
+
frameIds.delete(event.targetId);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
client.on("Page.frameNavigated", (event) => {
|
|
223
|
+
if (frameIds.has(event.frame.id)) {
|
|
224
|
+
logger.debug(
|
|
225
|
+
`OOPIF navigated: frameId=${event.frame.id} url=${event.frame.url}`,
|
|
226
|
+
);
|
|
227
|
+
frameIds.set(event.frame.id, event.frame.url);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
await client.send("Target.setAutoAttach", {
|
|
231
|
+
autoAttach: true,
|
|
232
|
+
waitForDebuggerOnStart: false,
|
|
233
|
+
flatten: true,
|
|
234
|
+
});
|
|
235
|
+
logger.debug("Enabled Target.setAutoAttach for OOPIF support");
|
|
236
|
+
} catch (error) {
|
|
237
|
+
logger.debug(
|
|
238
|
+
`Could not enable Target.setAutoAttach: ${error instanceof Error ? error.message : String(error)}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async fetchAccessibilityTree() {
|
|
243
|
+
await this.cdpReady;
|
|
244
|
+
await this.waitForPageToLoad();
|
|
245
|
+
const frameTree = await this.client.send("Page.getFrameTree");
|
|
246
|
+
const frameIds = this.getAllFrameIds(frameTree.frameTree);
|
|
247
|
+
const mainFrameId = frameTree.frameTree.frame.id;
|
|
248
|
+
logger.debug(
|
|
249
|
+
`Found ${frameIds.length} same-process frames, ${this.oopifFrameIds.size} OOPIFs`,
|
|
250
|
+
);
|
|
251
|
+
const frameToIframeMap = await this.buildFrameOwnerMap(
|
|
252
|
+
frameTree.frameTree,
|
|
253
|
+
mainFrameId,
|
|
254
|
+
);
|
|
255
|
+
const frameIdToPlaywrightFrame =
|
|
256
|
+
await this.buildPlaywrightFrameMap(frameTree);
|
|
257
|
+
const allNodes = [];
|
|
258
|
+
let frameIndex = 0;
|
|
259
|
+
for (const frameId of frameIds) {
|
|
260
|
+
const playwrightFrame =
|
|
261
|
+
frameIdToPlaywrightFrame.get(frameId) ?? this.page.mainFrame();
|
|
262
|
+
const nodes = await this.getFrameNodes(frameId, playwrightFrame);
|
|
263
|
+
this.mergeFrameNodes(
|
|
264
|
+
nodes,
|
|
265
|
+
frameId,
|
|
266
|
+
frameToIframeMap,
|
|
267
|
+
playwrightFrame,
|
|
268
|
+
frameIndex++,
|
|
269
|
+
allNodes,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
for (const oopifFrameId of this.oopifFrameIds.keys()) {
|
|
273
|
+
const playwrightFrame = frameIdToPlaywrightFrame.get(oopifFrameId);
|
|
274
|
+
if (!playwrightFrame) continue;
|
|
275
|
+
const nodes = await this.getOopifNodes(oopifFrameId, playwrightFrame);
|
|
276
|
+
this.mergeFrameNodes(
|
|
277
|
+
nodes,
|
|
278
|
+
oopifFrameId,
|
|
279
|
+
frameToIframeMap,
|
|
280
|
+
playwrightFrame,
|
|
281
|
+
frameIndex++,
|
|
282
|
+
allNodes,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
return new import_internal.ChromiumAccessibilityTree({ nodes: allNodes });
|
|
286
|
+
}
|
|
287
|
+
async click(id) {
|
|
288
|
+
const element = await this.findElement(id);
|
|
289
|
+
const tagName = await element.evaluate((el) => el.tagName);
|
|
290
|
+
if (tagName?.toLowerCase() === "option") {
|
|
291
|
+
const value = await element.evaluate((el) => el.value);
|
|
292
|
+
await this.autoswitchToNewTabAction(async () => {
|
|
293
|
+
await element.locator("xpath=ancestor::select").selectOption(value);
|
|
294
|
+
});
|
|
295
|
+
} else {
|
|
296
|
+
await this.autoswitchToNewTabAction(async () => {
|
|
297
|
+
await this.#scrollElementIfNeeded(element);
|
|
298
|
+
await element.click({ force: true });
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async dragSlider(id, value) {
|
|
303
|
+
const element = await this.findElement(id);
|
|
304
|
+
await this.#scrollElementIfNeeded(element);
|
|
305
|
+
await element.fill(String(value));
|
|
306
|
+
}
|
|
307
|
+
async dragAndDrop(fromId, toId) {
|
|
308
|
+
const fromElement = await this.findElement(fromId);
|
|
309
|
+
const toElement = await this.findElement(toId);
|
|
310
|
+
await this.#scrollElementIfNeeded(fromElement);
|
|
311
|
+
await fromElement.dragTo(toElement);
|
|
312
|
+
}
|
|
313
|
+
async hover(id) {
|
|
314
|
+
const element = await this.findElement(id);
|
|
315
|
+
await this.#scrollElementIntoCenter(element);
|
|
316
|
+
await element.hover();
|
|
317
|
+
}
|
|
318
|
+
async pressKey(key) {
|
|
319
|
+
const keyMap = {
|
|
320
|
+
Backspace: "Backspace",
|
|
321
|
+
Enter: "Enter",
|
|
322
|
+
Escape: "Escape",
|
|
323
|
+
Space: "Space",
|
|
324
|
+
Tab: "Tab",
|
|
325
|
+
ArrowDown: "ArrowDown",
|
|
326
|
+
ArrowUp: "ArrowUp",
|
|
327
|
+
ArrowLeft: "ArrowLeft",
|
|
328
|
+
ArrowRight: "ArrowRight",
|
|
329
|
+
};
|
|
330
|
+
await this.autoswitchToNewTabAction(() =>
|
|
331
|
+
this.page.keyboard.press(keyMap[key]),
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
async quit() {
|
|
335
|
+
await this.cdpReady;
|
|
336
|
+
await this.page.close();
|
|
337
|
+
}
|
|
338
|
+
async back() {
|
|
339
|
+
await this.page.goBack();
|
|
340
|
+
}
|
|
341
|
+
async visit(url) {
|
|
342
|
+
await this.checkNavigationPolicy(url);
|
|
343
|
+
await this.page.goto(url);
|
|
344
|
+
}
|
|
345
|
+
async scrollTo(id) {
|
|
346
|
+
const element = await this.findElement(id);
|
|
347
|
+
await this.#scrollElementIntoCenter(element);
|
|
348
|
+
}
|
|
349
|
+
async screenshot() {
|
|
350
|
+
return import_internal.retry(RETRY_OPTIONS, async () => {
|
|
351
|
+
const buffer = await this.page.screenshot({
|
|
352
|
+
fullPage: this.fullPageScreenshot,
|
|
353
|
+
});
|
|
354
|
+
return buffer.toString("base64");
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async title() {
|
|
358
|
+
return import_internal.retry(RETRY_OPTIONS, () => this.page.title());
|
|
359
|
+
}
|
|
360
|
+
async type(id, text) {
|
|
361
|
+
const element = await this.findElement(id);
|
|
362
|
+
await this.#scrollElementIfNeeded(element);
|
|
363
|
+
await element.fill(text);
|
|
364
|
+
}
|
|
365
|
+
async upload(id, paths) {
|
|
366
|
+
const element = await this.findElement(id);
|
|
367
|
+
const [fileChooser] = await Promise.all([
|
|
368
|
+
this.page.waitForEvent("filechooser", { timeout: 5000 }),
|
|
369
|
+
element.click({ force: true }),
|
|
370
|
+
]);
|
|
371
|
+
await fileChooser.setFiles(paths);
|
|
372
|
+
}
|
|
373
|
+
url() {
|
|
374
|
+
return import_internal.retry(RETRY_OPTIONS, async () => this.page.url());
|
|
375
|
+
}
|
|
376
|
+
async app() {
|
|
377
|
+
return import_internal.AppId.parse(this.page.url());
|
|
378
|
+
}
|
|
379
|
+
async findElement(id) {
|
|
380
|
+
const tree = await this.getAccessibilityTree();
|
|
381
|
+
const accessibilityElement = tree.elementById(id);
|
|
382
|
+
const frame = accessibilityElement.frame || this.page.mainFrame();
|
|
383
|
+
const backendNodeId = accessibilityElement.backendNodeId;
|
|
384
|
+
const isOopif = frame !== this.page.mainFrame() && this.isOopifFrame(frame);
|
|
385
|
+
const session = isOopif
|
|
386
|
+
? await this.page.context().newCDPSession(frame)
|
|
387
|
+
: this.client;
|
|
388
|
+
try {
|
|
389
|
+
await session.send("DOM.enable");
|
|
390
|
+
await session.send("DOM.getFlattenedDocument");
|
|
391
|
+
const nodeIds = await session.send(
|
|
392
|
+
"DOM.pushNodesByBackendIdsToFrontend",
|
|
393
|
+
{
|
|
394
|
+
backendNodeIds: [backendNodeId],
|
|
395
|
+
},
|
|
396
|
+
);
|
|
397
|
+
const nodeId = nodeIds.nodeIds[0];
|
|
398
|
+
import_alwaysly.ensure(nodeId);
|
|
399
|
+
await session.send("DOM.setAttributeValue", {
|
|
400
|
+
nodeId,
|
|
401
|
+
name: "data-alumnium-id",
|
|
402
|
+
value: String(backendNodeId),
|
|
403
|
+
});
|
|
404
|
+
} finally {
|
|
405
|
+
if (isOopif) await session.detach();
|
|
406
|
+
}
|
|
407
|
+
return frame.locator(`css=[data-alumnium-id='${backendNodeId}']`);
|
|
408
|
+
}
|
|
409
|
+
async #scrollElementIfNeeded(element) {
|
|
410
|
+
try {
|
|
411
|
+
logger.debug(`Attempting to hover over element`);
|
|
412
|
+
await element.hover({ trial: true, timeout: 200 });
|
|
413
|
+
} catch (error) {
|
|
414
|
+
logger.debug(error instanceof Error ? error.message : String(error));
|
|
415
|
+
logger.debug(`Hover failed, scrolling into view instead`);
|
|
416
|
+
await this.#scrollElementIntoCenter(element);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async #scrollElementIntoCenter(element) {
|
|
420
|
+
await element.evaluate((el) => {
|
|
421
|
+
el.scrollIntoView({ block: "center" });
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
isOopifFrame(frame) {
|
|
425
|
+
return this.oopifFrames.has(frame);
|
|
426
|
+
}
|
|
427
|
+
async buildFrameOwnerMap(frameInfo, mainFrameId) {
|
|
428
|
+
const map = new Map();
|
|
429
|
+
await this.client.send("DOM.enable");
|
|
430
|
+
const walk = async (fi) => {
|
|
431
|
+
if (fi.frame.id !== mainFrameId) {
|
|
432
|
+
try {
|
|
433
|
+
const owner = await this.client.send("DOM.getFrameOwner", {
|
|
434
|
+
frameId: fi.frame.id,
|
|
435
|
+
});
|
|
436
|
+
map.set(fi.frame.id, owner.backendNodeId);
|
|
437
|
+
logger.debug(
|
|
438
|
+
`Frame ${fi.frame.id.slice(0, 20)}... owned by iframe backendNodeId=${owner.backendNodeId}`,
|
|
439
|
+
);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
logger.debug(
|
|
442
|
+
`Could not get frame owner for ${fi.frame.id.slice(0, 20)}...: ${error instanceof Error ? error.message : String(error)}`,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
for (const child of fi.childFrames || []) await walk(child);
|
|
447
|
+
};
|
|
448
|
+
await walk(frameInfo);
|
|
449
|
+
for (const oopifFrameId of this.oopifFrameIds.keys()) {
|
|
450
|
+
try {
|
|
451
|
+
const owner = await this.client.send("DOM.getFrameOwner", {
|
|
452
|
+
frameId: oopifFrameId,
|
|
453
|
+
});
|
|
454
|
+
map.set(oopifFrameId, owner.backendNodeId);
|
|
455
|
+
logger.debug(
|
|
456
|
+
`OOPIF ${oopifFrameId.slice(0, 20)}... owned by iframe backendNodeId=${owner.backendNodeId}`,
|
|
457
|
+
);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
logger.debug(
|
|
460
|
+
`Could not get frame owner for OOPIF ${oopifFrameId.slice(0, 20)}...: ${error instanceof Error ? error.message : String(error)}`,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return map;
|
|
465
|
+
}
|
|
466
|
+
async buildPlaywrightFrameMap(frameTree) {
|
|
467
|
+
const map = new Map();
|
|
468
|
+
this.mapFrameTree(frameTree.frameTree, this.page.mainFrame(), map);
|
|
469
|
+
const unmappedOopifs = new Set(
|
|
470
|
+
[...this.oopifFrameIds.keys()].filter((id) => !map.has(id)),
|
|
471
|
+
);
|
|
472
|
+
if (unmappedOopifs.size > 0) {
|
|
473
|
+
for (const playwrightFrame of this.page.frames()) {
|
|
474
|
+
if (playwrightFrame === this.page.mainFrame()) continue;
|
|
475
|
+
if ([...map.values()].includes(playwrightFrame)) continue;
|
|
476
|
+
let frameSession;
|
|
477
|
+
try {
|
|
478
|
+
frameSession = await this.page
|
|
479
|
+
.context()
|
|
480
|
+
.newCDPSession(playwrightFrame);
|
|
481
|
+
const ft = await frameSession.send("Page.getFrameTree");
|
|
482
|
+
const rootFrameId = ft.frameTree.frame.id;
|
|
483
|
+
if (unmappedOopifs.has(rootFrameId)) {
|
|
484
|
+
map.set(rootFrameId, playwrightFrame);
|
|
485
|
+
this.oopifFrames.add(playwrightFrame);
|
|
486
|
+
unmappedOopifs.delete(rootFrameId);
|
|
487
|
+
logger.debug(
|
|
488
|
+
`Mapped OOPIF ${rootFrameId.slice(0, 20)}... to Playwright frame`,
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
} catch {
|
|
492
|
+
} finally {
|
|
493
|
+
await frameSession?.detach().catch(() => {
|
|
494
|
+
return;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return map;
|
|
500
|
+
}
|
|
501
|
+
mapFrameTree(frameInfo, playwrightFrame, map) {
|
|
502
|
+
map.set(frameInfo.frame.id, playwrightFrame);
|
|
503
|
+
const unmatched = [...(frameInfo.childFrames || [])];
|
|
504
|
+
const pending = playwrightFrame.childFrames();
|
|
505
|
+
const take = (matches) => {
|
|
506
|
+
for (const frame of [...pending]) {
|
|
507
|
+
const index = unmatched.findIndex((child) => matches(child, frame));
|
|
508
|
+
if (index === -1) continue;
|
|
509
|
+
const [child] = unmatched.splice(index, 1);
|
|
510
|
+
pending.splice(pending.indexOf(frame), 1);
|
|
511
|
+
this.mapFrameTree(child, frame, map);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
take(
|
|
515
|
+
(child, frame) =>
|
|
516
|
+
child.frame.url === frame.url() &&
|
|
517
|
+
(child.frame.name || "") === frame.name(),
|
|
518
|
+
);
|
|
519
|
+
take((child, frame) => child.frame.url === frame.url());
|
|
520
|
+
}
|
|
521
|
+
mergeFrameNodes(
|
|
522
|
+
nodes,
|
|
523
|
+
frameId,
|
|
524
|
+
frameToIframeMap,
|
|
525
|
+
playwrightFrame,
|
|
526
|
+
frameIndex,
|
|
527
|
+
allNodes,
|
|
528
|
+
) {
|
|
529
|
+
const prefix = `f${frameIndex}:`;
|
|
530
|
+
for (const node of nodes) {
|
|
531
|
+
node.nodeId = prefix + node.nodeId;
|
|
532
|
+
if (node.parentId != null) node.parentId = prefix + node.parentId;
|
|
533
|
+
if (node.childIds) node.childIds = node.childIds.map((id) => prefix + id);
|
|
534
|
+
node._frame = playwrightFrame;
|
|
535
|
+
if (node.parentId === undefined && frameToIframeMap.has(frameId)) {
|
|
536
|
+
node._parent_iframe_backend_node_id = frameToIframeMap.get(frameId);
|
|
537
|
+
}
|
|
538
|
+
allNodes.push(node);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async executeScript(script) {
|
|
542
|
+
logger.debug("Executing script: {script}", { script });
|
|
543
|
+
await this.page.evaluate(script);
|
|
544
|
+
}
|
|
545
|
+
async printToPdf(filepath) {
|
|
546
|
+
await this.page.pdf({ path: filepath });
|
|
547
|
+
}
|
|
548
|
+
async switchToNextTab() {
|
|
549
|
+
await this.page.waitForTimeout(100).catch(() => {
|
|
550
|
+
return;
|
|
551
|
+
});
|
|
552
|
+
const pages = this.openTabs();
|
|
553
|
+
if (pages.length <= 1) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const currentIndex = pages.indexOf(this.page);
|
|
557
|
+
const nextIndex = (currentIndex + 1) % pages.length;
|
|
558
|
+
await this.switchToTab(pages[nextIndex]);
|
|
559
|
+
}
|
|
560
|
+
async switchToPreviousTab() {
|
|
561
|
+
await this.page.waitForTimeout(100).catch(() => {
|
|
562
|
+
return;
|
|
563
|
+
});
|
|
564
|
+
const pages = this.openTabs();
|
|
565
|
+
if (pages.length <= 1) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const currentIndex = pages.indexOf(this.page);
|
|
569
|
+
const prevIndex = (currentIndex - 1 + pages.length) % pages.length;
|
|
570
|
+
await this.switchToTab(pages[prevIndex]);
|
|
571
|
+
}
|
|
572
|
+
async switchToTab(page) {
|
|
573
|
+
if (!page) return;
|
|
574
|
+
await this.activatePage(page);
|
|
575
|
+
await this.page.waitForLoadState();
|
|
576
|
+
}
|
|
577
|
+
async activatePage(page) {
|
|
578
|
+
await this.cdpReady;
|
|
579
|
+
if (page !== this.page) this.#previousPage = this.page;
|
|
580
|
+
this.page = page;
|
|
581
|
+
this.trackPage(page);
|
|
582
|
+
this.resetAccessibilityTree();
|
|
583
|
+
this.cdpReady = this.initCDPSession();
|
|
584
|
+
await this.cdpReady;
|
|
585
|
+
}
|
|
586
|
+
openTabs() {
|
|
587
|
+
return this.page
|
|
588
|
+
.context()
|
|
589
|
+
.pages()
|
|
590
|
+
.filter((page) => !page.isClosed());
|
|
591
|
+
}
|
|
592
|
+
async wait(seconds) {
|
|
593
|
+
const clampedSeconds = Math.max(1, Math.min(30, seconds));
|
|
594
|
+
logger.debug("Waiting for {clampedSeconds} seconds", {
|
|
595
|
+
seconds: clampedSeconds,
|
|
596
|
+
});
|
|
597
|
+
await new Promise((resolve) => setTimeout(resolve, clampedSeconds * 1000));
|
|
598
|
+
}
|
|
599
|
+
async waitForSelector(selector, timeout) {
|
|
600
|
+
const timeoutMs = (timeout ?? 10) * 1000;
|
|
601
|
+
logger.debug(
|
|
602
|
+
"Waiting for selector {selector} with timeout {timeoutMs} ms",
|
|
603
|
+
{ selector, timeoutMs },
|
|
604
|
+
);
|
|
605
|
+
await this.page.waitForSelector(selector, {
|
|
606
|
+
state: "visible",
|
|
607
|
+
timeout: timeoutMs,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
async grantPermissions(permissions) {
|
|
611
|
+
await this.page.context().grantPermissions(permissions);
|
|
612
|
+
}
|
|
613
|
+
async checkNavigationPolicy(url) {
|
|
614
|
+
const currentUrl = url ?? (await this.url());
|
|
615
|
+
this.navigationPolicy.check(currentUrl);
|
|
616
|
+
}
|
|
617
|
+
async waitForPageToLoad() {
|
|
618
|
+
return import_internal.retry(RETRY_OPTIONS, async () => {
|
|
619
|
+
await this.cdpReady;
|
|
620
|
+
logger.debug("Waiting for page to finish loading:");
|
|
621
|
+
const result = await import_internal.waitForPageStability(() =>
|
|
622
|
+
this.page.evaluate(import_internal.WAITER_SNAPSHOT_SCRIPT),
|
|
623
|
+
);
|
|
624
|
+
if (!result.loaded) {
|
|
625
|
+
logger.debug(
|
|
626
|
+
` <- Timed out waiting for page to load; pending requests: ${result.pending.join(", ")}`,
|
|
627
|
+
);
|
|
628
|
+
} else {
|
|
629
|
+
logger.debug(" <- Page finished loading");
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
async autoswitchToNewTabAction(action) {
|
|
634
|
+
if (!this.autoswitchToNewTab) {
|
|
635
|
+
await action();
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
await this.cdpReady;
|
|
639
|
+
const context = this.page.context();
|
|
640
|
+
const newTabAction = { announced: false, pages: [] };
|
|
641
|
+
this.newTabAction = newTabAction;
|
|
642
|
+
try {
|
|
643
|
+
await action();
|
|
644
|
+
if (!newTabAction.pages.length)
|
|
645
|
+
await new Promise((resolve) => setTimeout(resolve, NEW_TAB_DELAY));
|
|
646
|
+
let newPage = newTabAction.pages.findLast((page) => !page.isClosed());
|
|
647
|
+
if (!newPage && newTabAction.announced) {
|
|
648
|
+
newPage = await this.waitForAnnouncedTab(context, newTabAction);
|
|
649
|
+
}
|
|
650
|
+
if (!newPage) return;
|
|
651
|
+
logger.debug(`Auto-switching to new tab: ${newPage.url()}`);
|
|
652
|
+
await this.activatePage(newPage);
|
|
653
|
+
} finally {
|
|
654
|
+
if (this.newTabAction === newTabAction) this.newTabAction = undefined;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
async waitForAnnouncedTab(context, action) {
|
|
658
|
+
const deadline = Date.now() + this.newTabTimeout;
|
|
659
|
+
while (Date.now() < deadline) {
|
|
660
|
+
const page = await context
|
|
661
|
+
.waitForEvent("page", {
|
|
662
|
+
timeout: Math.max(1, Math.min(100, deadline - Date.now())),
|
|
663
|
+
})
|
|
664
|
+
.catch(() => {
|
|
665
|
+
return;
|
|
666
|
+
});
|
|
667
|
+
if (page) return page;
|
|
668
|
+
const captured = action.pages.findLast(
|
|
669
|
+
(candidate) => !candidate.isClosed(),
|
|
670
|
+
);
|
|
671
|
+
if (captured) return captured;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
getAllFrameIds(frameInfo) {
|
|
675
|
+
const frameIds = [frameInfo.frame.id];
|
|
676
|
+
for (const child of frameInfo.childFrames || []) {
|
|
677
|
+
frameIds.push(...this.getAllFrameIds(child));
|
|
678
|
+
}
|
|
679
|
+
return frameIds;
|
|
680
|
+
}
|
|
681
|
+
async getFrameNodes(frameId, _playwrightFrame) {
|
|
682
|
+
let nodes;
|
|
683
|
+
try {
|
|
684
|
+
const response = await this.client.send("Accessibility.getFullAXTree", {
|
|
685
|
+
frameId,
|
|
686
|
+
});
|
|
687
|
+
nodes = response.nodes || [];
|
|
688
|
+
logger.debug(
|
|
689
|
+
` -> Frame ${frameId.slice(0, 20)}...: ${nodes.length} nodes`,
|
|
690
|
+
);
|
|
691
|
+
} catch (error) {
|
|
692
|
+
logger.debug(
|
|
693
|
+
` -> Frame ${frameId.slice(0, 20)}...: failed (${error instanceof Error ? error.message : String(error)})`,
|
|
694
|
+
);
|
|
695
|
+
return [];
|
|
696
|
+
}
|
|
697
|
+
return nodes;
|
|
698
|
+
}
|
|
699
|
+
async getOopifNodes(frameId, playwrightFrame) {
|
|
700
|
+
let frameSession;
|
|
701
|
+
try {
|
|
702
|
+
frameSession = await this.page.context().newCDPSession(playwrightFrame);
|
|
703
|
+
const response = await frameSession.send(
|
|
704
|
+
"Accessibility.getFullAXTree",
|
|
705
|
+
{},
|
|
706
|
+
);
|
|
707
|
+
const nodes = response.nodes || [];
|
|
708
|
+
logger.debug(
|
|
709
|
+
` -> OOPIF ${frameId.slice(0, 20)}...: got ${nodes.length} nodes`,
|
|
710
|
+
);
|
|
711
|
+
return nodes;
|
|
712
|
+
} catch (oopifError) {
|
|
713
|
+
logger.debug(
|
|
714
|
+
` -> OOPIF ${frameId.slice(0, 20)}...: failed (${oopifError instanceof Error ? oopifError.message : String(oopifError)})`,
|
|
715
|
+
);
|
|
716
|
+
return [];
|
|
717
|
+
} finally {
|
|
718
|
+
await frameSession?.detach().catch(() => {
|
|
719
|
+
return;
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
async devDrillProbeTree(tree, rawId) {
|
|
724
|
+
let accessibilityElement;
|
|
725
|
+
try {
|
|
726
|
+
accessibilityElement = tree.elementById(rawId);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
throw new import_internal.TreeDevDrillError("resolve", error);
|
|
729
|
+
}
|
|
730
|
+
const backendNodeId = accessibilityElement.backendNodeId;
|
|
731
|
+
if (backendNodeId === undefined) {
|
|
732
|
+
throw new import_internal.TreeDevDrillError(
|
|
733
|
+
"resolve",
|
|
734
|
+
new Error(`Element with raw_id=${rawId} has no backend node ID`),
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
const frame = accessibilityElement.frame ?? this.page.mainFrame();
|
|
738
|
+
const isOopif = frame !== this.page.mainFrame() && this.isOopifFrame(frame);
|
|
739
|
+
let session;
|
|
740
|
+
try {
|
|
741
|
+
session = isOopif
|
|
742
|
+
? await this.page.context().newCDPSession(frame)
|
|
743
|
+
: this.client;
|
|
744
|
+
} catch (error) {
|
|
745
|
+
throw new import_internal.TreeDevDrillError(
|
|
746
|
+
"resolve",
|
|
747
|
+
error,
|
|
748
|
+
backendNodeId,
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
const attribute = "data-alumnium-drill";
|
|
752
|
+
let nodeId;
|
|
753
|
+
let set = false;
|
|
754
|
+
let failure;
|
|
755
|
+
try {
|
|
756
|
+
try {
|
|
757
|
+
await session.send("DOM.enable");
|
|
758
|
+
await session.send("DOM.getFlattenedDocument");
|
|
759
|
+
const response = await session.send(
|
|
760
|
+
"DOM.pushNodesByBackendIdsToFrontend",
|
|
761
|
+
{ backendNodeIds: [backendNodeId] },
|
|
762
|
+
);
|
|
763
|
+
nodeId = response.nodeIds[0];
|
|
764
|
+
if (!nodeId) {
|
|
765
|
+
throw new Error(
|
|
766
|
+
`No frontend node for backend node ID ${backendNodeId}`,
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
} catch (error) {
|
|
770
|
+
throw new import_internal.TreeDevDrillError(
|
|
771
|
+
"resolve",
|
|
772
|
+
error,
|
|
773
|
+
backendNodeId,
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
await session.send("DOM.setAttributeValue", {
|
|
778
|
+
nodeId,
|
|
779
|
+
name: attribute,
|
|
780
|
+
value: crypto.randomUUID(),
|
|
781
|
+
});
|
|
782
|
+
set = true;
|
|
783
|
+
} catch (error) {
|
|
784
|
+
throw new import_internal.TreeDevDrillError(
|
|
785
|
+
"probe",
|
|
786
|
+
error,
|
|
787
|
+
backendNodeId,
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
} catch (error) {
|
|
791
|
+
failure = error;
|
|
792
|
+
} finally {
|
|
793
|
+
if (set && nodeId) {
|
|
794
|
+
try {
|
|
795
|
+
await session.send("DOM.removeAttribute", {
|
|
796
|
+
nodeId,
|
|
797
|
+
name: attribute,
|
|
798
|
+
});
|
|
799
|
+
} catch (error) {
|
|
800
|
+
failure ??= new import_internal.TreeDevDrillError(
|
|
801
|
+
"probe",
|
|
802
|
+
error,
|
|
803
|
+
backendNodeId,
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (isOopif) {
|
|
808
|
+
try {
|
|
809
|
+
await session.detach();
|
|
810
|
+
} catch (error) {
|
|
811
|
+
failure ??= new import_internal.TreeDevDrillError(
|
|
812
|
+
"resolve",
|
|
813
|
+
error,
|
|
814
|
+
backendNodeId,
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (failure) throw failure;
|
|
820
|
+
return backendNodeId;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
__legacyDecorateClassTS(
|
|
824
|
+
[
|
|
825
|
+
span(
|
|
826
|
+
"driver.fetch_accessibility_tree",
|
|
827
|
+
import_internal.BaseDriver.spanAttrs,
|
|
828
|
+
),
|
|
829
|
+
],
|
|
830
|
+
PlaywrightDriver.prototype,
|
|
831
|
+
"fetchAccessibilityTree",
|
|
832
|
+
null,
|
|
833
|
+
);
|
|
834
|
+
__legacyDecorateClassTS(
|
|
835
|
+
[span("driver.click", import_internal.BaseDriver.spanAttrs), stateful],
|
|
836
|
+
PlaywrightDriver.prototype,
|
|
837
|
+
"click",
|
|
838
|
+
null,
|
|
839
|
+
);
|
|
840
|
+
__legacyDecorateClassTS(
|
|
841
|
+
[span("driver.drag_slider", import_internal.BaseDriver.spanAttrs), stateful],
|
|
842
|
+
PlaywrightDriver.prototype,
|
|
843
|
+
"dragSlider",
|
|
844
|
+
null,
|
|
845
|
+
);
|
|
846
|
+
__legacyDecorateClassTS(
|
|
847
|
+
[
|
|
848
|
+
span("driver.drag_and_drop", import_internal.BaseDriver.spanAttrs),
|
|
849
|
+
stateful,
|
|
850
|
+
],
|
|
851
|
+
PlaywrightDriver.prototype,
|
|
852
|
+
"dragAndDrop",
|
|
853
|
+
null,
|
|
854
|
+
);
|
|
855
|
+
__legacyDecorateClassTS(
|
|
856
|
+
[span("driver.hover", import_internal.BaseDriver.spanAttrs), stateful],
|
|
857
|
+
PlaywrightDriver.prototype,
|
|
858
|
+
"hover",
|
|
859
|
+
null,
|
|
860
|
+
);
|
|
861
|
+
__legacyDecorateClassTS(
|
|
862
|
+
[span("driver.press_key", import_internal.BaseDriver.spanAttrs), stateful],
|
|
863
|
+
PlaywrightDriver.prototype,
|
|
864
|
+
"pressKey",
|
|
865
|
+
null,
|
|
866
|
+
);
|
|
867
|
+
__legacyDecorateClassTS(
|
|
868
|
+
[span("driver.quit", import_internal.BaseDriver.spanAttrs)],
|
|
869
|
+
PlaywrightDriver.prototype,
|
|
870
|
+
"quit",
|
|
871
|
+
null,
|
|
872
|
+
);
|
|
873
|
+
__legacyDecorateClassTS(
|
|
874
|
+
[span("driver.back", import_internal.BaseDriver.spanAttrs), stateful],
|
|
875
|
+
PlaywrightDriver.prototype,
|
|
876
|
+
"back",
|
|
877
|
+
null,
|
|
878
|
+
);
|
|
879
|
+
__legacyDecorateClassTS(
|
|
880
|
+
[span("driver.visit", import_internal.BaseDriver.spanAttrs), stateful],
|
|
881
|
+
PlaywrightDriver.prototype,
|
|
882
|
+
"visit",
|
|
883
|
+
null,
|
|
884
|
+
);
|
|
885
|
+
__legacyDecorateClassTS(
|
|
886
|
+
[span("driver.scroll_to", import_internal.BaseDriver.spanAttrs), stateful],
|
|
887
|
+
PlaywrightDriver.prototype,
|
|
888
|
+
"scrollTo",
|
|
889
|
+
null,
|
|
890
|
+
);
|
|
891
|
+
__legacyDecorateClassTS(
|
|
892
|
+
[span("driver.screenshot", import_internal.BaseDriver.spanAttrs)],
|
|
893
|
+
PlaywrightDriver.prototype,
|
|
894
|
+
"screenshot",
|
|
895
|
+
null,
|
|
896
|
+
);
|
|
897
|
+
__legacyDecorateClassTS(
|
|
898
|
+
[span("driver.title", import_internal.BaseDriver.spanAttrs)],
|
|
899
|
+
PlaywrightDriver.prototype,
|
|
900
|
+
"title",
|
|
901
|
+
null,
|
|
902
|
+
);
|
|
903
|
+
__legacyDecorateClassTS(
|
|
904
|
+
[span("driver.type", import_internal.BaseDriver.spanAttrs), stateful],
|
|
905
|
+
PlaywrightDriver.prototype,
|
|
906
|
+
"type",
|
|
907
|
+
null,
|
|
908
|
+
);
|
|
909
|
+
__legacyDecorateClassTS(
|
|
910
|
+
[span("driver.upload", import_internal.BaseDriver.spanAttrs), stateful],
|
|
911
|
+
PlaywrightDriver.prototype,
|
|
912
|
+
"upload",
|
|
913
|
+
null,
|
|
914
|
+
);
|
|
915
|
+
__legacyDecorateClassTS(
|
|
916
|
+
[span("driver.url", import_internal.BaseDriver.spanAttrs)],
|
|
917
|
+
PlaywrightDriver.prototype,
|
|
918
|
+
"url",
|
|
919
|
+
null,
|
|
920
|
+
);
|
|
921
|
+
__legacyDecorateClassTS(
|
|
922
|
+
[span("driver.app", import_internal.BaseDriver.spanAttrs)],
|
|
923
|
+
PlaywrightDriver.prototype,
|
|
924
|
+
"app",
|
|
925
|
+
null,
|
|
926
|
+
);
|
|
927
|
+
__legacyDecorateClassTS(
|
|
928
|
+
[span("driver.find_element", import_internal.BaseDriver.spanAttrs)],
|
|
929
|
+
PlaywrightDriver.prototype,
|
|
930
|
+
"findElement",
|
|
931
|
+
null,
|
|
932
|
+
);
|
|
933
|
+
__legacyDecorateClassTS(
|
|
934
|
+
[
|
|
935
|
+
span("driver.execute_script", import_internal.BaseDriver.spanAttrs),
|
|
936
|
+
stateful,
|
|
937
|
+
],
|
|
938
|
+
PlaywrightDriver.prototype,
|
|
939
|
+
"executeScript",
|
|
940
|
+
null,
|
|
941
|
+
);
|
|
942
|
+
__legacyDecorateClassTS(
|
|
943
|
+
[span("driver.print_to_pdf", import_internal.BaseDriver.spanAttrs)],
|
|
944
|
+
PlaywrightDriver.prototype,
|
|
945
|
+
"printToPdf",
|
|
946
|
+
null,
|
|
947
|
+
);
|
|
948
|
+
__legacyDecorateClassTS(
|
|
949
|
+
[span("driver.switch_to_next_tab", import_internal.BaseDriver.spanAttrs)],
|
|
950
|
+
PlaywrightDriver.prototype,
|
|
951
|
+
"switchToNextTab",
|
|
952
|
+
null,
|
|
953
|
+
);
|
|
954
|
+
__legacyDecorateClassTS(
|
|
955
|
+
[span("driver.switch_to_previous_tab", import_internal.BaseDriver.spanAttrs)],
|
|
956
|
+
PlaywrightDriver.prototype,
|
|
957
|
+
"switchToPreviousTab",
|
|
958
|
+
null,
|
|
959
|
+
);
|
|
960
|
+
__legacyDecorateClassTS(
|
|
961
|
+
[stateful("switchToTab")],
|
|
962
|
+
PlaywrightDriver.prototype,
|
|
963
|
+
"switchToTab",
|
|
964
|
+
null,
|
|
965
|
+
);
|
|
966
|
+
__legacyDecorateClassTS(
|
|
967
|
+
[span("driver.wait", import_internal.BaseDriver.spanAttrs), stateful],
|
|
968
|
+
PlaywrightDriver.prototype,
|
|
969
|
+
"wait",
|
|
970
|
+
null,
|
|
971
|
+
);
|
|
972
|
+
__legacyDecorateClassTS(
|
|
973
|
+
[
|
|
974
|
+
span("driver.wait_for_selector", import_internal.BaseDriver.spanAttrs),
|
|
975
|
+
stateful,
|
|
976
|
+
],
|
|
977
|
+
PlaywrightDriver.prototype,
|
|
978
|
+
"waitForSelector",
|
|
979
|
+
null,
|
|
980
|
+
);
|
|
981
|
+
__legacyDecorateClassTS(
|
|
982
|
+
[stateful],
|
|
983
|
+
PlaywrightDriver.prototype,
|
|
984
|
+
"grantPermissions",
|
|
985
|
+
null,
|
|
986
|
+
);
|
|
987
|
+
__legacyDecorateClassTS(
|
|
988
|
+
[span("driver.wait_for_page_to_load", import_internal.BaseDriver.spanAttrs)],
|
|
989
|
+
PlaywrightDriver.prototype,
|
|
990
|
+
"waitForPageToLoad",
|
|
991
|
+
null,
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
// playwright/src/Alumni.ts
|
|
995
|
+
class Alumni extends import_core.Alumni {
|
|
996
|
+
constructor(driver, options = {}) {
|
|
997
|
+
super(new PlaywrightDriver(driver), options);
|
|
998
|
+
}
|
|
999
|
+
static accepts(driver) {
|
|
1000
|
+
return !!driver && typeof driver.context === "function";
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
//# debugId=C00EE79AFCA7353864756E2164756E21
|
|
1005
|
+
//# sourceMappingURL=index.cjs.map
|