@xbrowser/cli 1.1.2 → 1.2.1
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 +123 -13
- package/dist/daemon-main.js +68 -6
- package/dist/index.js +124 -14
- package/dist/{session-replayer-UHITXIOZ.js → session-replayer-IXLSCF5U.js} +1 -1
- package/package.json +2 -2
|
@@ -282,6 +282,22 @@ function sleep2(ms) {
|
|
|
282
282
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
+
// src/cdp-driver/selector-utils.ts
|
|
286
|
+
function queryJS(selector) {
|
|
287
|
+
if (selector.startsWith("xpath=")) {
|
|
288
|
+
const xpath = JSON.stringify(selector.slice(6));
|
|
289
|
+
return `document.evaluate(${xpath}, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue`;
|
|
290
|
+
}
|
|
291
|
+
return `document.querySelector(${JSON.stringify(selector)})`;
|
|
292
|
+
}
|
|
293
|
+
function queryAllJS(selector) {
|
|
294
|
+
if (selector.startsWith("xpath=")) {
|
|
295
|
+
const xpath = JSON.stringify(selector.slice(6));
|
|
296
|
+
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; })()`;
|
|
297
|
+
}
|
|
298
|
+
return `document.querySelectorAll(${JSON.stringify(selector)})`;
|
|
299
|
+
}
|
|
300
|
+
|
|
285
301
|
// src/cdp-driver/actionability.ts
|
|
286
302
|
async function waitForActionable(page, selector, opts = {}) {
|
|
287
303
|
const timeout = opts.timeout ?? 3e4;
|
|
@@ -306,7 +322,7 @@ async function waitForActionable(page, selector, opts = {}) {
|
|
|
306
322
|
async function checkActionable(page, selector) {
|
|
307
323
|
const result = await page.evaluate(`
|
|
308
324
|
(function() {
|
|
309
|
-
const el =
|
|
325
|
+
const el = ${queryJS(selector)};
|
|
310
326
|
if (!el) return { ok: false, reason: 'not_found' };
|
|
311
327
|
|
|
312
328
|
// Check attached to DOM
|
|
@@ -349,7 +365,7 @@ async function checkActionable(page, selector) {
|
|
|
349
365
|
async function scrollIntoView(page, selector) {
|
|
350
366
|
await page.evaluate(`
|
|
351
367
|
(function() {
|
|
352
|
-
const el =
|
|
368
|
+
const el = ${queryJS(selector)};
|
|
353
369
|
if (el) el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
|
|
354
370
|
})()
|
|
355
371
|
`);
|
|
@@ -363,13 +379,21 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
363
379
|
this.page = page;
|
|
364
380
|
this.selector = selector;
|
|
365
381
|
}
|
|
382
|
+
/** Resolve selector to a JS expression that finds a single element (CSS or xpath). */
|
|
383
|
+
_q(sel) {
|
|
384
|
+
return queryJS(sel);
|
|
385
|
+
}
|
|
386
|
+
/** Resolve selector to a JS expression that finds all matching elements (CSS or xpath). */
|
|
387
|
+
_qa(sel) {
|
|
388
|
+
return queryAllJS(sel);
|
|
389
|
+
}
|
|
366
390
|
// ── Actions ─────────────────────────────────────────────────
|
|
367
391
|
async click(opts = {}) {
|
|
368
392
|
const { rect } = await waitForActionable(this.page, this.selector, opts);
|
|
369
393
|
await scrollIntoView(this.page, this.selector);
|
|
370
394
|
const updatedRect = await this.page.evaluate(`
|
|
371
395
|
(function() {
|
|
372
|
-
const el =
|
|
396
|
+
const el = ${this._q(this.selector)};
|
|
373
397
|
if (!el) return null;
|
|
374
398
|
const rect = el.getBoundingClientRect();
|
|
375
399
|
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
@@ -389,7 +413,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
389
413
|
await scrollIntoView(this.page, this.selector);
|
|
390
414
|
await this.page.evaluate(`
|
|
391
415
|
(function() {
|
|
392
|
-
const el =
|
|
416
|
+
const el = ${this._q(this.selector)};
|
|
393
417
|
if (!el) throw new Error('Element not found: ${this.selector.replace(/'/g, "\\'")}');
|
|
394
418
|
el.focus();
|
|
395
419
|
el.value = '';
|
|
@@ -399,7 +423,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
399
423
|
await this.page.keyboard.insertText(value);
|
|
400
424
|
await this.page.evaluate(`
|
|
401
425
|
(function() {
|
|
402
|
-
const el =
|
|
426
|
+
const el = ${this._q(this.selector)};
|
|
403
427
|
if (el) {
|
|
404
428
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
405
429
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
@@ -412,7 +436,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
412
436
|
await scrollIntoView(this.page, this.selector);
|
|
413
437
|
await this.page.evaluate(`
|
|
414
438
|
(function() {
|
|
415
|
-
const el =
|
|
439
|
+
const el = ${this._q(this.selector)};
|
|
416
440
|
if (el) el.focus();
|
|
417
441
|
})()
|
|
418
442
|
`);
|
|
@@ -423,7 +447,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
423
447
|
await scrollIntoView(this.page, this.selector);
|
|
424
448
|
await this.page.evaluate(`
|
|
425
449
|
(function() {
|
|
426
|
-
const el =
|
|
450
|
+
const el = ${this._q(this.selector)};
|
|
427
451
|
if (el) el.focus();
|
|
428
452
|
})()
|
|
429
453
|
`);
|
|
@@ -440,7 +464,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
440
464
|
await scrollIntoView(this.page, this.selector);
|
|
441
465
|
const rect = await this.page.evaluate(`
|
|
442
466
|
(function() {
|
|
443
|
-
const el =
|
|
467
|
+
const el = ${this._q(this.selector)};
|
|
444
468
|
if (!el) return { x: 0, y: 0, width: 0, height: 0 };
|
|
445
469
|
const r = el.getBoundingClientRect();
|
|
446
470
|
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
@@ -458,7 +482,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
458
482
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
459
483
|
const isChecked = await this.page.evaluate(`
|
|
460
484
|
(function() {
|
|
461
|
-
const el =
|
|
485
|
+
const el = ${this._q(this.selector)};
|
|
462
486
|
return el?.checked === true;
|
|
463
487
|
})()
|
|
464
488
|
`);
|
|
@@ -470,7 +494,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
470
494
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
471
495
|
const isChecked = await this.page.evaluate(`
|
|
472
496
|
(function() {
|
|
473
|
-
const el =
|
|
497
|
+
const el = ${this._q(this.selector)};
|
|
474
498
|
return el?.checked === true;
|
|
475
499
|
})()
|
|
476
500
|
`);
|
|
@@ -483,7 +507,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
483
507
|
const values = Array.isArray(value) ? value : [value];
|
|
484
508
|
const selected = await this.page.evaluate(`
|
|
485
509
|
(function() {
|
|
486
|
-
const el =
|
|
510
|
+
const el = ${this._q(this.selector)};
|
|
487
511
|
if (!el || el.tagName !== 'SELECT') throw new Error('Not a select element');
|
|
488
512
|
|
|
489
513
|
const values = ${JSON.stringify(values)};
|
|
@@ -511,10 +535,15 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
511
535
|
}
|
|
512
536
|
async screenshot(opts = {}) {
|
|
513
537
|
await waitForActionable(this.page, this.selector);
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
538
|
+
const box = await this.page.evaluate(`
|
|
539
|
+
(function() {
|
|
540
|
+
const el = ${this._q(this.selector)};
|
|
541
|
+
if (!el) return null;
|
|
542
|
+
const r = el.getBoundingClientRect();
|
|
543
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
544
|
+
})()
|
|
545
|
+
`);
|
|
546
|
+
if (!box) throw new Error(`Element not found: ${this.selector}`);
|
|
518
547
|
return this.page.screenshot({
|
|
519
548
|
...opts,
|
|
520
549
|
clip: { x: box.x, y: box.y, width: box.width, height: box.height }
|
|
@@ -526,14 +555,14 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
526
555
|
}
|
|
527
556
|
async count() {
|
|
528
557
|
return this.page.evaluate(`
|
|
529
|
-
|
|
558
|
+
${this._qa(this.selector)}.length
|
|
530
559
|
`);
|
|
531
560
|
}
|
|
532
561
|
async isVisible() {
|
|
533
562
|
try {
|
|
534
563
|
const result = await this.page.evaluate(`
|
|
535
564
|
(function() {
|
|
536
|
-
const el =
|
|
565
|
+
const el = ${this._q(this.selector)};
|
|
537
566
|
if (!el) return false;
|
|
538
567
|
if (!el.isConnected) return false;
|
|
539
568
|
const style = window.getComputedStyle(el);
|
|
@@ -553,7 +582,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
553
582
|
async isEnabled() {
|
|
554
583
|
return this.page.evaluate(`
|
|
555
584
|
(function() {
|
|
556
|
-
const el =
|
|
585
|
+
const el = ${this._q(this.selector)};
|
|
557
586
|
if (!el) return false;
|
|
558
587
|
return !el.disabled;
|
|
559
588
|
})()
|
|
@@ -563,15 +592,20 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
563
592
|
return !await this.isEnabled();
|
|
564
593
|
}
|
|
565
594
|
async boundingBox() {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
595
|
+
return this.page.evaluate(`
|
|
596
|
+
(function() {
|
|
597
|
+
const el = ${this._q(this.selector)};
|
|
598
|
+
if (!el) return null;
|
|
599
|
+
const r = el.getBoundingClientRect();
|
|
600
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
601
|
+
})()
|
|
602
|
+
`);
|
|
569
603
|
}
|
|
570
604
|
// ── Text/HTML ───────────────────────────────────────────────
|
|
571
605
|
async textContent() {
|
|
572
606
|
return this.page.evaluate(`
|
|
573
607
|
(function() {
|
|
574
|
-
const el =
|
|
608
|
+
const el = ${this._q(this.selector)};
|
|
575
609
|
return el?.textContent ?? null;
|
|
576
610
|
})()
|
|
577
611
|
`);
|
|
@@ -579,7 +613,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
579
613
|
async innerText() {
|
|
580
614
|
return this.page.evaluate(`
|
|
581
615
|
(function() {
|
|
582
|
-
const el =
|
|
616
|
+
const el = ${this._q(this.selector)};
|
|
583
617
|
if (!el) throw new Error('Element not found');
|
|
584
618
|
return el.innerText;
|
|
585
619
|
})()
|
|
@@ -588,7 +622,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
588
622
|
async innerHTML() {
|
|
589
623
|
return this.page.evaluate(`
|
|
590
624
|
(function() {
|
|
591
|
-
const el =
|
|
625
|
+
const el = ${this._q(this.selector)};
|
|
592
626
|
if (!el) throw new Error('Element not found');
|
|
593
627
|
return el.innerHTML;
|
|
594
628
|
})()
|
|
@@ -597,7 +631,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
597
631
|
async getAttribute(name) {
|
|
598
632
|
return this.page.evaluate(`
|
|
599
633
|
(function() {
|
|
600
|
-
const el =
|
|
634
|
+
const el = ${this._q(this.selector)};
|
|
601
635
|
return el?.getAttribute(${JSON.stringify(name)}) ?? null;
|
|
602
636
|
})()
|
|
603
637
|
`);
|
|
@@ -605,13 +639,17 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
605
639
|
// ── Evaluate ────────────────────────────────────────────────
|
|
606
640
|
async evaluate(fn, ...args) {
|
|
607
641
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
642
|
+
const sel = JSON.stringify(this.selector);
|
|
643
|
+
const xpathPrefix = this.selector.startsWith("xpath=") ? JSON.stringify(this.selector.slice(6)) : "null";
|
|
608
644
|
return this.page.evaluate(
|
|
609
|
-
`(function(sel, fnStr, ...evalArgs) {
|
|
610
|
-
const el =
|
|
645
|
+
`(function(sel, xpathExpr, fnStr, ...evalArgs) {
|
|
646
|
+
const el = xpathExpr
|
|
647
|
+
? document.evaluate(xpathExpr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
|
|
648
|
+
: document.querySelector(sel);
|
|
611
649
|
if (!el) throw new Error('No element found for selector: ' + sel);
|
|
612
650
|
const fn = new Function('return ' + fnStr)();
|
|
613
651
|
return fn(el, ...evalArgs);
|
|
614
|
-
})(${
|
|
652
|
+
})(${sel}, ${xpathPrefix}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
|
|
615
653
|
);
|
|
616
654
|
}
|
|
617
655
|
async ariaSnapshot() {
|
|
@@ -638,7 +676,7 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
638
676
|
}
|
|
639
677
|
async all() {
|
|
640
678
|
const n = await this.page.evaluate(`
|
|
641
|
-
|
|
679
|
+
${this._qa(this.selector)}.length
|
|
642
680
|
`);
|
|
643
681
|
const locators = [];
|
|
644
682
|
for (let i = 0; i < n; i++) {
|
|
@@ -649,16 +687,33 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
649
687
|
async focus() {
|
|
650
688
|
await this.page.evaluate(`
|
|
651
689
|
(function() {
|
|
652
|
-
const el =
|
|
690
|
+
const el = ${this._q(this.selector)};
|
|
653
691
|
if (el) el.focus();
|
|
654
692
|
})()
|
|
655
693
|
`);
|
|
656
694
|
}
|
|
657
695
|
};
|
|
658
696
|
var FilteredLocator = class extends XBLocatorImpl {
|
|
697
|
+
index;
|
|
659
698
|
constructor(page, selector, index) {
|
|
660
|
-
const indexedSelector = index === -1 ? `${selector}:last-of-type` : `${selector}:nth-of-type(${index + 1})`;
|
|
699
|
+
const indexedSelector = selector.startsWith("xpath=") ? selector : index === -1 ? `${selector}:last-of-type` : `${selector}:nth-of-type(${index + 1})`;
|
|
661
700
|
super(page, indexedSelector);
|
|
701
|
+
this.index = index;
|
|
702
|
+
this._rawSelector = selector;
|
|
703
|
+
}
|
|
704
|
+
/** Original selector before index filtering */
|
|
705
|
+
_rawSelector;
|
|
706
|
+
/** For xpath selectors, override _q to return the nth element from evaluate results */
|
|
707
|
+
_q(sel) {
|
|
708
|
+
if (!sel.startsWith("xpath=")) return super._q(sel);
|
|
709
|
+
const xpath = JSON.stringify(this._rawSelector.slice(6));
|
|
710
|
+
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})`}; })()`;
|
|
711
|
+
}
|
|
712
|
+
/** For xpath selectors, override _qa to return all matching elements from evaluate */
|
|
713
|
+
_qa(sel) {
|
|
714
|
+
if (!sel.startsWith("xpath=")) return super._qa(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); const r=[]; for(let i=0;i<it.snapshotLength;i++) r.push(it.snapshotItem(i)); return r; })()`;
|
|
662
717
|
}
|
|
663
718
|
};
|
|
664
719
|
var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
@@ -666,7 +721,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
666
721
|
const tag = `data-xb-vt-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
667
722
|
const found = await this.page.evaluate(`
|
|
668
723
|
(function() {
|
|
669
|
-
const els =
|
|
724
|
+
const els = ${this._qa(this.selector)};
|
|
670
725
|
for (const el of els) {
|
|
671
726
|
if (!el.isConnected) continue;
|
|
672
727
|
const style = window.getComputedStyle(el);
|
|
@@ -684,7 +739,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
684
739
|
return await fn(`[${tag}]`);
|
|
685
740
|
} finally {
|
|
686
741
|
await this.page.evaluate(`
|
|
687
|
-
|
|
742
|
+
${this._qa(`[${tag}]`)}.forEach(el => el.removeAttribute(${JSON.stringify(tag)}))
|
|
688
743
|
`);
|
|
689
744
|
}
|
|
690
745
|
}
|
|
@@ -704,7 +759,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
704
759
|
return this.page.evaluate(`
|
|
705
760
|
(function() {
|
|
706
761
|
let count = 0;
|
|
707
|
-
const els =
|
|
762
|
+
const els = ${this._qa(this.selector)};
|
|
708
763
|
for (const el of els) {
|
|
709
764
|
if (!el.isConnected) continue;
|
|
710
765
|
const style = window.getComputedStyle(el);
|
|
@@ -721,7 +776,7 @@ var VisibleFilteredLocator = class extends XBLocatorImpl {
|
|
|
721
776
|
try {
|
|
722
777
|
const result = await this.page.evaluate(`
|
|
723
778
|
(function() {
|
|
724
|
-
const els =
|
|
779
|
+
const els = ${this._qa(this.selector)};
|
|
725
780
|
for (const el of els) {
|
|
726
781
|
if (!el.isConnected) continue;
|
|
727
782
|
const style = window.getComputedStyle(el);
|
|
@@ -1166,20 +1221,20 @@ var XBPageImpl = class _XBPageImpl {
|
|
|
1166
1221
|
const deadline = Date.now() + timeout;
|
|
1167
1222
|
while (Date.now() < deadline) {
|
|
1168
1223
|
const exists = await this.evaluate(
|
|
1169
|
-
`(function() { const el =
|
|
1224
|
+
`(function() { const el = ${queryJS(selector)}; return !!el; })()`
|
|
1170
1225
|
);
|
|
1171
1226
|
if (state === "attached" && exists) return;
|
|
1172
1227
|
if (state === "detached" && !exists) return;
|
|
1173
1228
|
if (state === "visible" && exists) {
|
|
1174
1229
|
const visible = await this.evaluate(
|
|
1175
|
-
`(function() { const el =
|
|
1230
|
+
`(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'; })()`
|
|
1176
1231
|
);
|
|
1177
1232
|
if (visible) return;
|
|
1178
1233
|
}
|
|
1179
1234
|
if (state === "hidden") {
|
|
1180
1235
|
if (!exists) return;
|
|
1181
1236
|
const visible = await this.evaluate(
|
|
1182
|
-
`(function() { const el =
|
|
1237
|
+
`(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'; })()`
|
|
1183
1238
|
);
|
|
1184
1239
|
if (!visible) return;
|
|
1185
1240
|
}
|
|
@@ -1222,6 +1277,8 @@ Last error: ${lastError.message}` : "";
|
|
|
1222
1277
|
// ── Evaluation ──────────────────────────────────────────────
|
|
1223
1278
|
async evaluate(fn, ...args) {
|
|
1224
1279
|
if (this._closed) throw new Error("Page is closed");
|
|
1280
|
+
this.conn.send("Page.handleJavaScriptDialog", { accept: false }, this.sessionId).catch(() => {
|
|
1281
|
+
});
|
|
1225
1282
|
let expression;
|
|
1226
1283
|
if (typeof fn === "string") {
|
|
1227
1284
|
expression = fn;
|
|
@@ -1261,23 +1318,31 @@ Last error: ${lastError.message}` : "";
|
|
|
1261
1318
|
}
|
|
1262
1319
|
async $eval(selector, fn, ...args) {
|
|
1263
1320
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
1321
|
+
const selJSON = JSON.stringify(selector);
|
|
1322
|
+
const xpathPrefix = selector.startsWith("xpath=") ? JSON.stringify(selector.slice(6)) : "null";
|
|
1264
1323
|
return this.evaluate(
|
|
1265
|
-
`(function(sel, fnStr, ...evalArgs) {
|
|
1266
|
-
const el =
|
|
1324
|
+
`(function(sel, xpathExpr, fnStr, ...evalArgs) {
|
|
1325
|
+
const el = xpathExpr
|
|
1326
|
+
? document.evaluate(xpathExpr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
|
|
1327
|
+
: document.querySelector(sel);
|
|
1267
1328
|
if (!el) throw new Error('No element found for selector: ' + sel);
|
|
1268
1329
|
const fn = new Function('return ' + fnStr)();
|
|
1269
1330
|
return fn(el, ...evalArgs);
|
|
1270
|
-
})(${
|
|
1331
|
+
})(${selJSON}, ${xpathPrefix}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
|
|
1271
1332
|
);
|
|
1272
1333
|
}
|
|
1273
1334
|
async $$eval(selector, fn, ...args) {
|
|
1274
1335
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
1336
|
+
const selJSON = JSON.stringify(selector);
|
|
1337
|
+
const xpathPrefix = selector.startsWith("xpath=") ? JSON.stringify(selector.slice(6)) : "null";
|
|
1275
1338
|
return this.evaluate(
|
|
1276
|
-
`(function(sel, fnStr, ...evalArgs) {
|
|
1277
|
-
const els =
|
|
1339
|
+
`(function(sel, xpathExpr, fnStr, ...evalArgs) {
|
|
1340
|
+
const els = xpathExpr
|
|
1341
|
+
? (() => { 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; })()
|
|
1342
|
+
: Array.from(document.querySelectorAll(sel));
|
|
1278
1343
|
const fn = new Function('return ' + fnStr)();
|
|
1279
1344
|
return fn(els, ...evalArgs);
|
|
1280
|
-
})(${
|
|
1345
|
+
})(${selJSON}, ${xpathPrefix}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
|
|
1281
1346
|
);
|
|
1282
1347
|
}
|
|
1283
1348
|
// ── Locator ─────────────────────────────────────────────────
|
|
@@ -1350,22 +1415,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1350
1415
|
// ── Convenience selectors ───────────────────────────────────
|
|
1351
1416
|
async textContent(selector) {
|
|
1352
1417
|
return this.evaluate(
|
|
1353
|
-
`(function() { const el =
|
|
1418
|
+
`(function() { const el = ${queryJS(selector)}; return el?.textContent ?? null; })()`
|
|
1354
1419
|
);
|
|
1355
1420
|
}
|
|
1356
1421
|
async innerText(selector) {
|
|
1357
1422
|
return this.evaluate(
|
|
1358
|
-
`(function() { const el =
|
|
1423
|
+
`(function() { const el = ${queryJS(selector)}; if (!el) throw new Error('Element not found'); return el.innerText; })()`
|
|
1359
1424
|
);
|
|
1360
1425
|
}
|
|
1361
1426
|
async innerHTML(selector) {
|
|
1362
1427
|
return this.evaluate(
|
|
1363
|
-
`(function() { const el =
|
|
1428
|
+
`(function() { const el = ${queryJS(selector)}; if (!el) throw new Error('Element not found'); return el.innerHTML; })()`
|
|
1364
1429
|
);
|
|
1365
1430
|
}
|
|
1366
1431
|
async getAttribute(selector, name) {
|
|
1367
1432
|
return this.evaluate(
|
|
1368
|
-
`(function() { const el =
|
|
1433
|
+
`(function() { const el = ${queryJS(selector)}; return el?.getAttribute(${JSON.stringify(name)}) ?? null; })()`
|
|
1369
1434
|
);
|
|
1370
1435
|
}
|
|
1371
1436
|
// ── Query ───────────────────────────────────────────────────
|
|
@@ -1671,7 +1736,7 @@ Last error: ${lastError.message}` : "";
|
|
|
1671
1736
|
setTimeout(() => {
|
|
1672
1737
|
this.conn.send("Page.handleJavaScriptDialog", { accept: false }, this.sessionId).catch(() => {
|
|
1673
1738
|
});
|
|
1674
|
-
},
|
|
1739
|
+
}, 0);
|
|
1675
1740
|
})
|
|
1676
1741
|
);
|
|
1677
1742
|
this._subscriptions.push(
|
|
@@ -1986,7 +2051,7 @@ Last error: ${lastError.message}` : "";
|
|
|
1986
2051
|
await this.evaluate(`
|
|
1987
2052
|
(function() {
|
|
1988
2053
|
var selector = ${JSON.stringify(selector)};
|
|
1989
|
-
var input =
|
|
2054
|
+
var input = ${queryJS(selector)};
|
|
1990
2055
|
if (!input) throw new Error('Element not found: ' + selector);
|
|
1991
2056
|
|
|
1992
2057
|
var fileList = ${JSON.stringify(fileList)};
|
|
@@ -2011,7 +2076,7 @@ Last error: ${lastError.message}` : "";
|
|
|
2011
2076
|
async dragAndDrop(source, target) {
|
|
2012
2077
|
const sourceRect = await this.evaluate(`
|
|
2013
2078
|
(function() {
|
|
2014
|
-
const el =
|
|
2079
|
+
const el = ${queryJS(source)};
|
|
2015
2080
|
if (!el) throw new Error('Source not found: ${source}');
|
|
2016
2081
|
el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
2017
2082
|
const r = el.getBoundingClientRect();
|
|
@@ -2020,7 +2085,7 @@ Last error: ${lastError.message}` : "";
|
|
|
2020
2085
|
`);
|
|
2021
2086
|
const targetRect = await this.evaluate(`
|
|
2022
2087
|
(function() {
|
|
2023
|
-
const el =
|
|
2088
|
+
const el = ${queryJS(target)};
|
|
2024
2089
|
if (!el) throw new Error('Target not found: ${target}');
|
|
2025
2090
|
el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
2026
2091
|
const r = el.getBoundingClientRect();
|