@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.
@@ -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 = document.querySelector(${JSON.stringify(selector)});
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 = document.querySelector(${JSON.stringify(selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 nodeId = await this.page.querySelector(this.selector);
521
- if (!nodeId) throw new Error(`Element not found: ${this.selector}`);
522
- const box = await this.page.getBoxModel(nodeId);
523
- if (!box) throw new Error("Element has no box");
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
- document.querySelectorAll(${JSON.stringify(this.selector)}).length
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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
- const nodeId = await this.page.querySelector(this.selector);
573
- if (!nodeId) return null;
574
- return this.page.getBoxModel(nodeId);
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelector(sel);
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
- })(${JSON.stringify(this.selector)}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
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
- document.querySelectorAll(${JSON.stringify(this.selector)}).length
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 = document.querySelector(${JSON.stringify(this.selector)});
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 = document.querySelectorAll(${JSON.stringify(this.selector)});
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
- document.querySelectorAll(${JSON.stringify(`[${tag}]`)}).forEach(el => el.removeAttribute(${JSON.stringify(tag)}))
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 = document.querySelectorAll(${JSON.stringify(this.selector)});
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 = document.querySelectorAll(${JSON.stringify(this.selector)});
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 = document.querySelector(${JSON.stringify(selector)}); return !!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 = document.querySelector(${JSON.stringify(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'; })()`
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 = document.querySelector(${JSON.stringify(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'; })()`
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 = document.querySelector(sel);
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
- })(${JSON.stringify(selector)}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
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 = Array.from(document.querySelectorAll(sel));
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
- })(${JSON.stringify(selector)}, ${JSON.stringify(fnBody)}${args.length > 0 ? ", " + args.map((a) => JSON.stringify(a)).join(", ") : ""})`
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 = document.querySelector(${JSON.stringify(selector)}); return el?.textContent ?? null; })()`
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 = document.querySelector(${JSON.stringify(selector)}); if (!el) throw new Error('Element not found'); return el.innerText; })()`
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 = document.querySelector(${JSON.stringify(selector)}); if (!el) throw new Error('Element not found'); return el.innerHTML; })()`
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 = document.querySelector(${JSON.stringify(selector)}); return el?.getAttribute(${JSON.stringify(name)}) ?? null; })()`
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
- }, 100);
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 = document.querySelector(selector);
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 = document.querySelector(${JSON.stringify(source)});
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 = document.querySelector(${JSON.stringify(target)});
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();