@skrillex1224/playwright-toolkit 2.1.231 → 2.1.233
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 +10 -3
- package/dist/index.cjs +397 -187
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +397 -187
- package/dist/index.js.map +3 -3
- package/index.d.ts +35 -13
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -505,9 +505,6 @@ var SUPPORTED_TYPES = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
|
|
|
505
505
|
var EXPANDED_SCROLLABLE_CLASS = "__pk_expanded__";
|
|
506
506
|
var DEFAULT_MAX_HEIGHT = 8e3;
|
|
507
507
|
var DEFAULT_SETTLE_MS = 1e3;
|
|
508
|
-
var DEFAULT_BUFFER_RATIO = 0.25;
|
|
509
|
-
var DEFAULT_BUFFER_MIN = 120;
|
|
510
|
-
var DEFAULT_BUFFER_MAX = 320;
|
|
511
508
|
var toPositiveNumber = (value, fallback = 0) => {
|
|
512
509
|
const n = Number(value);
|
|
513
510
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
@@ -517,10 +514,6 @@ var toPositiveInteger = (value, fallback = 0) => {
|
|
|
517
514
|
const number = Math.round(Number(value) || 0);
|
|
518
515
|
return number > 0 ? number : fallback;
|
|
519
516
|
};
|
|
520
|
-
var resolveDefaultBuffer = (viewport = {}) => {
|
|
521
|
-
const height = Math.max(1, Number(viewport.height) || 0);
|
|
522
|
-
return Math.round(Math.min(DEFAULT_BUFFER_MAX, Math.max(DEFAULT_BUFFER_MIN, height * DEFAULT_BUFFER_RATIO)));
|
|
523
|
-
};
|
|
524
517
|
var normalizeType = (value) => {
|
|
525
518
|
const raw = String(value || "png").trim().toLowerCase();
|
|
526
519
|
if (!SUPPORTED_TYPES.has(raw)) return "png";
|
|
@@ -625,6 +618,7 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
625
618
|
return documentTop + scrollHeight;
|
|
626
619
|
};
|
|
627
620
|
const scrollableElements = [];
|
|
621
|
+
const expandedAncestors = [];
|
|
628
622
|
const expandElementToScrollHeight = (el) => {
|
|
629
623
|
if (!el || isDocumentElement(el)) return;
|
|
630
624
|
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
@@ -647,13 +641,44 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
647
641
|
rememberOriginalBoxStyles(node);
|
|
648
642
|
node.style.setProperty("overflow", "visible", "important");
|
|
649
643
|
node.style.setProperty("max-height", "none", "important");
|
|
650
|
-
|
|
644
|
+
expandedAncestors.push(node);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const expandAncestorsToCurrentScrollHeight = () => {
|
|
648
|
+
const ancestors = [...new Set(expandedAncestors)];
|
|
649
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
650
|
+
ancestors.forEach((node) => {
|
|
651
|
+
if (!node || isDocumentElement(node)) return;
|
|
651
652
|
const scrollHeight = Math.ceil(node.scrollHeight || 0);
|
|
653
|
+
if (scrollHeight <= node.clientHeight + 1) return;
|
|
654
|
+
rememberOriginalBoxStyles(node);
|
|
652
655
|
node.style.setProperty("height", `${scrollHeight}px`, "important");
|
|
653
656
|
node.style.setProperty("min-height", `${scrollHeight}px`, "important");
|
|
654
|
-
|
|
657
|
+
node.style.setProperty("max-height", "none", "important");
|
|
658
|
+
});
|
|
655
659
|
}
|
|
656
660
|
};
|
|
661
|
+
const measureExpandedRenderedBottom = () => {
|
|
662
|
+
let renderedBottom = maxHeight;
|
|
663
|
+
const scrollY = window.scrollY || window.pageYOffset || 0;
|
|
664
|
+
candidates.forEach((el) => {
|
|
665
|
+
if (!el || isDocumentElement(el)) return;
|
|
666
|
+
if (!el.classList.contains(className) && !el.closest(`.${className}`)) return;
|
|
667
|
+
const style = window.getComputedStyle(el);
|
|
668
|
+
const rect = el.getBoundingClientRect();
|
|
669
|
+
if (!isVisible(el, style, rect)) return;
|
|
670
|
+
renderedBottom = Math.max(renderedBottom, Math.ceil(rect.bottom + scrollY));
|
|
671
|
+
});
|
|
672
|
+
return renderedBottom;
|
|
673
|
+
};
|
|
674
|
+
const measureDocumentScrollHeight = () => {
|
|
675
|
+
return Math.max(
|
|
676
|
+
maxHeight,
|
|
677
|
+
Math.ceil(root?.scrollHeight || 0),
|
|
678
|
+
Math.ceil(body?.scrollHeight || 0),
|
|
679
|
+
Math.ceil(scrollingElement?.scrollHeight || 0)
|
|
680
|
+
);
|
|
681
|
+
};
|
|
657
682
|
candidates.forEach((el) => {
|
|
658
683
|
const style = window.getComputedStyle(el);
|
|
659
684
|
const rect = el.getBoundingClientRect();
|
|
@@ -684,13 +709,15 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
684
709
|
el.style.height = "auto";
|
|
685
710
|
el.style.maxHeight = "none";
|
|
686
711
|
});
|
|
712
|
+
expandAncestorsToCurrentScrollHeight();
|
|
687
713
|
scrollableElements.forEach((el) => {
|
|
688
714
|
const neededHeight = measureNeededHeight(el);
|
|
689
715
|
if (neededHeight > maxHeight) {
|
|
690
716
|
maxHeight = neededHeight;
|
|
691
717
|
}
|
|
692
718
|
});
|
|
693
|
-
|
|
719
|
+
maxHeight = Math.max(maxHeight, measureDocumentScrollHeight(), measureExpandedRenderedBottom());
|
|
720
|
+
return Math.ceil(maxHeight);
|
|
694
721
|
}, {
|
|
695
722
|
className: EXPANDED_SCROLLABLE_CLASS,
|
|
696
723
|
forceScrollableHeight: options.forceScrollableHeight !== false,
|
|
@@ -699,15 +726,13 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
699
726
|
};
|
|
700
727
|
var prepareExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
701
728
|
const originalViewport = await resolveCurrentViewportSize(page);
|
|
702
|
-
const defaultBuffer = resolveDefaultBuffer(originalViewport);
|
|
703
|
-
const buffer = options.buffer ?? defaultBuffer;
|
|
704
729
|
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
705
730
|
const settleMs = Math.max(0, Number(options.settleMs ?? DEFAULT_SETTLE_MS) || 0);
|
|
706
731
|
const maxScrollHeight = await expandScrollableContent(page, {
|
|
707
732
|
forceScrollableHeight: options.forceScrollableHeight,
|
|
708
733
|
visibleOnly: options.visibleOnly !== false
|
|
709
734
|
});
|
|
710
|
-
const targetHeight = Math.min(maxScrollHeight
|
|
735
|
+
const targetHeight = Math.min(maxScrollHeight, maxHeight);
|
|
711
736
|
await page.setViewportSize({
|
|
712
737
|
width: originalViewport.width,
|
|
713
738
|
height: targetHeight
|
|
@@ -2350,6 +2375,358 @@ var AntiCheat = {
|
|
|
2350
2375
|
}
|
|
2351
2376
|
};
|
|
2352
2377
|
|
|
2378
|
+
// src/device-input.js
|
|
2379
|
+
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
2380
|
+
var assertPage = (page, method) => {
|
|
2381
|
+
if (!page || typeof page !== "object") {
|
|
2382
|
+
throw new Error(`DeviceInput.${method} requires a Playwright page`);
|
|
2383
|
+
}
|
|
2384
|
+
};
|
|
2385
|
+
var isPoint = (value) => value && typeof value === "object" && Number.isFinite(Number(value.x)) && Number.isFinite(Number(value.y));
|
|
2386
|
+
var pointFromArgs = (pointOrX, maybeY) => {
|
|
2387
|
+
if (isPoint(pointOrX)) {
|
|
2388
|
+
return { x: Number(pointOrX.x), y: Number(pointOrX.y) };
|
|
2389
|
+
}
|
|
2390
|
+
return { x: Number(pointOrX), y: Number(maybeY) };
|
|
2391
|
+
};
|
|
2392
|
+
var resolveNativeTarget = (page, target) => {
|
|
2393
|
+
if (!target) return null;
|
|
2394
|
+
if (typeof target === "string") return page.locator(target);
|
|
2395
|
+
return target;
|
|
2396
|
+
};
|
|
2397
|
+
var normalizeSelectorOptions = (options = {}) => ({
|
|
2398
|
+
targetParent: Boolean(options.targetParent ?? options.parent ?? false),
|
|
2399
|
+
pick: options.pick === "last" || options.last ? "last" : "first",
|
|
2400
|
+
visibleOnly: options.visibleOnly !== false,
|
|
2401
|
+
fallbackDomClick: Boolean(options.fallbackDomClick),
|
|
2402
|
+
forceMouse: Boolean(options.forceMouse),
|
|
2403
|
+
forceClick: Boolean(options.forceClick),
|
|
2404
|
+
mouseOptions: options.mouseOptions || {},
|
|
2405
|
+
clickOptions: options.clickOptions || {},
|
|
2406
|
+
tapOptions: options.tapOptions || {}
|
|
2407
|
+
});
|
|
2408
|
+
var waitFor = (page, timeout) => {
|
|
2409
|
+
if (!timeout) return Promise.resolve();
|
|
2410
|
+
if (page && typeof page.waitForTimeout === "function") {
|
|
2411
|
+
return page.waitForTimeout(timeout);
|
|
2412
|
+
}
|
|
2413
|
+
return new Promise((resolve) => setTimeout(resolve, timeout));
|
|
2414
|
+
};
|
|
2415
|
+
var getTargetBoundingBox = async (target) => {
|
|
2416
|
+
if (!target) return null;
|
|
2417
|
+
if (typeof target.boundingBox === "function") return target.boundingBox();
|
|
2418
|
+
if (typeof target.elementHandle === "function") {
|
|
2419
|
+
const handle = await target.elementHandle();
|
|
2420
|
+
try {
|
|
2421
|
+
return await handle?.boundingBox?.();
|
|
2422
|
+
} finally {
|
|
2423
|
+
if (handle && typeof handle.dispose === "function") {
|
|
2424
|
+
await handle.dispose().catch(() => {
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
return null;
|
|
2430
|
+
};
|
|
2431
|
+
var assertPoint = (point) => {
|
|
2432
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {
|
|
2433
|
+
throw new Error(`Invalid input point: ${JSON.stringify(point)}`);
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
var dispatchMouseMove = (page, point, options = {}) => page.mouse.move(point.x, point.y, options);
|
|
2437
|
+
var dispatchMouseStart = (page, options = {}) => page.mouse.down(options);
|
|
2438
|
+
var dispatchMouseEnd = (page, options = {}) => page.mouse.up(options);
|
|
2439
|
+
var dragWithMouse = async (page, points, options = {}) => {
|
|
2440
|
+
await dispatchMouseMove(page, points.start, { steps: 8 });
|
|
2441
|
+
await waitFor(page, options.initialDelayMs ?? 250);
|
|
2442
|
+
await dispatchMouseStart(page);
|
|
2443
|
+
await waitFor(page, options.holdDelayMs ?? 350);
|
|
2444
|
+
await dispatchMouseMove(page, points.lift, { steps: 6 });
|
|
2445
|
+
await waitFor(page, options.liftDelayMs ?? 250);
|
|
2446
|
+
for (let step = 1; step <= points.steps; step += 1) {
|
|
2447
|
+
const progress = step / points.steps;
|
|
2448
|
+
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
2449
|
+
await dispatchMouseMove(page, {
|
|
2450
|
+
x: points.lift.x + (points.end.x - points.lift.x) * easedProgress,
|
|
2451
|
+
y: points.lift.y + (points.end.y - points.lift.y) * easedProgress
|
|
2452
|
+
}, { steps: 2 });
|
|
2453
|
+
await waitFor(page, options.stepDelayMs ?? 90);
|
|
2454
|
+
}
|
|
2455
|
+
await waitFor(page, options.beforeReleaseDelayMs ?? 100);
|
|
2456
|
+
await dispatchMouseEnd(page);
|
|
2457
|
+
await waitFor(page, options.afterReleaseDelayMs ?? 100);
|
|
2458
|
+
return true;
|
|
2459
|
+
};
|
|
2460
|
+
var dragWithTouch = async (page, points, options = {}) => {
|
|
2461
|
+
let client = null;
|
|
2462
|
+
try {
|
|
2463
|
+
client = await page.context().newCDPSession(page);
|
|
2464
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2465
|
+
type: "touchStart",
|
|
2466
|
+
touchPoints: [{ x: points.start.x, y: points.start.y, id: 1 }]
|
|
2467
|
+
});
|
|
2468
|
+
await waitFor(page, options.holdDelayMs ?? 350);
|
|
2469
|
+
const totalSteps = points.steps + 1;
|
|
2470
|
+
for (let step = 1; step <= totalSteps; step += 1) {
|
|
2471
|
+
const progress = step / totalSteps;
|
|
2472
|
+
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
2473
|
+
const from = step === 1 ? points.start : points.lift;
|
|
2474
|
+
const to = points.end;
|
|
2475
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2476
|
+
type: "touchMove",
|
|
2477
|
+
touchPoints: [{
|
|
2478
|
+
x: from.x + (to.x - from.x) * easedProgress,
|
|
2479
|
+
y: from.y + (to.y - from.y) * easedProgress,
|
|
2480
|
+
id: 1
|
|
2481
|
+
}]
|
|
2482
|
+
});
|
|
2483
|
+
await waitFor(page, options.stepDelayMs ?? 90);
|
|
2484
|
+
}
|
|
2485
|
+
await waitFor(page, options.beforeReleaseDelayMs ?? 100);
|
|
2486
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2487
|
+
type: "touchEnd",
|
|
2488
|
+
touchPoints: []
|
|
2489
|
+
});
|
|
2490
|
+
await waitFor(page, options.afterReleaseDelayMs ?? 100);
|
|
2491
|
+
return true;
|
|
2492
|
+
} finally {
|
|
2493
|
+
if (client && typeof client.detach === "function") {
|
|
2494
|
+
await client.detach().catch(() => {
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
};
|
|
2499
|
+
var clickTargetWithDevice = async (page, target, options = {}) => {
|
|
2500
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2501
|
+
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2502
|
+
if (target && resolvedDevice === Device.Mobile && !normalizedOptions.forceClick) {
|
|
2503
|
+
if (typeof target.tap === "function") {
|
|
2504
|
+
await target.tap(normalizedOptions.tapOptions);
|
|
2505
|
+
return true;
|
|
2506
|
+
}
|
|
2507
|
+
const box = await getTargetBoundingBox(target);
|
|
2508
|
+
if (box) {
|
|
2509
|
+
await DeviceInput.clickPoint(page, {
|
|
2510
|
+
x: box.x + box.width / 2,
|
|
2511
|
+
y: box.y + box.height / 2
|
|
2512
|
+
}, normalizedOptions);
|
|
2513
|
+
return true;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
if (!target || typeof target.click !== "function") {
|
|
2517
|
+
throw new Error("DeviceInput click target does not expose click()");
|
|
2518
|
+
}
|
|
2519
|
+
await target.click(normalizedOptions.clickOptions || options);
|
|
2520
|
+
return true;
|
|
2521
|
+
};
|
|
2522
|
+
var DeviceInput = {
|
|
2523
|
+
device(page) {
|
|
2524
|
+
return resolveDeviceFromPage(page);
|
|
2525
|
+
},
|
|
2526
|
+
isMobile(page) {
|
|
2527
|
+
return resolveDeviceFromPage(page) === Device.Mobile;
|
|
2528
|
+
},
|
|
2529
|
+
async clickPoint(page, pointOrX, maybeY, options = {}) {
|
|
2530
|
+
assertPage(page, "clickPoint");
|
|
2531
|
+
const clickOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2532
|
+
const point = pointFromArgs(pointOrX, maybeY);
|
|
2533
|
+
assertPoint(point);
|
|
2534
|
+
if (resolveDeviceFromPage(page) === Device.Mobile && !clickOptions.forceMouse && page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
2535
|
+
await page.touchscreen.tap(point.x, point.y);
|
|
2536
|
+
return true;
|
|
2537
|
+
}
|
|
2538
|
+
await page.mouse.click(point.x, point.y, clickOptions.mouseOptions || {});
|
|
2539
|
+
return true;
|
|
2540
|
+
},
|
|
2541
|
+
async tapPoint(page, pointOrX, maybeY, options = {}) {
|
|
2542
|
+
return DeviceInput.clickPoint(page, pointOrX, maybeY, options);
|
|
2543
|
+
},
|
|
2544
|
+
async click(page, target, options = {}) {
|
|
2545
|
+
assertPage(page, "click");
|
|
2546
|
+
if (isPoint(target)) {
|
|
2547
|
+
return DeviceInput.clickPoint(page, target, void 0, options);
|
|
2548
|
+
}
|
|
2549
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2550
|
+
return clickTargetWithDevice(page, nativeTarget, options);
|
|
2551
|
+
},
|
|
2552
|
+
async tap(page, target, options = {}) {
|
|
2553
|
+
return DeviceInput.click(page, target, options);
|
|
2554
|
+
},
|
|
2555
|
+
async move(page, pointOrX, maybeY, options = {}) {
|
|
2556
|
+
assertPage(page, "move");
|
|
2557
|
+
const moveOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2558
|
+
const point = pointFromArgs(pointOrX, maybeY);
|
|
2559
|
+
assertPoint(point);
|
|
2560
|
+
if (resolveDeviceFromPage(page) === Device.Mobile && !moveOptions.forceMouse) {
|
|
2561
|
+
return true;
|
|
2562
|
+
}
|
|
2563
|
+
await page.mouse.move(point.x, point.y, moveOptions.mouseOptions || moveOptions);
|
|
2564
|
+
return true;
|
|
2565
|
+
},
|
|
2566
|
+
async focus(page, target, options = {}) {
|
|
2567
|
+
assertPage(page, "focus");
|
|
2568
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2569
|
+
if (!nativeTarget || typeof nativeTarget.focus !== "function") {
|
|
2570
|
+
throw new Error("DeviceInput focus target does not expose focus()");
|
|
2571
|
+
}
|
|
2572
|
+
await nativeTarget.focus(options);
|
|
2573
|
+
return true;
|
|
2574
|
+
},
|
|
2575
|
+
async fill(page, target, value, options = {}) {
|
|
2576
|
+
assertPage(page, "fill");
|
|
2577
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2578
|
+
if (!nativeTarget || typeof nativeTarget.fill !== "function") {
|
|
2579
|
+
throw new Error("DeviceInput fill target does not expose fill()");
|
|
2580
|
+
}
|
|
2581
|
+
await nativeTarget.fill(value, options);
|
|
2582
|
+
return true;
|
|
2583
|
+
},
|
|
2584
|
+
async keyboardType(page, text, options = {}) {
|
|
2585
|
+
assertPage(page, "keyboardType");
|
|
2586
|
+
await page.keyboard.type(text, options);
|
|
2587
|
+
return true;
|
|
2588
|
+
},
|
|
2589
|
+
async type(page, targetOrText, maybeText, options = {}) {
|
|
2590
|
+
assertPage(page, "type");
|
|
2591
|
+
if (maybeText == null || typeof maybeText === "object" && !Array.isArray(maybeText)) {
|
|
2592
|
+
return DeviceInput.keyboardType(page, targetOrText, maybeText || options);
|
|
2593
|
+
}
|
|
2594
|
+
const nativeTarget = resolveNativeTarget(page, targetOrText);
|
|
2595
|
+
if (!nativeTarget || typeof nativeTarget.type !== "function") {
|
|
2596
|
+
throw new Error("DeviceInput type target does not expose type()");
|
|
2597
|
+
}
|
|
2598
|
+
await nativeTarget.type(maybeText, options);
|
|
2599
|
+
return true;
|
|
2600
|
+
},
|
|
2601
|
+
async press(page, targetOrKey, maybeKey, options = {}) {
|
|
2602
|
+
assertPage(page, "press");
|
|
2603
|
+
const hasTarget = typeof maybeKey === "string";
|
|
2604
|
+
const key = hasTarget ? maybeKey : targetOrKey;
|
|
2605
|
+
const pressOptions = hasTarget ? options : maybeKey || options;
|
|
2606
|
+
if (hasTarget) {
|
|
2607
|
+
await DeviceInput.click(page, targetOrKey, pressOptions.clickOptions || {});
|
|
2608
|
+
}
|
|
2609
|
+
await page.keyboard.press(key, pressOptions.keyboardOptions || pressOptions);
|
|
2610
|
+
return true;
|
|
2611
|
+
},
|
|
2612
|
+
async selectorCenterPoint(page, selector, options = {}) {
|
|
2613
|
+
assertPage(page, "selectorCenterPoint");
|
|
2614
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2615
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2616
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2617
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2618
|
+
let rect = null;
|
|
2619
|
+
for (const node of ordered) {
|
|
2620
|
+
if (!node) continue;
|
|
2621
|
+
const target = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2622
|
+
rect = target.getBoundingClientRect?.() || null;
|
|
2623
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2624
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2625
|
+
break;
|
|
2626
|
+
}
|
|
2627
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
2628
|
+
return {
|
|
2629
|
+
x: rect.left + rect.width / 2,
|
|
2630
|
+
y: rect.top + rect.height / 2
|
|
2631
|
+
};
|
|
2632
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2633
|
+
},
|
|
2634
|
+
async domClick(page, selector, options = {}) {
|
|
2635
|
+
assertPage(page, "domClick");
|
|
2636
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2637
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2638
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2639
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2640
|
+
let target = null;
|
|
2641
|
+
for (const node of ordered) {
|
|
2642
|
+
if (!node) continue;
|
|
2643
|
+
const candidate = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2644
|
+
const rect = candidate.getBoundingClientRect?.() || null;
|
|
2645
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2646
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2647
|
+
target = candidate;
|
|
2648
|
+
break;
|
|
2649
|
+
}
|
|
2650
|
+
if (!target || typeof target.click !== "function") return false;
|
|
2651
|
+
target.click();
|
|
2652
|
+
return true;
|
|
2653
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2654
|
+
},
|
|
2655
|
+
async clickSelectorCenter(page, selector, options = {}) {
|
|
2656
|
+
assertPage(page, "clickSelectorCenter");
|
|
2657
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2658
|
+
try {
|
|
2659
|
+
const point = await DeviceInput.selectorCenterPoint(page, selector, normalizedOptions);
|
|
2660
|
+
if (point) {
|
|
2661
|
+
await DeviceInput.clickPoint(page, point, void 0, normalizedOptions);
|
|
2662
|
+
return true;
|
|
2663
|
+
}
|
|
2664
|
+
} catch (error) {
|
|
2665
|
+
if (!normalizedOptions.fallbackDomClick) throw error;
|
|
2666
|
+
}
|
|
2667
|
+
if (normalizedOptions.fallbackDomClick) {
|
|
2668
|
+
return DeviceInput.domClick(page, selector, normalizedOptions);
|
|
2669
|
+
}
|
|
2670
|
+
return false;
|
|
2671
|
+
},
|
|
2672
|
+
async clickText(frameOrPage, texts, options = {}) {
|
|
2673
|
+
for (const text of texts || []) {
|
|
2674
|
+
if (!text) continue;
|
|
2675
|
+
const candidates = [
|
|
2676
|
+
frameOrPage.getByText(text, { exact: false }).first(),
|
|
2677
|
+
frameOrPage.locator(`text=${text}`).first()
|
|
2678
|
+
];
|
|
2679
|
+
for (const candidate of candidates) {
|
|
2680
|
+
const isVisible = await candidate.isVisible({
|
|
2681
|
+
timeout: options.visibleTimeoutMs ?? options.actionVisibleTimeoutMs ?? 1500
|
|
2682
|
+
}).catch(() => false);
|
|
2683
|
+
if (!isVisible) continue;
|
|
2684
|
+
const page = options.page || (frameOrPage && typeof frameOrPage.context === "function" ? frameOrPage : null) || candidate.page?.();
|
|
2685
|
+
if (!page) {
|
|
2686
|
+
throw new Error("DeviceInput.clickText requires options.page when used with a frame locator");
|
|
2687
|
+
}
|
|
2688
|
+
await DeviceInput.click(page, candidate, options);
|
|
2689
|
+
return true;
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
return false;
|
|
2693
|
+
},
|
|
2694
|
+
async drag(page, sourceLocator, targetLocator, options = {}) {
|
|
2695
|
+
assertPage(page, "drag");
|
|
2696
|
+
const sourceBox = await sourceLocator.boundingBox();
|
|
2697
|
+
const targetBox = await targetLocator.boundingBox();
|
|
2698
|
+
if (!sourceBox || !targetBox) {
|
|
2699
|
+
throw new Error("Unable to resolve drag coordinates.");
|
|
2700
|
+
}
|
|
2701
|
+
const steps = options.steps || 10;
|
|
2702
|
+
const liftOffsetX = Math.min(18, Math.max(8, sourceBox.width * 0.12));
|
|
2703
|
+
const liftOffsetY = Math.min(12, Math.max(4, sourceBox.height * 0.08));
|
|
2704
|
+
const points = {
|
|
2705
|
+
start: {
|
|
2706
|
+
x: sourceBox.x + sourceBox.width / 2,
|
|
2707
|
+
y: sourceBox.y + sourceBox.height / 2
|
|
2708
|
+
},
|
|
2709
|
+
lift: {
|
|
2710
|
+
x: sourceBox.x + sourceBox.width / 2 + liftOffsetX,
|
|
2711
|
+
y: sourceBox.y + sourceBox.height / 2 + liftOffsetY
|
|
2712
|
+
},
|
|
2713
|
+
end: {
|
|
2714
|
+
x: targetBox.x + targetBox.width / 2,
|
|
2715
|
+
y: targetBox.y + targetBox.height / 2
|
|
2716
|
+
},
|
|
2717
|
+
steps
|
|
2718
|
+
};
|
|
2719
|
+
if (resolveDeviceFromPage(page) === Device.Mobile && !options.forceMouse) {
|
|
2720
|
+
try {
|
|
2721
|
+
return await dragWithTouch(page, points, options);
|
|
2722
|
+
} catch (error) {
|
|
2723
|
+
if (options.allowMouseFallback === false) throw error;
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
return dragWithMouse(page, points, options);
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2353
2730
|
// src/internals/humanize/desktop.js
|
|
2354
2731
|
var import_delay2 = __toESM(require("delay"), 1);
|
|
2355
2732
|
var import_ghost_cursor_playwright = require("ghost-cursor-playwright");
|
|
@@ -2865,141 +3242,6 @@ var Humanize = {
|
|
|
2865
3242
|
}
|
|
2866
3243
|
};
|
|
2867
3244
|
|
|
2868
|
-
// src/internals/humanize/machine.js
|
|
2869
|
-
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
2870
|
-
var isPoint = (value) => value && typeof value === "object" && Number.isFinite(Number(value.x)) && Number.isFinite(Number(value.y));
|
|
2871
|
-
var resolveNativeTarget = (page, target) => {
|
|
2872
|
-
if (!target) return null;
|
|
2873
|
-
if (typeof target === "string") {
|
|
2874
|
-
return page.locator(target);
|
|
2875
|
-
}
|
|
2876
|
-
return target;
|
|
2877
|
-
};
|
|
2878
|
-
var normalizeSelectorOptions = (options = {}) => ({
|
|
2879
|
-
targetParent: Boolean(options.targetParent ?? options.parent ?? false),
|
|
2880
|
-
pick: options.pick === "last" || options.last ? "last" : "first",
|
|
2881
|
-
visibleOnly: options.visibleOnly !== false,
|
|
2882
|
-
fallbackDomClick: Boolean(options.fallbackDomClick),
|
|
2883
|
-
forceMouse: Boolean(options.forceMouse)
|
|
2884
|
-
});
|
|
2885
|
-
var MachineHumanize = {
|
|
2886
|
-
async clickPoint(page, pointOrX, maybeY, options = {}) {
|
|
2887
|
-
const clickOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2888
|
-
const point = isPoint(pointOrX) ? pointOrX : { x: pointOrX, y: maybeY };
|
|
2889
|
-
const x = Number(point.x);
|
|
2890
|
-
const y = Number(point.y);
|
|
2891
|
-
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
2892
|
-
throw new Error(`Invalid click point: ${JSON.stringify(point)}`);
|
|
2893
|
-
}
|
|
2894
|
-
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2895
|
-
if (resolvedDevice === Device.Mobile && !clickOptions.forceMouse && page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
2896
|
-
await page.touchscreen.tap(x, y);
|
|
2897
|
-
return true;
|
|
2898
|
-
}
|
|
2899
|
-
await page.mouse.click(x, y, clickOptions.mouseOptions || {});
|
|
2900
|
-
return true;
|
|
2901
|
-
},
|
|
2902
|
-
async click(page, target, options = {}) {
|
|
2903
|
-
if (isPoint(target)) {
|
|
2904
|
-
return MachineHumanize.clickPoint(page, target, void 0, options);
|
|
2905
|
-
}
|
|
2906
|
-
const nativeTarget = resolveNativeTarget(page, target);
|
|
2907
|
-
if (!nativeTarget || typeof nativeTarget.click !== "function") {
|
|
2908
|
-
throw new Error("Machine click target does not expose click()");
|
|
2909
|
-
}
|
|
2910
|
-
await nativeTarget.click(options.clickOptions || options);
|
|
2911
|
-
return true;
|
|
2912
|
-
},
|
|
2913
|
-
async focus(page, target, options = {}) {
|
|
2914
|
-
const nativeTarget = resolveNativeTarget(page, target);
|
|
2915
|
-
if (!nativeTarget || typeof nativeTarget.focus !== "function") {
|
|
2916
|
-
throw new Error("Machine focus target does not expose focus()");
|
|
2917
|
-
}
|
|
2918
|
-
await nativeTarget.focus(options);
|
|
2919
|
-
return true;
|
|
2920
|
-
},
|
|
2921
|
-
async fill(page, target, value, options = {}) {
|
|
2922
|
-
const nativeTarget = resolveNativeTarget(page, target);
|
|
2923
|
-
if (!nativeTarget || typeof nativeTarget.fill !== "function") {
|
|
2924
|
-
throw new Error("Machine fill target does not expose fill()");
|
|
2925
|
-
}
|
|
2926
|
-
await nativeTarget.fill(value, options);
|
|
2927
|
-
return true;
|
|
2928
|
-
},
|
|
2929
|
-
async keyboardType(page, text, options = {}) {
|
|
2930
|
-
await page.keyboard.type(text, options);
|
|
2931
|
-
return true;
|
|
2932
|
-
},
|
|
2933
|
-
async type(page, targetOrText, maybeText, options = {}) {
|
|
2934
|
-
if (maybeText == null || typeof maybeText === "object" && !Array.isArray(maybeText)) {
|
|
2935
|
-
return MachineHumanize.keyboardType(page, targetOrText, maybeText || options);
|
|
2936
|
-
}
|
|
2937
|
-
const nativeTarget = resolveNativeTarget(page, targetOrText);
|
|
2938
|
-
if (!nativeTarget || typeof nativeTarget.type !== "function") {
|
|
2939
|
-
throw new Error("Machine type target does not expose type()");
|
|
2940
|
-
}
|
|
2941
|
-
await nativeTarget.type(maybeText, options);
|
|
2942
|
-
return true;
|
|
2943
|
-
},
|
|
2944
|
-
async selectorCenterPoint(page, selector, options = {}) {
|
|
2945
|
-
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2946
|
-
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2947
|
-
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2948
|
-
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2949
|
-
let rect = null;
|
|
2950
|
-
for (const node of ordered) {
|
|
2951
|
-
if (!node) continue;
|
|
2952
|
-
const target = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2953
|
-
rect = target.getBoundingClientRect?.() || null;
|
|
2954
|
-
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2955
|
-
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2956
|
-
break;
|
|
2957
|
-
}
|
|
2958
|
-
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
2959
|
-
return {
|
|
2960
|
-
x: rect.left + rect.width / 2,
|
|
2961
|
-
y: rect.top + rect.height / 2
|
|
2962
|
-
};
|
|
2963
|
-
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2964
|
-
},
|
|
2965
|
-
async domClick(page, selector, options = {}) {
|
|
2966
|
-
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2967
|
-
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2968
|
-
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2969
|
-
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2970
|
-
let target = null;
|
|
2971
|
-
for (const node of ordered) {
|
|
2972
|
-
if (!node) continue;
|
|
2973
|
-
const candidate = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2974
|
-
const rect = candidate.getBoundingClientRect?.() || null;
|
|
2975
|
-
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2976
|
-
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2977
|
-
target = candidate;
|
|
2978
|
-
break;
|
|
2979
|
-
}
|
|
2980
|
-
if (!target || typeof target.click !== "function") return false;
|
|
2981
|
-
target.click();
|
|
2982
|
-
return true;
|
|
2983
|
-
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2984
|
-
},
|
|
2985
|
-
async clickSelectorCenter(page, selector, options = {}) {
|
|
2986
|
-
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2987
|
-
try {
|
|
2988
|
-
const point = await MachineHumanize.selectorCenterPoint(page, selector, normalizedOptions);
|
|
2989
|
-
if (point) {
|
|
2990
|
-
await MachineHumanize.clickPoint(page, point, void 0, normalizedOptions);
|
|
2991
|
-
return true;
|
|
2992
|
-
}
|
|
2993
|
-
} catch (error) {
|
|
2994
|
-
if (!normalizedOptions.fallbackDomClick) throw error;
|
|
2995
|
-
}
|
|
2996
|
-
if (normalizedOptions.fallbackDomClick) {
|
|
2997
|
-
return MachineHumanize.domClick(page, selector, normalizedOptions);
|
|
2998
|
-
}
|
|
2999
|
-
return false;
|
|
3000
|
-
}
|
|
3001
|
-
};
|
|
3002
|
-
|
|
3003
3245
|
// src/internals/humanize/shared.js
|
|
3004
3246
|
var import_delay3 = __toESM(require("delay"), 1);
|
|
3005
3247
|
var jitterMs = (base, jitterPercent = 0.3) => {
|
|
@@ -3828,9 +4070,6 @@ var callDelegate = (method, page, args) => {
|
|
|
3828
4070
|
return delegate[method](page, ...args);
|
|
3829
4071
|
};
|
|
3830
4072
|
var Humanize2 = {
|
|
3831
|
-
// M = Machine: native/mechanical operations for兼容路径。
|
|
3832
|
-
// 这组 API 不做人类化处理,只保留原生 click / focus / fill / type 等语义。
|
|
3833
|
-
M: MachineHumanize,
|
|
3834
4073
|
jitterMs(base, jitterPercent = 0.3) {
|
|
3835
4074
|
return Humanize.jitterMs(base, jitterPercent);
|
|
3836
4075
|
},
|
|
@@ -4406,42 +4645,14 @@ var clickCaptchaAction = async (frame, texts, options) => {
|
|
|
4406
4645
|
if (!isVisible) {
|
|
4407
4646
|
continue;
|
|
4408
4647
|
}
|
|
4409
|
-
await
|
|
4648
|
+
await DeviceInput.click(options.page, candidate);
|
|
4410
4649
|
return true;
|
|
4411
4650
|
}
|
|
4412
4651
|
}
|
|
4413
4652
|
return false;
|
|
4414
4653
|
};
|
|
4415
|
-
var
|
|
4416
|
-
|
|
4417
|
-
const targetBox = await targetLocator.boundingBox();
|
|
4418
|
-
if (!sourceBox || !targetBox) {
|
|
4419
|
-
throw new Error("Unable to resolve captcha drag coordinates.");
|
|
4420
|
-
}
|
|
4421
|
-
const startX = sourceBox.x + sourceBox.width / 2;
|
|
4422
|
-
const startY = sourceBox.y + sourceBox.height / 2;
|
|
4423
|
-
const endX = targetBox.x + targetBox.width / 2;
|
|
4424
|
-
const endY = targetBox.y + targetBox.height / 2;
|
|
4425
|
-
const steps = 10;
|
|
4426
|
-
const liftOffsetX = Math.min(18, Math.max(8, sourceBox.width * 0.12));
|
|
4427
|
-
const liftOffsetY = Math.min(12, Math.max(4, sourceBox.height * 0.08));
|
|
4428
|
-
await page.mouse.move(startX, startY, { steps: 8 });
|
|
4429
|
-
await page.waitForTimeout(250);
|
|
4430
|
-
await page.mouse.down();
|
|
4431
|
-
await page.waitForTimeout(350);
|
|
4432
|
-
await page.mouse.move(startX + liftOffsetX, startY + liftOffsetY, { steps: 6 });
|
|
4433
|
-
await page.waitForTimeout(250);
|
|
4434
|
-
for (let step = 1; step <= steps; step++) {
|
|
4435
|
-
const progress = step / steps;
|
|
4436
|
-
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
4437
|
-
const currentX = startX + liftOffsetX + (endX - startX - liftOffsetX) * easedProgress;
|
|
4438
|
-
const currentY = startY + liftOffsetY + (endY - startY - liftOffsetY) * easedProgress;
|
|
4439
|
-
await page.mouse.move(currentX, currentY, { steps: 2 });
|
|
4440
|
-
await page.waitForTimeout(90);
|
|
4441
|
-
}
|
|
4442
|
-
await page.waitForTimeout(100);
|
|
4443
|
-
await page.mouse.up();
|
|
4444
|
-
await page.waitForTimeout(100);
|
|
4654
|
+
var dragCaptchaAction = async (page, sourceLocator, targetLocator) => {
|
|
4655
|
+
await DeviceInput.drag(page, sourceLocator, targetLocator);
|
|
4445
4656
|
};
|
|
4446
4657
|
|
|
4447
4658
|
// src/internals/captcha/bytedance.js
|
|
@@ -4537,7 +4748,7 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
4537
4748
|
return { iframeLocator, frame };
|
|
4538
4749
|
};
|
|
4539
4750
|
var refreshCaptcha = async (page, frame, options) => {
|
|
4540
|
-
const clicked = await clickCaptchaAction(frame, options.refreshTexts, options).catch(() => false);
|
|
4751
|
+
const clicked = await clickCaptchaAction(frame, options.refreshTexts, { ...options, page }).catch(() => false);
|
|
4541
4752
|
if (!clicked) {
|
|
4542
4753
|
logger10.warn("Refresh button not found.");
|
|
4543
4754
|
return false;
|
|
@@ -4663,9 +4874,9 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
4663
4874
|
state: "visible",
|
|
4664
4875
|
timeout: config.sourceImageVisibleTimeoutMs
|
|
4665
4876
|
});
|
|
4666
|
-
await
|
|
4877
|
+
await dragCaptchaAction(page, sourceImage, dropTarget);
|
|
4667
4878
|
}
|
|
4668
|
-
const submitted = await clickCaptchaAction(frame, config.submitTexts, config).catch(() => false);
|
|
4879
|
+
const submitted = await clickCaptchaAction(frame, config.submitTexts, { ...config, page }).catch(() => false);
|
|
4669
4880
|
if (!submitted) {
|
|
4670
4881
|
logger10.warn("\u672A\u627E\u5230\u63D0\u4EA4\u6309\u94AE\uFF0C\u53EF\u80FD\u4F1A\u81EA\u52A8\u63D0\u4EA4\u3002");
|
|
4671
4882
|
}
|
|
@@ -8371,7 +8582,6 @@ var Share = {
|
|
|
8371
8582
|
*
|
|
8372
8583
|
* @param {import('playwright').Page} page
|
|
8373
8584
|
* @param {Object} [options]
|
|
8374
|
-
* @param {number} [options.buffer] 额外缓冲高度;默认按视口高度自动计算,传 0 可关闭
|
|
8375
8585
|
* @param {boolean} [options.restore]
|
|
8376
8586
|
* @param {number} [options.maxHeight]
|
|
8377
8587
|
* @param {number} [options.maxBytes] 默认 5MiB,返回 base64 超过后会压缩
|
|
@@ -8384,7 +8594,6 @@ var Share = {
|
|
|
8384
8594
|
const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
|
|
8385
8595
|
const compression = resolveImageCompression(options);
|
|
8386
8596
|
const captureOptions = {
|
|
8387
|
-
...Object.prototype.hasOwnProperty.call(options, "buffer") ? { buffer: options.buffer } : {},
|
|
8388
8597
|
restore,
|
|
8389
8598
|
maxHeight: options.maxHeight,
|
|
8390
8599
|
type: "png",
|
|
@@ -8410,6 +8619,7 @@ var usePlaywrightToolKit = () => {
|
|
|
8410
8619
|
return {
|
|
8411
8620
|
ApifyKit,
|
|
8412
8621
|
AntiCheat,
|
|
8622
|
+
DeviceInput,
|
|
8413
8623
|
Humanize: Humanize2,
|
|
8414
8624
|
Launch,
|
|
8415
8625
|
LiveView,
|