@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.js
CHANGED
|
@@ -478,9 +478,6 @@ var SUPPORTED_TYPES = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
|
|
|
478
478
|
var EXPANDED_SCROLLABLE_CLASS = "__pk_expanded__";
|
|
479
479
|
var DEFAULT_MAX_HEIGHT = 8e3;
|
|
480
480
|
var DEFAULT_SETTLE_MS = 1e3;
|
|
481
|
-
var DEFAULT_BUFFER_RATIO = 0.25;
|
|
482
|
-
var DEFAULT_BUFFER_MIN = 120;
|
|
483
|
-
var DEFAULT_BUFFER_MAX = 320;
|
|
484
481
|
var toPositiveNumber = (value, fallback = 0) => {
|
|
485
482
|
const n = Number(value);
|
|
486
483
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
@@ -490,10 +487,6 @@ var toPositiveInteger = (value, fallback = 0) => {
|
|
|
490
487
|
const number = Math.round(Number(value) || 0);
|
|
491
488
|
return number > 0 ? number : fallback;
|
|
492
489
|
};
|
|
493
|
-
var resolveDefaultBuffer = (viewport = {}) => {
|
|
494
|
-
const height = Math.max(1, Number(viewport.height) || 0);
|
|
495
|
-
return Math.round(Math.min(DEFAULT_BUFFER_MAX, Math.max(DEFAULT_BUFFER_MIN, height * DEFAULT_BUFFER_RATIO)));
|
|
496
|
-
};
|
|
497
490
|
var normalizeType = (value) => {
|
|
498
491
|
const raw = String(value || "png").trim().toLowerCase();
|
|
499
492
|
if (!SUPPORTED_TYPES.has(raw)) return "png";
|
|
@@ -598,6 +591,7 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
598
591
|
return documentTop + scrollHeight;
|
|
599
592
|
};
|
|
600
593
|
const scrollableElements = [];
|
|
594
|
+
const expandedAncestors = [];
|
|
601
595
|
const expandElementToScrollHeight = (el) => {
|
|
602
596
|
if (!el || isDocumentElement(el)) return;
|
|
603
597
|
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
@@ -620,13 +614,44 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
620
614
|
rememberOriginalBoxStyles(node);
|
|
621
615
|
node.style.setProperty("overflow", "visible", "important");
|
|
622
616
|
node.style.setProperty("max-height", "none", "important");
|
|
623
|
-
|
|
617
|
+
expandedAncestors.push(node);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
const expandAncestorsToCurrentScrollHeight = () => {
|
|
621
|
+
const ancestors = [...new Set(expandedAncestors)];
|
|
622
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
623
|
+
ancestors.forEach((node) => {
|
|
624
|
+
if (!node || isDocumentElement(node)) return;
|
|
624
625
|
const scrollHeight = Math.ceil(node.scrollHeight || 0);
|
|
626
|
+
if (scrollHeight <= node.clientHeight + 1) return;
|
|
627
|
+
rememberOriginalBoxStyles(node);
|
|
625
628
|
node.style.setProperty("height", `${scrollHeight}px`, "important");
|
|
626
629
|
node.style.setProperty("min-height", `${scrollHeight}px`, "important");
|
|
627
|
-
|
|
630
|
+
node.style.setProperty("max-height", "none", "important");
|
|
631
|
+
});
|
|
628
632
|
}
|
|
629
633
|
};
|
|
634
|
+
const measureExpandedRenderedBottom = () => {
|
|
635
|
+
let renderedBottom = maxHeight;
|
|
636
|
+
const scrollY = window.scrollY || window.pageYOffset || 0;
|
|
637
|
+
candidates.forEach((el) => {
|
|
638
|
+
if (!el || isDocumentElement(el)) return;
|
|
639
|
+
if (!el.classList.contains(className) && !el.closest(`.${className}`)) return;
|
|
640
|
+
const style = window.getComputedStyle(el);
|
|
641
|
+
const rect = el.getBoundingClientRect();
|
|
642
|
+
if (!isVisible(el, style, rect)) return;
|
|
643
|
+
renderedBottom = Math.max(renderedBottom, Math.ceil(rect.bottom + scrollY));
|
|
644
|
+
});
|
|
645
|
+
return renderedBottom;
|
|
646
|
+
};
|
|
647
|
+
const measureDocumentScrollHeight = () => {
|
|
648
|
+
return Math.max(
|
|
649
|
+
maxHeight,
|
|
650
|
+
Math.ceil(root?.scrollHeight || 0),
|
|
651
|
+
Math.ceil(body?.scrollHeight || 0),
|
|
652
|
+
Math.ceil(scrollingElement?.scrollHeight || 0)
|
|
653
|
+
);
|
|
654
|
+
};
|
|
630
655
|
candidates.forEach((el) => {
|
|
631
656
|
const style = window.getComputedStyle(el);
|
|
632
657
|
const rect = el.getBoundingClientRect();
|
|
@@ -657,13 +682,15 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
657
682
|
el.style.height = "auto";
|
|
658
683
|
el.style.maxHeight = "none";
|
|
659
684
|
});
|
|
685
|
+
expandAncestorsToCurrentScrollHeight();
|
|
660
686
|
scrollableElements.forEach((el) => {
|
|
661
687
|
const neededHeight = measureNeededHeight(el);
|
|
662
688
|
if (neededHeight > maxHeight) {
|
|
663
689
|
maxHeight = neededHeight;
|
|
664
690
|
}
|
|
665
691
|
});
|
|
666
|
-
|
|
692
|
+
maxHeight = Math.max(maxHeight, measureDocumentScrollHeight(), measureExpandedRenderedBottom());
|
|
693
|
+
return Math.ceil(maxHeight);
|
|
667
694
|
}, {
|
|
668
695
|
className: EXPANDED_SCROLLABLE_CLASS,
|
|
669
696
|
forceScrollableHeight: options.forceScrollableHeight !== false,
|
|
@@ -672,15 +699,13 @@ var expandScrollableContent = async (page, options = {}) => {
|
|
|
672
699
|
};
|
|
673
700
|
var prepareExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
674
701
|
const originalViewport = await resolveCurrentViewportSize(page);
|
|
675
|
-
const defaultBuffer = resolveDefaultBuffer(originalViewport);
|
|
676
|
-
const buffer = options.buffer ?? defaultBuffer;
|
|
677
702
|
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
678
703
|
const settleMs = Math.max(0, Number(options.settleMs ?? DEFAULT_SETTLE_MS) || 0);
|
|
679
704
|
const maxScrollHeight = await expandScrollableContent(page, {
|
|
680
705
|
forceScrollableHeight: options.forceScrollableHeight,
|
|
681
706
|
visibleOnly: options.visibleOnly !== false
|
|
682
707
|
});
|
|
683
|
-
const targetHeight = Math.min(maxScrollHeight
|
|
708
|
+
const targetHeight = Math.min(maxScrollHeight, maxHeight);
|
|
684
709
|
await page.setViewportSize({
|
|
685
710
|
width: originalViewport.width,
|
|
686
711
|
height: targetHeight
|
|
@@ -2322,6 +2347,358 @@ var AntiCheat = {
|
|
|
2322
2347
|
}
|
|
2323
2348
|
};
|
|
2324
2349
|
|
|
2350
|
+
// src/device-input.js
|
|
2351
|
+
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
2352
|
+
var assertPage = (page, method) => {
|
|
2353
|
+
if (!page || typeof page !== "object") {
|
|
2354
|
+
throw new Error(`DeviceInput.${method} requires a Playwright page`);
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
var isPoint = (value) => value && typeof value === "object" && Number.isFinite(Number(value.x)) && Number.isFinite(Number(value.y));
|
|
2358
|
+
var pointFromArgs = (pointOrX, maybeY) => {
|
|
2359
|
+
if (isPoint(pointOrX)) {
|
|
2360
|
+
return { x: Number(pointOrX.x), y: Number(pointOrX.y) };
|
|
2361
|
+
}
|
|
2362
|
+
return { x: Number(pointOrX), y: Number(maybeY) };
|
|
2363
|
+
};
|
|
2364
|
+
var resolveNativeTarget = (page, target) => {
|
|
2365
|
+
if (!target) return null;
|
|
2366
|
+
if (typeof target === "string") return page.locator(target);
|
|
2367
|
+
return target;
|
|
2368
|
+
};
|
|
2369
|
+
var normalizeSelectorOptions = (options = {}) => ({
|
|
2370
|
+
targetParent: Boolean(options.targetParent ?? options.parent ?? false),
|
|
2371
|
+
pick: options.pick === "last" || options.last ? "last" : "first",
|
|
2372
|
+
visibleOnly: options.visibleOnly !== false,
|
|
2373
|
+
fallbackDomClick: Boolean(options.fallbackDomClick),
|
|
2374
|
+
forceMouse: Boolean(options.forceMouse),
|
|
2375
|
+
forceClick: Boolean(options.forceClick),
|
|
2376
|
+
mouseOptions: options.mouseOptions || {},
|
|
2377
|
+
clickOptions: options.clickOptions || {},
|
|
2378
|
+
tapOptions: options.tapOptions || {}
|
|
2379
|
+
});
|
|
2380
|
+
var waitFor = (page, timeout) => {
|
|
2381
|
+
if (!timeout) return Promise.resolve();
|
|
2382
|
+
if (page && typeof page.waitForTimeout === "function") {
|
|
2383
|
+
return page.waitForTimeout(timeout);
|
|
2384
|
+
}
|
|
2385
|
+
return new Promise((resolve) => setTimeout(resolve, timeout));
|
|
2386
|
+
};
|
|
2387
|
+
var getTargetBoundingBox = async (target) => {
|
|
2388
|
+
if (!target) return null;
|
|
2389
|
+
if (typeof target.boundingBox === "function") return target.boundingBox();
|
|
2390
|
+
if (typeof target.elementHandle === "function") {
|
|
2391
|
+
const handle = await target.elementHandle();
|
|
2392
|
+
try {
|
|
2393
|
+
return await handle?.boundingBox?.();
|
|
2394
|
+
} finally {
|
|
2395
|
+
if (handle && typeof handle.dispose === "function") {
|
|
2396
|
+
await handle.dispose().catch(() => {
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
return null;
|
|
2402
|
+
};
|
|
2403
|
+
var assertPoint = (point) => {
|
|
2404
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {
|
|
2405
|
+
throw new Error(`Invalid input point: ${JSON.stringify(point)}`);
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
var dispatchMouseMove = (page, point, options = {}) => page.mouse.move(point.x, point.y, options);
|
|
2409
|
+
var dispatchMouseStart = (page, options = {}) => page.mouse.down(options);
|
|
2410
|
+
var dispatchMouseEnd = (page, options = {}) => page.mouse.up(options);
|
|
2411
|
+
var dragWithMouse = async (page, points, options = {}) => {
|
|
2412
|
+
await dispatchMouseMove(page, points.start, { steps: 8 });
|
|
2413
|
+
await waitFor(page, options.initialDelayMs ?? 250);
|
|
2414
|
+
await dispatchMouseStart(page);
|
|
2415
|
+
await waitFor(page, options.holdDelayMs ?? 350);
|
|
2416
|
+
await dispatchMouseMove(page, points.lift, { steps: 6 });
|
|
2417
|
+
await waitFor(page, options.liftDelayMs ?? 250);
|
|
2418
|
+
for (let step = 1; step <= points.steps; step += 1) {
|
|
2419
|
+
const progress = step / points.steps;
|
|
2420
|
+
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
2421
|
+
await dispatchMouseMove(page, {
|
|
2422
|
+
x: points.lift.x + (points.end.x - points.lift.x) * easedProgress,
|
|
2423
|
+
y: points.lift.y + (points.end.y - points.lift.y) * easedProgress
|
|
2424
|
+
}, { steps: 2 });
|
|
2425
|
+
await waitFor(page, options.stepDelayMs ?? 90);
|
|
2426
|
+
}
|
|
2427
|
+
await waitFor(page, options.beforeReleaseDelayMs ?? 100);
|
|
2428
|
+
await dispatchMouseEnd(page);
|
|
2429
|
+
await waitFor(page, options.afterReleaseDelayMs ?? 100);
|
|
2430
|
+
return true;
|
|
2431
|
+
};
|
|
2432
|
+
var dragWithTouch = async (page, points, options = {}) => {
|
|
2433
|
+
let client = null;
|
|
2434
|
+
try {
|
|
2435
|
+
client = await page.context().newCDPSession(page);
|
|
2436
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2437
|
+
type: "touchStart",
|
|
2438
|
+
touchPoints: [{ x: points.start.x, y: points.start.y, id: 1 }]
|
|
2439
|
+
});
|
|
2440
|
+
await waitFor(page, options.holdDelayMs ?? 350);
|
|
2441
|
+
const totalSteps = points.steps + 1;
|
|
2442
|
+
for (let step = 1; step <= totalSteps; step += 1) {
|
|
2443
|
+
const progress = step / totalSteps;
|
|
2444
|
+
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
2445
|
+
const from = step === 1 ? points.start : points.lift;
|
|
2446
|
+
const to = points.end;
|
|
2447
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2448
|
+
type: "touchMove",
|
|
2449
|
+
touchPoints: [{
|
|
2450
|
+
x: from.x + (to.x - from.x) * easedProgress,
|
|
2451
|
+
y: from.y + (to.y - from.y) * easedProgress,
|
|
2452
|
+
id: 1
|
|
2453
|
+
}]
|
|
2454
|
+
});
|
|
2455
|
+
await waitFor(page, options.stepDelayMs ?? 90);
|
|
2456
|
+
}
|
|
2457
|
+
await waitFor(page, options.beforeReleaseDelayMs ?? 100);
|
|
2458
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2459
|
+
type: "touchEnd",
|
|
2460
|
+
touchPoints: []
|
|
2461
|
+
});
|
|
2462
|
+
await waitFor(page, options.afterReleaseDelayMs ?? 100);
|
|
2463
|
+
return true;
|
|
2464
|
+
} finally {
|
|
2465
|
+
if (client && typeof client.detach === "function") {
|
|
2466
|
+
await client.detach().catch(() => {
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2471
|
+
var clickTargetWithDevice = async (page, target, options = {}) => {
|
|
2472
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2473
|
+
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2474
|
+
if (target && resolvedDevice === Device.Mobile && !normalizedOptions.forceClick) {
|
|
2475
|
+
if (typeof target.tap === "function") {
|
|
2476
|
+
await target.tap(normalizedOptions.tapOptions);
|
|
2477
|
+
return true;
|
|
2478
|
+
}
|
|
2479
|
+
const box = await getTargetBoundingBox(target);
|
|
2480
|
+
if (box) {
|
|
2481
|
+
await DeviceInput.clickPoint(page, {
|
|
2482
|
+
x: box.x + box.width / 2,
|
|
2483
|
+
y: box.y + box.height / 2
|
|
2484
|
+
}, normalizedOptions);
|
|
2485
|
+
return true;
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
if (!target || typeof target.click !== "function") {
|
|
2489
|
+
throw new Error("DeviceInput click target does not expose click()");
|
|
2490
|
+
}
|
|
2491
|
+
await target.click(normalizedOptions.clickOptions || options);
|
|
2492
|
+
return true;
|
|
2493
|
+
};
|
|
2494
|
+
var DeviceInput = {
|
|
2495
|
+
device(page) {
|
|
2496
|
+
return resolveDeviceFromPage(page);
|
|
2497
|
+
},
|
|
2498
|
+
isMobile(page) {
|
|
2499
|
+
return resolveDeviceFromPage(page) === Device.Mobile;
|
|
2500
|
+
},
|
|
2501
|
+
async clickPoint(page, pointOrX, maybeY, options = {}) {
|
|
2502
|
+
assertPage(page, "clickPoint");
|
|
2503
|
+
const clickOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2504
|
+
const point = pointFromArgs(pointOrX, maybeY);
|
|
2505
|
+
assertPoint(point);
|
|
2506
|
+
if (resolveDeviceFromPage(page) === Device.Mobile && !clickOptions.forceMouse && page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
2507
|
+
await page.touchscreen.tap(point.x, point.y);
|
|
2508
|
+
return true;
|
|
2509
|
+
}
|
|
2510
|
+
await page.mouse.click(point.x, point.y, clickOptions.mouseOptions || {});
|
|
2511
|
+
return true;
|
|
2512
|
+
},
|
|
2513
|
+
async tapPoint(page, pointOrX, maybeY, options = {}) {
|
|
2514
|
+
return DeviceInput.clickPoint(page, pointOrX, maybeY, options);
|
|
2515
|
+
},
|
|
2516
|
+
async click(page, target, options = {}) {
|
|
2517
|
+
assertPage(page, "click");
|
|
2518
|
+
if (isPoint(target)) {
|
|
2519
|
+
return DeviceInput.clickPoint(page, target, void 0, options);
|
|
2520
|
+
}
|
|
2521
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2522
|
+
return clickTargetWithDevice(page, nativeTarget, options);
|
|
2523
|
+
},
|
|
2524
|
+
async tap(page, target, options = {}) {
|
|
2525
|
+
return DeviceInput.click(page, target, options);
|
|
2526
|
+
},
|
|
2527
|
+
async move(page, pointOrX, maybeY, options = {}) {
|
|
2528
|
+
assertPage(page, "move");
|
|
2529
|
+
const moveOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2530
|
+
const point = pointFromArgs(pointOrX, maybeY);
|
|
2531
|
+
assertPoint(point);
|
|
2532
|
+
if (resolveDeviceFromPage(page) === Device.Mobile && !moveOptions.forceMouse) {
|
|
2533
|
+
return true;
|
|
2534
|
+
}
|
|
2535
|
+
await page.mouse.move(point.x, point.y, moveOptions.mouseOptions || moveOptions);
|
|
2536
|
+
return true;
|
|
2537
|
+
},
|
|
2538
|
+
async focus(page, target, options = {}) {
|
|
2539
|
+
assertPage(page, "focus");
|
|
2540
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2541
|
+
if (!nativeTarget || typeof nativeTarget.focus !== "function") {
|
|
2542
|
+
throw new Error("DeviceInput focus target does not expose focus()");
|
|
2543
|
+
}
|
|
2544
|
+
await nativeTarget.focus(options);
|
|
2545
|
+
return true;
|
|
2546
|
+
},
|
|
2547
|
+
async fill(page, target, value, options = {}) {
|
|
2548
|
+
assertPage(page, "fill");
|
|
2549
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2550
|
+
if (!nativeTarget || typeof nativeTarget.fill !== "function") {
|
|
2551
|
+
throw new Error("DeviceInput fill target does not expose fill()");
|
|
2552
|
+
}
|
|
2553
|
+
await nativeTarget.fill(value, options);
|
|
2554
|
+
return true;
|
|
2555
|
+
},
|
|
2556
|
+
async keyboardType(page, text, options = {}) {
|
|
2557
|
+
assertPage(page, "keyboardType");
|
|
2558
|
+
await page.keyboard.type(text, options);
|
|
2559
|
+
return true;
|
|
2560
|
+
},
|
|
2561
|
+
async type(page, targetOrText, maybeText, options = {}) {
|
|
2562
|
+
assertPage(page, "type");
|
|
2563
|
+
if (maybeText == null || typeof maybeText === "object" && !Array.isArray(maybeText)) {
|
|
2564
|
+
return DeviceInput.keyboardType(page, targetOrText, maybeText || options);
|
|
2565
|
+
}
|
|
2566
|
+
const nativeTarget = resolveNativeTarget(page, targetOrText);
|
|
2567
|
+
if (!nativeTarget || typeof nativeTarget.type !== "function") {
|
|
2568
|
+
throw new Error("DeviceInput type target does not expose type()");
|
|
2569
|
+
}
|
|
2570
|
+
await nativeTarget.type(maybeText, options);
|
|
2571
|
+
return true;
|
|
2572
|
+
},
|
|
2573
|
+
async press(page, targetOrKey, maybeKey, options = {}) {
|
|
2574
|
+
assertPage(page, "press");
|
|
2575
|
+
const hasTarget = typeof maybeKey === "string";
|
|
2576
|
+
const key = hasTarget ? maybeKey : targetOrKey;
|
|
2577
|
+
const pressOptions = hasTarget ? options : maybeKey || options;
|
|
2578
|
+
if (hasTarget) {
|
|
2579
|
+
await DeviceInput.click(page, targetOrKey, pressOptions.clickOptions || {});
|
|
2580
|
+
}
|
|
2581
|
+
await page.keyboard.press(key, pressOptions.keyboardOptions || pressOptions);
|
|
2582
|
+
return true;
|
|
2583
|
+
},
|
|
2584
|
+
async selectorCenterPoint(page, selector, options = {}) {
|
|
2585
|
+
assertPage(page, "selectorCenterPoint");
|
|
2586
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2587
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2588
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2589
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2590
|
+
let rect = null;
|
|
2591
|
+
for (const node of ordered) {
|
|
2592
|
+
if (!node) continue;
|
|
2593
|
+
const target = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2594
|
+
rect = target.getBoundingClientRect?.() || null;
|
|
2595
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2596
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2597
|
+
break;
|
|
2598
|
+
}
|
|
2599
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
2600
|
+
return {
|
|
2601
|
+
x: rect.left + rect.width / 2,
|
|
2602
|
+
y: rect.top + rect.height / 2
|
|
2603
|
+
};
|
|
2604
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2605
|
+
},
|
|
2606
|
+
async domClick(page, selector, options = {}) {
|
|
2607
|
+
assertPage(page, "domClick");
|
|
2608
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2609
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2610
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2611
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2612
|
+
let target = null;
|
|
2613
|
+
for (const node of ordered) {
|
|
2614
|
+
if (!node) continue;
|
|
2615
|
+
const candidate = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2616
|
+
const rect = candidate.getBoundingClientRect?.() || null;
|
|
2617
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2618
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2619
|
+
target = candidate;
|
|
2620
|
+
break;
|
|
2621
|
+
}
|
|
2622
|
+
if (!target || typeof target.click !== "function") return false;
|
|
2623
|
+
target.click();
|
|
2624
|
+
return true;
|
|
2625
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2626
|
+
},
|
|
2627
|
+
async clickSelectorCenter(page, selector, options = {}) {
|
|
2628
|
+
assertPage(page, "clickSelectorCenter");
|
|
2629
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2630
|
+
try {
|
|
2631
|
+
const point = await DeviceInput.selectorCenterPoint(page, selector, normalizedOptions);
|
|
2632
|
+
if (point) {
|
|
2633
|
+
await DeviceInput.clickPoint(page, point, void 0, normalizedOptions);
|
|
2634
|
+
return true;
|
|
2635
|
+
}
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
if (!normalizedOptions.fallbackDomClick) throw error;
|
|
2638
|
+
}
|
|
2639
|
+
if (normalizedOptions.fallbackDomClick) {
|
|
2640
|
+
return DeviceInput.domClick(page, selector, normalizedOptions);
|
|
2641
|
+
}
|
|
2642
|
+
return false;
|
|
2643
|
+
},
|
|
2644
|
+
async clickText(frameOrPage, texts, options = {}) {
|
|
2645
|
+
for (const text of texts || []) {
|
|
2646
|
+
if (!text) continue;
|
|
2647
|
+
const candidates = [
|
|
2648
|
+
frameOrPage.getByText(text, { exact: false }).first(),
|
|
2649
|
+
frameOrPage.locator(`text=${text}`).first()
|
|
2650
|
+
];
|
|
2651
|
+
for (const candidate of candidates) {
|
|
2652
|
+
const isVisible = await candidate.isVisible({
|
|
2653
|
+
timeout: options.visibleTimeoutMs ?? options.actionVisibleTimeoutMs ?? 1500
|
|
2654
|
+
}).catch(() => false);
|
|
2655
|
+
if (!isVisible) continue;
|
|
2656
|
+
const page = options.page || (frameOrPage && typeof frameOrPage.context === "function" ? frameOrPage : null) || candidate.page?.();
|
|
2657
|
+
if (!page) {
|
|
2658
|
+
throw new Error("DeviceInput.clickText requires options.page when used with a frame locator");
|
|
2659
|
+
}
|
|
2660
|
+
await DeviceInput.click(page, candidate, options);
|
|
2661
|
+
return true;
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
return false;
|
|
2665
|
+
},
|
|
2666
|
+
async drag(page, sourceLocator, targetLocator, options = {}) {
|
|
2667
|
+
assertPage(page, "drag");
|
|
2668
|
+
const sourceBox = await sourceLocator.boundingBox();
|
|
2669
|
+
const targetBox = await targetLocator.boundingBox();
|
|
2670
|
+
if (!sourceBox || !targetBox) {
|
|
2671
|
+
throw new Error("Unable to resolve drag coordinates.");
|
|
2672
|
+
}
|
|
2673
|
+
const steps = options.steps || 10;
|
|
2674
|
+
const liftOffsetX = Math.min(18, Math.max(8, sourceBox.width * 0.12));
|
|
2675
|
+
const liftOffsetY = Math.min(12, Math.max(4, sourceBox.height * 0.08));
|
|
2676
|
+
const points = {
|
|
2677
|
+
start: {
|
|
2678
|
+
x: sourceBox.x + sourceBox.width / 2,
|
|
2679
|
+
y: sourceBox.y + sourceBox.height / 2
|
|
2680
|
+
},
|
|
2681
|
+
lift: {
|
|
2682
|
+
x: sourceBox.x + sourceBox.width / 2 + liftOffsetX,
|
|
2683
|
+
y: sourceBox.y + sourceBox.height / 2 + liftOffsetY
|
|
2684
|
+
},
|
|
2685
|
+
end: {
|
|
2686
|
+
x: targetBox.x + targetBox.width / 2,
|
|
2687
|
+
y: targetBox.y + targetBox.height / 2
|
|
2688
|
+
},
|
|
2689
|
+
steps
|
|
2690
|
+
};
|
|
2691
|
+
if (resolveDeviceFromPage(page) === Device.Mobile && !options.forceMouse) {
|
|
2692
|
+
try {
|
|
2693
|
+
return await dragWithTouch(page, points, options);
|
|
2694
|
+
} catch (error) {
|
|
2695
|
+
if (options.allowMouseFallback === false) throw error;
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
return dragWithMouse(page, points, options);
|
|
2699
|
+
}
|
|
2700
|
+
};
|
|
2701
|
+
|
|
2325
2702
|
// src/internals/humanize/desktop.js
|
|
2326
2703
|
import delay2 from "delay";
|
|
2327
2704
|
import { createCursor } from "ghost-cursor-playwright";
|
|
@@ -2837,141 +3214,6 @@ var Humanize = {
|
|
|
2837
3214
|
}
|
|
2838
3215
|
};
|
|
2839
3216
|
|
|
2840
|
-
// src/internals/humanize/machine.js
|
|
2841
|
-
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
2842
|
-
var isPoint = (value) => value && typeof value === "object" && Number.isFinite(Number(value.x)) && Number.isFinite(Number(value.y));
|
|
2843
|
-
var resolveNativeTarget = (page, target) => {
|
|
2844
|
-
if (!target) return null;
|
|
2845
|
-
if (typeof target === "string") {
|
|
2846
|
-
return page.locator(target);
|
|
2847
|
-
}
|
|
2848
|
-
return target;
|
|
2849
|
-
};
|
|
2850
|
-
var normalizeSelectorOptions = (options = {}) => ({
|
|
2851
|
-
targetParent: Boolean(options.targetParent ?? options.parent ?? false),
|
|
2852
|
-
pick: options.pick === "last" || options.last ? "last" : "first",
|
|
2853
|
-
visibleOnly: options.visibleOnly !== false,
|
|
2854
|
-
fallbackDomClick: Boolean(options.fallbackDomClick),
|
|
2855
|
-
forceMouse: Boolean(options.forceMouse)
|
|
2856
|
-
});
|
|
2857
|
-
var MachineHumanize = {
|
|
2858
|
-
async clickPoint(page, pointOrX, maybeY, options = {}) {
|
|
2859
|
-
const clickOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2860
|
-
const point = isPoint(pointOrX) ? pointOrX : { x: pointOrX, y: maybeY };
|
|
2861
|
-
const x = Number(point.x);
|
|
2862
|
-
const y = Number(point.y);
|
|
2863
|
-
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
2864
|
-
throw new Error(`Invalid click point: ${JSON.stringify(point)}`);
|
|
2865
|
-
}
|
|
2866
|
-
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2867
|
-
if (resolvedDevice === Device.Mobile && !clickOptions.forceMouse && page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
2868
|
-
await page.touchscreen.tap(x, y);
|
|
2869
|
-
return true;
|
|
2870
|
-
}
|
|
2871
|
-
await page.mouse.click(x, y, clickOptions.mouseOptions || {});
|
|
2872
|
-
return true;
|
|
2873
|
-
},
|
|
2874
|
-
async click(page, target, options = {}) {
|
|
2875
|
-
if (isPoint(target)) {
|
|
2876
|
-
return MachineHumanize.clickPoint(page, target, void 0, options);
|
|
2877
|
-
}
|
|
2878
|
-
const nativeTarget = resolveNativeTarget(page, target);
|
|
2879
|
-
if (!nativeTarget || typeof nativeTarget.click !== "function") {
|
|
2880
|
-
throw new Error("Machine click target does not expose click()");
|
|
2881
|
-
}
|
|
2882
|
-
await nativeTarget.click(options.clickOptions || options);
|
|
2883
|
-
return true;
|
|
2884
|
-
},
|
|
2885
|
-
async focus(page, target, options = {}) {
|
|
2886
|
-
const nativeTarget = resolveNativeTarget(page, target);
|
|
2887
|
-
if (!nativeTarget || typeof nativeTarget.focus !== "function") {
|
|
2888
|
-
throw new Error("Machine focus target does not expose focus()");
|
|
2889
|
-
}
|
|
2890
|
-
await nativeTarget.focus(options);
|
|
2891
|
-
return true;
|
|
2892
|
-
},
|
|
2893
|
-
async fill(page, target, value, options = {}) {
|
|
2894
|
-
const nativeTarget = resolveNativeTarget(page, target);
|
|
2895
|
-
if (!nativeTarget || typeof nativeTarget.fill !== "function") {
|
|
2896
|
-
throw new Error("Machine fill target does not expose fill()");
|
|
2897
|
-
}
|
|
2898
|
-
await nativeTarget.fill(value, options);
|
|
2899
|
-
return true;
|
|
2900
|
-
},
|
|
2901
|
-
async keyboardType(page, text, options = {}) {
|
|
2902
|
-
await page.keyboard.type(text, options);
|
|
2903
|
-
return true;
|
|
2904
|
-
},
|
|
2905
|
-
async type(page, targetOrText, maybeText, options = {}) {
|
|
2906
|
-
if (maybeText == null || typeof maybeText === "object" && !Array.isArray(maybeText)) {
|
|
2907
|
-
return MachineHumanize.keyboardType(page, targetOrText, maybeText || options);
|
|
2908
|
-
}
|
|
2909
|
-
const nativeTarget = resolveNativeTarget(page, targetOrText);
|
|
2910
|
-
if (!nativeTarget || typeof nativeTarget.type !== "function") {
|
|
2911
|
-
throw new Error("Machine type target does not expose type()");
|
|
2912
|
-
}
|
|
2913
|
-
await nativeTarget.type(maybeText, options);
|
|
2914
|
-
return true;
|
|
2915
|
-
},
|
|
2916
|
-
async selectorCenterPoint(page, selector, options = {}) {
|
|
2917
|
-
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2918
|
-
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2919
|
-
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2920
|
-
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2921
|
-
let rect = null;
|
|
2922
|
-
for (const node of ordered) {
|
|
2923
|
-
if (!node) continue;
|
|
2924
|
-
const target = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2925
|
-
rect = target.getBoundingClientRect?.() || null;
|
|
2926
|
-
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2927
|
-
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2928
|
-
break;
|
|
2929
|
-
}
|
|
2930
|
-
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
2931
|
-
return {
|
|
2932
|
-
x: rect.left + rect.width / 2,
|
|
2933
|
-
y: rect.top + rect.height / 2
|
|
2934
|
-
};
|
|
2935
|
-
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2936
|
-
},
|
|
2937
|
-
async domClick(page, selector, options = {}) {
|
|
2938
|
-
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2939
|
-
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2940
|
-
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2941
|
-
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2942
|
-
let target = null;
|
|
2943
|
-
for (const node of ordered) {
|
|
2944
|
-
if (!node) continue;
|
|
2945
|
-
const candidate = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2946
|
-
const rect = candidate.getBoundingClientRect?.() || null;
|
|
2947
|
-
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2948
|
-
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2949
|
-
target = candidate;
|
|
2950
|
-
break;
|
|
2951
|
-
}
|
|
2952
|
-
if (!target || typeof target.click !== "function") return false;
|
|
2953
|
-
target.click();
|
|
2954
|
-
return true;
|
|
2955
|
-
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2956
|
-
},
|
|
2957
|
-
async clickSelectorCenter(page, selector, options = {}) {
|
|
2958
|
-
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2959
|
-
try {
|
|
2960
|
-
const point = await MachineHumanize.selectorCenterPoint(page, selector, normalizedOptions);
|
|
2961
|
-
if (point) {
|
|
2962
|
-
await MachineHumanize.clickPoint(page, point, void 0, normalizedOptions);
|
|
2963
|
-
return true;
|
|
2964
|
-
}
|
|
2965
|
-
} catch (error) {
|
|
2966
|
-
if (!normalizedOptions.fallbackDomClick) throw error;
|
|
2967
|
-
}
|
|
2968
|
-
if (normalizedOptions.fallbackDomClick) {
|
|
2969
|
-
return MachineHumanize.domClick(page, selector, normalizedOptions);
|
|
2970
|
-
}
|
|
2971
|
-
return false;
|
|
2972
|
-
}
|
|
2973
|
-
};
|
|
2974
|
-
|
|
2975
3217
|
// src/internals/humanize/shared.js
|
|
2976
3218
|
import delay3 from "delay";
|
|
2977
3219
|
var jitterMs = (base, jitterPercent = 0.3) => {
|
|
@@ -3800,9 +4042,6 @@ var callDelegate = (method, page, args) => {
|
|
|
3800
4042
|
return delegate[method](page, ...args);
|
|
3801
4043
|
};
|
|
3802
4044
|
var Humanize2 = {
|
|
3803
|
-
// M = Machine: native/mechanical operations for兼容路径。
|
|
3804
|
-
// 这组 API 不做人类化处理,只保留原生 click / focus / fill / type 等语义。
|
|
3805
|
-
M: MachineHumanize,
|
|
3806
4045
|
jitterMs(base, jitterPercent = 0.3) {
|
|
3807
4046
|
return Humanize.jitterMs(base, jitterPercent);
|
|
3808
4047
|
},
|
|
@@ -4378,42 +4617,14 @@ var clickCaptchaAction = async (frame, texts, options) => {
|
|
|
4378
4617
|
if (!isVisible) {
|
|
4379
4618
|
continue;
|
|
4380
4619
|
}
|
|
4381
|
-
await
|
|
4620
|
+
await DeviceInput.click(options.page, candidate);
|
|
4382
4621
|
return true;
|
|
4383
4622
|
}
|
|
4384
4623
|
}
|
|
4385
4624
|
return false;
|
|
4386
4625
|
};
|
|
4387
|
-
var
|
|
4388
|
-
|
|
4389
|
-
const targetBox = await targetLocator.boundingBox();
|
|
4390
|
-
if (!sourceBox || !targetBox) {
|
|
4391
|
-
throw new Error("Unable to resolve captcha drag coordinates.");
|
|
4392
|
-
}
|
|
4393
|
-
const startX = sourceBox.x + sourceBox.width / 2;
|
|
4394
|
-
const startY = sourceBox.y + sourceBox.height / 2;
|
|
4395
|
-
const endX = targetBox.x + targetBox.width / 2;
|
|
4396
|
-
const endY = targetBox.y + targetBox.height / 2;
|
|
4397
|
-
const steps = 10;
|
|
4398
|
-
const liftOffsetX = Math.min(18, Math.max(8, sourceBox.width * 0.12));
|
|
4399
|
-
const liftOffsetY = Math.min(12, Math.max(4, sourceBox.height * 0.08));
|
|
4400
|
-
await page.mouse.move(startX, startY, { steps: 8 });
|
|
4401
|
-
await page.waitForTimeout(250);
|
|
4402
|
-
await page.mouse.down();
|
|
4403
|
-
await page.waitForTimeout(350);
|
|
4404
|
-
await page.mouse.move(startX + liftOffsetX, startY + liftOffsetY, { steps: 6 });
|
|
4405
|
-
await page.waitForTimeout(250);
|
|
4406
|
-
for (let step = 1; step <= steps; step++) {
|
|
4407
|
-
const progress = step / steps;
|
|
4408
|
-
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
4409
|
-
const currentX = startX + liftOffsetX + (endX - startX - liftOffsetX) * easedProgress;
|
|
4410
|
-
const currentY = startY + liftOffsetY + (endY - startY - liftOffsetY) * easedProgress;
|
|
4411
|
-
await page.mouse.move(currentX, currentY, { steps: 2 });
|
|
4412
|
-
await page.waitForTimeout(90);
|
|
4413
|
-
}
|
|
4414
|
-
await page.waitForTimeout(100);
|
|
4415
|
-
await page.mouse.up();
|
|
4416
|
-
await page.waitForTimeout(100);
|
|
4626
|
+
var dragCaptchaAction = async (page, sourceLocator, targetLocator) => {
|
|
4627
|
+
await DeviceInput.drag(page, sourceLocator, targetLocator);
|
|
4417
4628
|
};
|
|
4418
4629
|
|
|
4419
4630
|
// src/internals/captcha/bytedance.js
|
|
@@ -4509,7 +4720,7 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
4509
4720
|
return { iframeLocator, frame };
|
|
4510
4721
|
};
|
|
4511
4722
|
var refreshCaptcha = async (page, frame, options) => {
|
|
4512
|
-
const clicked = await clickCaptchaAction(frame, options.refreshTexts, options).catch(() => false);
|
|
4723
|
+
const clicked = await clickCaptchaAction(frame, options.refreshTexts, { ...options, page }).catch(() => false);
|
|
4513
4724
|
if (!clicked) {
|
|
4514
4725
|
logger10.warn("Refresh button not found.");
|
|
4515
4726
|
return false;
|
|
@@ -4635,9 +4846,9 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
4635
4846
|
state: "visible",
|
|
4636
4847
|
timeout: config.sourceImageVisibleTimeoutMs
|
|
4637
4848
|
});
|
|
4638
|
-
await
|
|
4849
|
+
await dragCaptchaAction(page, sourceImage, dropTarget);
|
|
4639
4850
|
}
|
|
4640
|
-
const submitted = await clickCaptchaAction(frame, config.submitTexts, config).catch(() => false);
|
|
4851
|
+
const submitted = await clickCaptchaAction(frame, config.submitTexts, { ...config, page }).catch(() => false);
|
|
4641
4852
|
if (!submitted) {
|
|
4642
4853
|
logger10.warn("\u672A\u627E\u5230\u63D0\u4EA4\u6309\u94AE\uFF0C\u53EF\u80FD\u4F1A\u81EA\u52A8\u63D0\u4EA4\u3002");
|
|
4643
4854
|
}
|
|
@@ -8343,7 +8554,6 @@ var Share = {
|
|
|
8343
8554
|
*
|
|
8344
8555
|
* @param {import('playwright').Page} page
|
|
8345
8556
|
* @param {Object} [options]
|
|
8346
|
-
* @param {number} [options.buffer] 额外缓冲高度;默认按视口高度自动计算,传 0 可关闭
|
|
8347
8557
|
* @param {boolean} [options.restore]
|
|
8348
8558
|
* @param {number} [options.maxHeight]
|
|
8349
8559
|
* @param {number} [options.maxBytes] 默认 5MiB,返回 base64 超过后会压缩
|
|
@@ -8356,7 +8566,6 @@ var Share = {
|
|
|
8356
8566
|
const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
|
|
8357
8567
|
const compression = resolveImageCompression(options);
|
|
8358
8568
|
const captureOptions = {
|
|
8359
|
-
...Object.prototype.hasOwnProperty.call(options, "buffer") ? { buffer: options.buffer } : {},
|
|
8360
8569
|
restore,
|
|
8361
8570
|
maxHeight: options.maxHeight,
|
|
8362
8571
|
type: "png",
|
|
@@ -8382,6 +8591,7 @@ var usePlaywrightToolKit = () => {
|
|
|
8382
8591
|
return {
|
|
8383
8592
|
ApifyKit,
|
|
8384
8593
|
AntiCheat,
|
|
8594
|
+
DeviceInput,
|
|
8385
8595
|
Humanize: Humanize2,
|
|
8386
8596
|
Launch,
|
|
8387
8597
|
LiveView,
|