@xbrowser/cli 1.1.2 → 1.2.0
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 +5 -3
- package/dist/{browser-ZF4EJ3SK.js → browser-CWI6BXYK.js} +2 -2
- package/dist/{browser-H55TWH2I.js → browser-JP2LFPR2.js} +2 -2
- package/dist/{browser-DZVIVKOA.js → browser-PZX7PO23.js} +1 -1
- package/dist/{cdp-driver-DF5JHA7W.js → cdp-driver-RPUNQBGM.js} +117 -52
- package/dist/{cdp-driver-WWQBRTPF.js → cdp-driver-S5STYUZZ.js} +1 -1
- package/dist/{chunk-OH7CB2P6.js → chunk-6V57JME6.js} +1 -1
- package/dist/{chunk-XVZ6NKRJ.js → chunk-MNFOCOL6.js} +117 -52
- package/dist/{chunk-E5WWMKXB.js → chunk-QFROODUU.js} +117 -52
- package/dist/{chunk-NDAMCPIJ.js → chunk-SLQR57XZ.js} +1 -1
- package/dist/cli.js +122 -13
- package/dist/daemon-main.js +67 -6
- package/dist/index.js +123 -14
- package/dist/{session-replayer-UHITXIOZ.js → session-replayer-IXLSCF5U.js} +1 -1
- package/package.json +2 -2
|
@@ -288,6 +288,22 @@ function sleep2(ms) {
|
|
|
288
288
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
// src/cdp-driver/selector-utils.ts
|
|
292
|
+
function queryJS(selector) {
|
|
293
|
+
if (selector.startsWith("xpath=")) {
|
|
294
|
+
const xpath = JSON.stringify(selector.slice(6));
|
|
295
|
+
return `document.evaluate(${xpath}, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue`;
|
|
296
|
+
}
|
|
297
|
+
return `document.querySelector(${JSON.stringify(selector)})`;
|
|
298
|
+
}
|
|
299
|
+
function queryAllJS(selector) {
|
|
300
|
+
if (selector.startsWith("xpath=")) {
|
|
301
|
+
const xpath = JSON.stringify(selector.slice(6));
|
|
302
|
+
return `(() => { const it = document.evaluate(${xpath}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); const r=[]; for(let i=0;i<it.snapshotLength;i++) r.push(it.snapshotItem(i)); return r; })()`;
|
|
303
|
+
}
|
|
304
|
+
return `document.querySelectorAll(${JSON.stringify(selector)})`;
|
|
305
|
+
}
|
|
306
|
+
|
|
291
307
|
// src/cdp-driver/actionability.ts
|
|
292
308
|
async function waitForActionable(page, selector, opts = {}) {
|
|
293
309
|
const timeout = opts.timeout ?? 3e4;
|
|
@@ -312,7 +328,7 @@ async function waitForActionable(page, selector, opts = {}) {
|
|
|
312
328
|
async function checkActionable(page, selector) {
|
|
313
329
|
const result = await page.evaluate(`
|
|
314
330
|
(function() {
|
|
315
|
-
const el =
|
|
331
|
+
const el = ${queryJS(selector)};
|
|
316
332
|
if (!el) return { ok: false, reason: 'not_found' };
|
|
317
333
|
|
|
318
334
|
// Check attached to DOM
|
|
@@ -355,7 +371,7 @@ async function checkActionable(page, selector) {
|
|
|
355
371
|
async function scrollIntoView(page, selector) {
|
|
356
372
|
await page.evaluate(`
|
|
357
373
|
(function() {
|
|
358
|
-
const el =
|
|
374
|
+
const el = ${queryJS(selector)};
|
|
359
375
|
if (el) el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
|
|
360
376
|
})()
|
|
361
377
|
`);
|
|
@@ -369,13 +385,21 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
369
385
|
this.page = page;
|
|
370
386
|
this.selector = selector;
|
|
371
387
|
}
|
|
388
|
+
/** Resolve selector to a JS expression that finds a single element (CSS or xpath). */
|
|
389
|
+
_q(sel) {
|
|
390
|
+
return queryJS(sel);
|
|
391
|
+
}
|
|
392
|
+
/** Resolve selector to a JS expression that finds all matching elements (CSS or xpath). */
|
|
393
|
+
_qa(sel) {
|
|
394
|
+
return queryAllJS(sel);
|
|
395
|
+
}
|
|
372
396
|
// ── Actions ─────────────────────────────────────────────────
|
|
373
397
|
async click(opts = {}) {
|
|
374
398
|
const { rect } = await waitForActionable(this.page, this.selector, opts);
|
|
375
399
|
await scrollIntoView(this.page, this.selector);
|
|
376
400
|
const updatedRect = await this.page.evaluate(`
|
|
377
401
|
(function() {
|
|
378
|
-
const el =
|
|
402
|
+
const el = ${this._q(this.selector)};
|
|
379
403
|
if (!el) return null;
|
|
380
404
|
const rect = el.getBoundingClientRect();
|
|
381
405
|
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
@@ -395,7 +419,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
395
419
|
await scrollIntoView(this.page, this.selector);
|
|
396
420
|
await this.page.evaluate(`
|
|
397
421
|
(function() {
|
|
398
|
-
const el =
|
|
422
|
+
const el = ${this._q(this.selector)};
|
|
399
423
|
if (!el) throw new Error('Element not found: ${this.selector.replace(/'/g, "\\'")}');
|
|
400
424
|
el.focus();
|
|
401
425
|
el.value = '';
|
|
@@ -405,7 +429,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
405
429
|
await this.page.keyboard.insertText(value);
|
|
406
430
|
await this.page.evaluate(`
|
|
407
431
|
(function() {
|
|
408
|
-
const el =
|
|
432
|
+
const el = ${this._q(this.selector)};
|
|
409
433
|
if (el) {
|
|
410
434
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
411
435
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
@@ -418,7 +442,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
418
442
|
await scrollIntoView(this.page, this.selector);
|
|
419
443
|
await this.page.evaluate(`
|
|
420
444
|
(function() {
|
|
421
|
-
const el =
|
|
445
|
+
const el = ${this._q(this.selector)};
|
|
422
446
|
if (el) el.focus();
|
|
423
447
|
})()
|
|
424
448
|
`);
|
|
@@ -429,7 +453,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
429
453
|
await scrollIntoView(this.page, this.selector);
|
|
430
454
|
await this.page.evaluate(`
|
|
431
455
|
(function() {
|
|
432
|
-
const el =
|
|
456
|
+
const el = ${this._q(this.selector)};
|
|
433
457
|
if (el) el.focus();
|
|
434
458
|
})()
|
|
435
459
|
`);
|
|
@@ -446,7 +470,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
446
470
|
await scrollIntoView(this.page, this.selector);
|
|
447
471
|
const rect = await this.page.evaluate(`
|
|
448
472
|
(function() {
|
|
449
|
-
const el =
|
|
473
|
+
const el = ${this._q(this.selector)};
|
|
450
474
|
if (!el) return { x: 0, y: 0, width: 0, height: 0 };
|
|
451
475
|
const r = el.getBoundingClientRect();
|
|
452
476
|
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
@@ -464,7 +488,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
464
488
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
465
489
|
const isChecked = await this.page.evaluate(`
|
|
466
490
|
(function() {
|
|
467
|
-
const el =
|
|
491
|
+
const el = ${this._q(this.selector)};
|
|
468
492
|
return el?.checked === true;
|
|
469
493
|
})()
|
|
470
494
|
`);
|
|
@@ -476,7 +500,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
476
500
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
477
501
|
const isChecked = await this.page.evaluate(`
|
|
478
502
|
(function() {
|
|
479
|
-
const el =
|
|
503
|
+
const el = ${this._q(this.selector)};
|
|
480
504
|
return el?.checked === true;
|
|
481
505
|
})()
|
|
482
506
|
`);
|
|
@@ -489,7 +513,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
489
513
|
const values = Array.isArray(value) ? value : [value];
|
|
490
514
|
const selected = await this.page.evaluate(`
|
|
491
515
|
(function() {
|
|
492
|
-
const el =
|
|
516
|
+
const el = ${this._q(this.selector)};
|
|
493
517
|
if (!el || el.tagName !== 'SELECT') throw new Error('Not a select element');
|
|
494
518
|
|
|
495
519
|
const values = ${JSON.stringify(values)};
|
|
@@ -517,10 +541,15 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
517
541
|
}
|
|
518
542
|
async screenshot(opts = {}) {
|
|
519
543
|
await waitForActionable(this.page, this.selector);
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
544
|
+
const box = await this.page.evaluate(`
|
|
545
|
+
(function() {
|
|
546
|
+
const el = ${this._q(this.selector)};
|
|
547
|
+
if (!el) return null;
|
|
548
|
+
const r = el.getBoundingClientRect();
|
|
549
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
550
|
+
})()
|
|
551
|
+
`);
|
|
552
|
+
if (!box) throw new Error(`Element not found: ${this.selector}`);
|
|
524
553
|
return this.page.screenshot({
|
|
525
554
|
...opts,
|
|
526
555
|
clip: { x: box.x, y: box.y, width: box.width, height: box.height }
|
|
@@ -532,14 +561,14 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
532
561
|
}
|
|
533
562
|
async count() {
|
|
534
563
|
return this.page.evaluate(`
|
|
535
|
-
|
|
564
|
+
${this._qa(this.selector)}.length
|
|
536
565
|
`);
|
|
537
566
|
}
|
|
538
567
|
async isVisible() {
|
|
539
568
|
try {
|
|
540
569
|
const result = await this.page.evaluate(`
|
|
541
570
|
(function() {
|
|
542
|
-
const el =
|
|
571
|
+
const el = ${this._q(this.selector)};
|
|
543
572
|
if (!el) return false;
|
|
544
573
|
if (!el.isConnected) return false;
|
|
545
574
|
const style = window.getComputedStyle(el);
|
|
@@ -559,7 +588,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
559
588
|
async isEnabled() {
|
|
560
589
|
return this.page.evaluate(`
|
|
561
590
|
(function() {
|
|
562
|
-
const el =
|
|
591
|
+
const el = ${this._q(this.selector)};
|
|
563
592
|
if (!el) return false;
|
|
564
593
|
return !el.disabled;
|
|
565
594
|
})()
|
|
@@ -569,15 +598,20 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
569
598
|
return !await this.isEnabled();
|
|
570
599
|
}
|
|
571
600
|
async boundingBox() {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
601
|
+
return this.page.evaluate(`
|
|
602
|
+
(function() {
|
|
603
|
+
const el = ${this._q(this.selector)};
|
|
604
|
+
if (!el) return null;
|
|
605
|
+
const r = el.getBoundingClientRect();
|
|
606
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
607
|
+
})()
|
|
608
|
+
`);
|
|
575
609
|
}
|
|
576
610
|
// ── Text/HTML ───────────────────────────────────────────────
|
|
577
611
|
async textContent() {
|
|
578
612
|
return this.page.evaluate(`
|
|
579
613
|
(function() {
|
|
580
|
-
const el =
|
|
614
|
+
const el = ${this._q(this.selector)};
|
|
581
615
|
return el?.textContent ?? null;
|
|
582
616
|
})()
|
|
583
617
|
`);
|
|
@@ -585,7 +619,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
585
619
|
async innerText() {
|
|
586
620
|
return this.page.evaluate(`
|
|
587
621
|
(function() {
|
|
588
|
-
const el =
|
|
622
|
+
const el = ${this._q(this.selector)};
|
|
589
623
|
if (!el) throw new Error('Element not found');
|
|
590
624
|
return el.innerText;
|
|
591
625
|
})()
|
|
@@ -594,7 +628,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
594
628
|
async innerHTML() {
|
|
595
629
|
return this.page.evaluate(`
|
|
596
630
|
(function() {
|
|
597
|
-
const el =
|
|
631
|
+
const el = ${this._q(this.selector)};
|
|
598
632
|
if (!el) throw new Error('Element not found');
|
|
599
633
|
return el.innerHTML;
|
|
600
634
|
})()
|
|
@@ -603,7 +637,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
603
637
|
async getAttribute(name) {
|
|
604
638
|
return this.page.evaluate(`
|
|
605
639
|
(function() {
|
|
606
|
-
const el =
|
|
640
|
+
const el = ${this._q(this.selector)};
|
|
607
641
|
return el?.getAttribute(${JSON.stringify(name)}) ?? null;
|
|
608
642
|
})()
|
|
609
643
|
`);
|
|
@@ -611,13 +645,17 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
611
645
|
// ── Evaluate ────────────────────────────────────────────────
|
|
612
646
|
async evaluate(fn, ...args) {
|
|
613
647
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
648
|
+
const sel = JSON.stringify(this.selector);
|
|
649
|
+
const xpathPrefix = this.selector.startsWith("xpath=") ? JSON.stringify(this.selector.slice(6)) : "null";
|
|
614
650
|
return this.page.evaluate(
|
|
615
|
-
`(function(sel, fnStr, ...evalArgs) {
|
|
616
|
-
const el =
|
|
651
|
+
`(function(sel, xpathExpr, fnStr, ...evalArgs) {
|
|
652
|
+
const el = xpathExpr
|
|
653
|
+
? document.evaluate(xpathExpr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
|
|
654
|
+
: document.querySelector(sel);
|
|
617
655
|
if (!el) throw new Error('No element found for selector: ' + sel);
|
|
618
656
|
const fn = new Function('return ' + fnStr)();
|
|
619
657
|
return fn(el, ...evalArgs);
|
|
620
|
-
})(${
|
|
658
|
+
})(${sel}, ${xpathPrefix}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
|
|
621
659
|
);
|
|
622
660
|
}
|
|
623
661
|
async ariaSnapshot() {
|
|
@@ -644,7 +682,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
644
682
|
}
|
|
645
683
|
async all() {
|
|
646
684
|
const n = await this.page.evaluate(`
|
|
647
|
-
|
|
685
|
+
${this._qa(this.selector)}.length
|
|
648
686
|
`);
|
|
649
687
|
const locators = [];
|
|
650
688
|
for (let i = 0; i < n; i++) {
|
|
@@ -655,16 +693,33 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
655
693
|
async focus() {
|
|
656
694
|
await this.page.evaluate(`
|
|
657
695
|
(function() {
|
|
658
|
-
const el =
|
|
696
|
+
const el = ${this._q(this.selector)};
|
|
659
697
|
if (el) el.focus();
|
|
660
698
|
})()
|
|
661
699
|
`);
|
|
662
700
|
}
|
|
663
701
|
};
|
|
664
702
|
var FilteredLocator = class extends XBLocatorImpl {
|
|
703
|
+
index;
|
|
665
704
|
constructor(page, selector, index) {
|
|
666
|
-
const indexedSelector = index === -1 ? `${selector}:last-of-type` : `${selector}:nth-of-type(${index + 1})`;
|
|
705
|
+
const indexedSelector = selector.startsWith("xpath=") ? selector : index === -1 ? `${selector}:last-of-type` : `${selector}:nth-of-type(${index + 1})`;
|
|
667
706
|
super(page, indexedSelector);
|
|
707
|
+
this.index = index;
|
|
708
|
+
this._rawSelector = selector;
|
|
709
|
+
}
|
|
710
|
+
/** Original selector before index filtering */
|
|
711
|
+
_rawSelector;
|
|
712
|
+
/** For xpath selectors, override _q to return the nth element from evaluate results */
|
|
713
|
+
_q(sel) {
|
|
714
|
+
if (!sel.startsWith("xpath=")) return super._q(sel);
|
|
715
|
+
const xpath = JSON.stringify(this._rawSelector.slice(6));
|
|
716
|
+
return `(() => { const it = document.evaluate(${xpath}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); return ${this.index === -1 ? "it.snapshotItem(it.snapshotLength - 1)" : `it.snapshotItem(${this.index})`}; })()`;
|
|
717
|
+
}
|
|
718
|
+
/** For xpath selectors, override _qa to return all matching elements from evaluate */
|
|
719
|
+
_qa(sel) {
|
|
720
|
+
if (!sel.startsWith("xpath=")) return super._qa(sel);
|
|
721
|
+
const xpath = JSON.stringify(this._rawSelector.slice(6));
|
|
722
|
+
return `(() => { const it = document.evaluate(${xpath}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); const r=[]; for(let i=0;i<it.snapshotLength;i++) r.push(it.snapshotItem(i)); return r; })()`;
|
|
668
723
|
}
|
|
669
724
|
};
|
|
670
725
|
var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
@@ -672,7 +727,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
672
727
|
const tag = `data-xb-vt-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
673
728
|
const found = await this.page.evaluate(`
|
|
674
729
|
(function() {
|
|
675
|
-
const els =
|
|
730
|
+
const els = ${this._qa(this.selector)};
|
|
676
731
|
for (const el of els) {
|
|
677
732
|
if (!el.isConnected) continue;
|
|
678
733
|
const style = window.getComputedStyle(el);
|
|
@@ -690,7 +745,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
690
745
|
return await fn(`[${tag}]`);
|
|
691
746
|
} finally {
|
|
692
747
|
await this.page.evaluate(`
|
|
693
|
-
|
|
748
|
+
${this._qa(`[${tag}]`)}.forEach(el => el.removeAttribute(${JSON.stringify(tag)}))
|
|
694
749
|
`);
|
|
695
750
|
}
|
|
696
751
|
}
|
|
@@ -710,7 +765,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
710
765
|
return this.page.evaluate(`
|
|
711
766
|
(function() {
|
|
712
767
|
let count = 0;
|
|
713
|
-
const els =
|
|
768
|
+
const els = ${this._qa(this.selector)};
|
|
714
769
|
for (const el of els) {
|
|
715
770
|
if (!el.isConnected) continue;
|
|
716
771
|
const style = window.getComputedStyle(el);
|
|
@@ -727,7 +782,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
727
782
|
try {
|
|
728
783
|
const result = await this.page.evaluate(`
|
|
729
784
|
(function() {
|
|
730
|
-
const els =
|
|
785
|
+
const els = ${this._qa(this.selector)};
|
|
731
786
|
for (const el of els) {
|
|
732
787
|
if (!el.isConnected) continue;
|
|
733
788
|
const style = window.getComputedStyle(el);
|
|
@@ -1172,20 +1227,20 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1172
1227
|
const deadline = Date.now() + timeout;
|
|
1173
1228
|
while (Date.now() < deadline) {
|
|
1174
1229
|
const exists = await this.evaluate(
|
|
1175
|
-
`(function() { const el =
|
|
1230
|
+
`(function() { const el = ${queryJS(selector)}; return !!el; })()`
|
|
1176
1231
|
);
|
|
1177
1232
|
if (state === "attached" && exists) return;
|
|
1178
1233
|
if (state === "detached" && !exists) return;
|
|
1179
1234
|
if (state === "visible" && exists) {
|
|
1180
1235
|
const visible = await this.evaluate(
|
|
1181
|
-
`(function() { const el =
|
|
1236
|
+
`(function() { const el = ${queryJS(selector)}; if (!el) return false; const rect = el.getBoundingClientRect(); const style = window.getComputedStyle(el); return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none'; })()`
|
|
1182
1237
|
);
|
|
1183
1238
|
if (visible) return;
|
|
1184
1239
|
}
|
|
1185
1240
|
if (state === "hidden") {
|
|
1186
1241
|
if (!exists) return;
|
|
1187
1242
|
const visible = await this.evaluate(
|
|
1188
|
-
`(function() { const el =
|
|
1243
|
+
`(function() { const el = ${queryJS(selector)}; if (!el) return false; const rect = el.getBoundingClientRect(); const style = window.getComputedStyle(el); return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none'; })()`
|
|
1189
1244
|
);
|
|
1190
1245
|
if (!visible) return;
|
|
1191
1246
|
}
|
|
@@ -1228,6 +1283,8 @@ Last error: ${lastError.message}` : "";
|
|
|
1228
1283
|
// ── Evaluation ──────────────────────────────────────────────
|
|
1229
1284
|
async evaluate(fn, ...args) {
|
|
1230
1285
|
if (this._closed) throw new Error("Page is closed");
|
|
1286
|
+
this.conn.send("Page.handleJavaScriptDialog", { accept: false }, this.sessionId).catch(() => {
|
|
1287
|
+
});
|
|
1231
1288
|
let expression;
|
|
1232
1289
|
if (typeof fn === "string") {
|
|
1233
1290
|
expression = fn;
|
|
@@ -1267,23 +1324,31 @@ Last error: ${lastError.message}` : "";
|
|
|
1267
1324
|
}
|
|
1268
1325
|
async $eval(selector, fn, ...args) {
|
|
1269
1326
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
1327
|
+
const selJSON = JSON.stringify(selector);
|
|
1328
|
+
const xpathPrefix = selector.startsWith("xpath=") ? JSON.stringify(selector.slice(6)) : "null";
|
|
1270
1329
|
return this.evaluate(
|
|
1271
|
-
`(function(sel, fnStr, ...evalArgs) {
|
|
1272
|
-
const el =
|
|
1330
|
+
`(function(sel, xpathExpr, fnStr, ...evalArgs) {
|
|
1331
|
+
const el = xpathExpr
|
|
1332
|
+
? document.evaluate(xpathExpr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
|
|
1333
|
+
: document.querySelector(sel);
|
|
1273
1334
|
if (!el) throw new Error('No element found for selector: ' + sel);
|
|
1274
1335
|
const fn = new Function('return ' + fnStr)();
|
|
1275
1336
|
return fn(el, ...evalArgs);
|
|
1276
|
-
})(${
|
|
1337
|
+
})(${selJSON}, ${xpathPrefix}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
|
|
1277
1338
|
);
|
|
1278
1339
|
}
|
|
1279
1340
|
async $$eval(selector, fn, ...args) {
|
|
1280
1341
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
1342
|
+
const selJSON = JSON.stringify(selector);
|
|
1343
|
+
const xpathPrefix = selector.startsWith("xpath=") ? JSON.stringify(selector.slice(6)) : "null";
|
|
1281
1344
|
return this.evaluate(
|
|
1282
|
-
`(function(sel, fnStr, ...evalArgs) {
|
|
1283
|
-
const els =
|
|
1345
|
+
`(function(sel, xpathExpr, fnStr, ...evalArgs) {
|
|
1346
|
+
const els = xpathExpr
|
|
1347
|
+
? (() => { const it = document.evaluate(xpathExpr, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); const r=[]; for(let i=0;i<it.snapshotLength;i++) r.push(it.snapshotItem(i)); return r; })()
|
|
1348
|
+
: Array.from(document.querySelectorAll(sel));
|
|
1284
1349
|
const fn = new Function('return ' + fnStr)();
|
|
1285
1350
|
return fn(els, ...evalArgs);
|
|
1286
|
-
})(${
|
|
1351
|
+
})(${selJSON}, ${xpathPrefix}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
|
|
1287
1352
|
);
|
|
1288
1353
|
}
|
|
1289
1354
|
// ── Locator ─────────────────────────────────────────────────
|
|
@@ -1356,22 +1421,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1356
1421
|
// ── Convenience selectors ───────────────────────────────────
|
|
1357
1422
|
async textContent(selector) {
|
|
1358
1423
|
return this.evaluate(
|
|
1359
|
-
`(function() { const el =
|
|
1424
|
+
`(function() { const el = ${queryJS(selector)}; return el?.textContent ?? null; })()`
|
|
1360
1425
|
);
|
|
1361
1426
|
}
|
|
1362
1427
|
async innerText(selector) {
|
|
1363
1428
|
return this.evaluate(
|
|
1364
|
-
`(function() { const el =
|
|
1429
|
+
`(function() { const el = ${queryJS(selector)}; if (!el) throw new Error('Element not found'); return el.innerText; })()`
|
|
1365
1430
|
);
|
|
1366
1431
|
}
|
|
1367
1432
|
async innerHTML(selector) {
|
|
1368
1433
|
return this.evaluate(
|
|
1369
|
-
`(function() { const el =
|
|
1434
|
+
`(function() { const el = ${queryJS(selector)}; if (!el) throw new Error('Element not found'); return el.innerHTML; })()`
|
|
1370
1435
|
);
|
|
1371
1436
|
}
|
|
1372
1437
|
async getAttribute(selector, name) {
|
|
1373
1438
|
return this.evaluate(
|
|
1374
|
-
`(function() { const el =
|
|
1439
|
+
`(function() { const el = ${queryJS(selector)}; return el?.getAttribute(${JSON.stringify(name)}) ?? null; })()`
|
|
1375
1440
|
);
|
|
1376
1441
|
}
|
|
1377
1442
|
// ── Query ───────────────────────────────────────────────────
|
|
@@ -1677,7 +1742,7 @@ Last error: ${lastError.message}` : "";
|
|
|
1677
1742
|
setTimeout(() => {
|
|
1678
1743
|
this.conn.send("Page.handleJavaScriptDialog", { accept: false }, this.sessionId).catch(() => {
|
|
1679
1744
|
});
|
|
1680
|
-
},
|
|
1745
|
+
}, 0);
|
|
1681
1746
|
})
|
|
1682
1747
|
);
|
|
1683
1748
|
this._subscriptions.push(
|
|
@@ -1992,7 +2057,7 @@ Last error: ${lastError.message}` : "";
|
|
|
1992
2057
|
await this.evaluate(`
|
|
1993
2058
|
(function() {
|
|
1994
2059
|
var selector = ${JSON.stringify(selector)};
|
|
1995
|
-
var input =
|
|
2060
|
+
var input = ${queryJS(selector)};
|
|
1996
2061
|
if (!input) throw new Error('Element not found: ' + selector);
|
|
1997
2062
|
|
|
1998
2063
|
var fileList = ${JSON.stringify(fileList)};
|
|
@@ -2017,7 +2082,7 @@ Last error: ${lastError.message}` : "";
|
|
|
2017
2082
|
async dragAndDrop(source, target) {
|
|
2018
2083
|
const sourceRect = await this.evaluate(`
|
|
2019
2084
|
(function() {
|
|
2020
|
-
const el =
|
|
2085
|
+
const el = ${queryJS(source)};
|
|
2021
2086
|
if (!el) throw new Error('Source not found: ${source}');
|
|
2022
2087
|
el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
2023
2088
|
const r = el.getBoundingClientRect();
|
|
@@ -2026,7 +2091,7 @@ Last error: ${lastError.message}` : "";
|
|
|
2026
2091
|
`);
|
|
2027
2092
|
const targetRect = await this.evaluate(`
|
|
2028
2093
|
(function() {
|
|
2029
|
-
const el =
|
|
2094
|
+
const el = ${queryJS(target)};
|
|
2030
2095
|
if (!el) throw new Error('Target not found: ${target}');
|
|
2031
2096
|
el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
2032
2097
|
const r = el.getBoundingClientRect();
|