@skrillex1224/playwright-toolkit 2.1.20 → 2.1.22
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/dist/index.cjs +100 -111
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +100 -111
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -564,7 +564,7 @@ var Humanize = {
|
|
|
564
564
|
},
|
|
565
565
|
/**
|
|
566
566
|
* 渐进式滚动到元素可见(仅处理 Y 轴滚动)
|
|
567
|
-
*
|
|
567
|
+
* 返回 restore 方法,用于将滚动容器恢复到原位置
|
|
568
568
|
*
|
|
569
569
|
* @param {import('playwright').Page} page
|
|
570
570
|
* @param {string|import('playwright').ElementHandle} target - CSS 选择器或元素句柄
|
|
@@ -574,7 +574,6 @@ var Humanize = {
|
|
|
574
574
|
* @param {number} [options.maxStep=220] - 单次滚动最大步长
|
|
575
575
|
*/
|
|
576
576
|
async humanScroll(page, target, options = {}) {
|
|
577
|
-
const cursor = $GetCursor(page);
|
|
578
577
|
const { maxSteps = 25, minStep = 80, maxStep = 220 } = options;
|
|
579
578
|
let element;
|
|
580
579
|
if (typeof target === "string") {
|
|
@@ -586,13 +585,13 @@ var Humanize = {
|
|
|
586
585
|
} else {
|
|
587
586
|
element = target;
|
|
588
587
|
}
|
|
589
|
-
const isScrollable = (node) => {
|
|
590
|
-
if (!node || node === document.body) return false;
|
|
591
|
-
const style = window.getComputedStyle(node);
|
|
592
|
-
const overflowY = style.overflowY || style.overflow;
|
|
593
|
-
return (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight + 1;
|
|
594
|
-
};
|
|
595
588
|
const needsScroll = await element.evaluate((el) => {
|
|
589
|
+
const isScrollable = (node) => {
|
|
590
|
+
if (!node || node === document.body) return false;
|
|
591
|
+
const style = window.getComputedStyle(node);
|
|
592
|
+
const overflowY = style.overflowY || style.overflow;
|
|
593
|
+
return (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight + 1;
|
|
594
|
+
};
|
|
596
595
|
const rect = el.getBoundingClientRect();
|
|
597
596
|
if (!rect || rect.width === 0 || rect.height === 0) return true;
|
|
598
597
|
const inViewport = rect.top >= 0 && rect.bottom <= window.innerHeight;
|
|
@@ -613,118 +612,110 @@ var Humanize = {
|
|
|
613
612
|
return { element, didScroll: false, restore: async () => {
|
|
614
613
|
} };
|
|
615
614
|
}
|
|
616
|
-
const
|
|
617
|
-
const
|
|
615
|
+
const scrollStateHandle = await element.evaluateHandle((el) => {
|
|
616
|
+
const isScrollable = (node) => {
|
|
617
|
+
if (!node || node === document.body) return false;
|
|
618
|
+
const style = window.getComputedStyle(node);
|
|
619
|
+
const overflowY = style.overflowY || style.overflow;
|
|
620
|
+
return (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight + 1;
|
|
621
|
+
};
|
|
622
|
+
const scrollables = [];
|
|
623
|
+
const addNode = (node) => {
|
|
624
|
+
if (!node || scrollables.some((item) => item.el === node)) return;
|
|
625
|
+
scrollables.push({
|
|
626
|
+
el: node,
|
|
627
|
+
top: node.scrollTop
|
|
628
|
+
});
|
|
629
|
+
};
|
|
618
630
|
let current = el.parentElement;
|
|
619
631
|
while (current) {
|
|
620
|
-
if (isScrollable(current))
|
|
632
|
+
if (isScrollable(current)) addNode(current);
|
|
621
633
|
current = current.parentElement;
|
|
622
634
|
}
|
|
623
635
|
const scrollingElement = document.scrollingElement || document.documentElement;
|
|
624
|
-
if (scrollingElement)
|
|
625
|
-
return
|
|
636
|
+
if (scrollingElement) addNode(scrollingElement);
|
|
637
|
+
return scrollables;
|
|
626
638
|
});
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const top = await itemEl.evaluate((el) => el.scrollTop);
|
|
637
|
-
scrollables.push({ el: itemEl, top });
|
|
638
|
-
} else {
|
|
639
|
-
await itemHandle.dispose();
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
} finally {
|
|
643
|
-
await scrollablesHandle.dispose();
|
|
644
|
-
}
|
|
645
|
-
const pickTarget = async () => {
|
|
646
|
-
const handle = await element.evaluateHandle((el) => {
|
|
639
|
+
for (let i = 0; i < maxSteps; i++) {
|
|
640
|
+
const step = minStep + Math.floor(Math.random() * (maxStep - minStep));
|
|
641
|
+
const result = await element.evaluate((el, stepPx) => {
|
|
642
|
+
const isScrollable = (node) => {
|
|
643
|
+
if (!node || node === document.body) return false;
|
|
644
|
+
const style = window.getComputedStyle(node);
|
|
645
|
+
const overflowY = style.overflowY || style.overflow;
|
|
646
|
+
return (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight + 1;
|
|
647
|
+
};
|
|
647
648
|
const rect = el.getBoundingClientRect();
|
|
648
649
|
if (!rect || rect.width === 0 || rect.height === 0) {
|
|
649
|
-
return {
|
|
650
|
+
return { moved: false, inView: false };
|
|
650
651
|
}
|
|
651
|
-
const
|
|
652
|
+
const scrollables = [];
|
|
652
653
|
let current = el.parentElement;
|
|
653
654
|
while (current) {
|
|
654
|
-
if (isScrollable(current))
|
|
655
|
+
if (isScrollable(current)) scrollables.push(current);
|
|
655
656
|
current = current.parentElement;
|
|
656
657
|
}
|
|
657
658
|
const scrollingElement = document.scrollingElement || document.documentElement;
|
|
658
|
-
if (scrollingElement)
|
|
659
|
-
|
|
659
|
+
if (scrollingElement) scrollables.push(scrollingElement);
|
|
660
|
+
let target2 = null;
|
|
661
|
+
for (const container of scrollables) {
|
|
660
662
|
const crect = container === scrollingElement ? { top: 0, bottom: window.innerHeight } : container.getBoundingClientRect();
|
|
661
663
|
if (rect.top < crect.top + 2) {
|
|
662
|
-
|
|
664
|
+
target2 = { container, direction: -1 };
|
|
665
|
+
break;
|
|
663
666
|
}
|
|
664
667
|
if (rect.bottom > crect.bottom - 2) {
|
|
665
|
-
|
|
668
|
+
target2 = { container, direction: 1 };
|
|
669
|
+
break;
|
|
666
670
|
}
|
|
667
671
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const inViewHandle = await handle.getProperty("inView");
|
|
671
|
-
const inView = await inViewHandle.jsonValue();
|
|
672
|
-
await inViewHandle.dispose();
|
|
673
|
-
if (inView) {
|
|
674
|
-
await handle.dispose();
|
|
675
|
-
return { inView: true };
|
|
676
|
-
}
|
|
677
|
-
const directionHandle = await handle.getProperty("direction");
|
|
678
|
-
const direction = await directionHandle.jsonValue();
|
|
679
|
-
await directionHandle.dispose();
|
|
680
|
-
const containerHandle = (await handle.getProperty("container")).asElement();
|
|
681
|
-
await handle.dispose();
|
|
682
|
-
return { inView: false, direction, container: containerHandle };
|
|
683
|
-
};
|
|
684
|
-
for (let i = 0; i < maxSteps; i++) {
|
|
685
|
-
const { inView, direction, container } = await pickTarget();
|
|
686
|
-
if (inView) break;
|
|
687
|
-
if (!container || !direction) break;
|
|
688
|
-
try {
|
|
689
|
-
const box = await container.boundingBox();
|
|
690
|
-
if (!box) break;
|
|
691
|
-
const x = box.x + box.width / 2 + (Math.random() - 0.5) * box.width * 0.2;
|
|
692
|
-
const y = box.y + box.height / 2 + (Math.random() - 0.5) * box.height * 0.2;
|
|
693
|
-
await cursor.actions.move({ x, y });
|
|
694
|
-
const step = minStep + Math.floor(Math.random() * (maxStep - minStep));
|
|
695
|
-
await page.mouse.wheel(0, direction * step);
|
|
696
|
-
if (Math.random() < 0.12) {
|
|
697
|
-
await (0, import_delay2.default)(this.jitterMs(60, 0.5));
|
|
698
|
-
const backStep = Math.max(20, Math.floor(step * (0.15 + Math.random() * 0.2)));
|
|
699
|
-
await page.mouse.wheel(0, -direction * backStep);
|
|
672
|
+
if (!target2) {
|
|
673
|
+
return { moved: false, inView: true };
|
|
700
674
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
675
|
+
const maxScroll = target2.container.scrollHeight - target2.container.clientHeight;
|
|
676
|
+
if (maxScroll <= 0) {
|
|
677
|
+
return { moved: false, inView: false };
|
|
678
|
+
}
|
|
679
|
+
const before = target2.container.scrollTop;
|
|
680
|
+
let next = before + target2.direction * stepPx;
|
|
681
|
+
if (next < 0) next = 0;
|
|
682
|
+
if (next > maxScroll) next = maxScroll;
|
|
683
|
+
if (next === before) {
|
|
684
|
+
return { moved: false, inView: false };
|
|
685
|
+
}
|
|
686
|
+
target2.container.scrollTop = next;
|
|
687
|
+
return { moved: true, inView: false };
|
|
688
|
+
}, step);
|
|
689
|
+
if (result.inView) break;
|
|
690
|
+
if (!result.moved) break;
|
|
704
691
|
await (0, import_delay2.default)(this.jitterMs(120, 0.4));
|
|
705
692
|
}
|
|
706
693
|
const restore = async () => {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
if (!
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
694
|
+
if (!scrollStateHandle) return;
|
|
695
|
+
try {
|
|
696
|
+
const restoreOnce2 = async () => page.evaluate((state) => {
|
|
697
|
+
if (!Array.isArray(state) || state.length === 0) return true;
|
|
698
|
+
let done = true;
|
|
699
|
+
for (const item of state) {
|
|
700
|
+
if (!item || !item.el) continue;
|
|
701
|
+
const current = item.el.scrollTop;
|
|
702
|
+
const target2 = item.top;
|
|
703
|
+
if (Math.abs(current - target2) > 1) {
|
|
704
|
+
done = false;
|
|
705
|
+
const delta = target2 - current;
|
|
706
|
+
const step = Math.sign(delta) * Math.max(20, Math.min(120, Math.abs(delta) * 0.3));
|
|
707
|
+
item.el.scrollTop = current + step;
|
|
708
|
+
}
|
|
722
709
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
await
|
|
710
|
+
return done;
|
|
711
|
+
}, scrollStateHandle);
|
|
712
|
+
for (let i = 0; i < 10; i++) {
|
|
713
|
+
const done = await restoreOnce2();
|
|
714
|
+
if (done) break;
|
|
715
|
+
await (0, import_delay2.default)(this.jitterMs(80, 0.4));
|
|
727
716
|
}
|
|
717
|
+
} finally {
|
|
718
|
+
await scrollStateHandle.dispose();
|
|
728
719
|
}
|
|
729
720
|
};
|
|
730
721
|
return { element, didScroll: true, restore };
|
|
@@ -744,18 +735,6 @@ var Humanize = {
|
|
|
744
735
|
const { reactionDelay = 250, throwOnMissing = true, scrollIfNeeded = true } = options;
|
|
745
736
|
const targetDesc = target == null ? "Current Position" : typeof target === "string" ? target : "ElementHandle";
|
|
746
737
|
logger4.start("humanClick", `target=${targetDesc}`);
|
|
747
|
-
let restoreScroll = async () => {
|
|
748
|
-
};
|
|
749
|
-
let restored = false;
|
|
750
|
-
const restoreOnce = async () => {
|
|
751
|
-
if (restored) return;
|
|
752
|
-
restored = true;
|
|
753
|
-
try {
|
|
754
|
-
await restoreScroll();
|
|
755
|
-
} catch (restoreError) {
|
|
756
|
-
logger4.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
|
|
757
|
-
}
|
|
758
|
-
};
|
|
759
738
|
try {
|
|
760
739
|
if (target == null) {
|
|
761
740
|
await (0, import_delay2.default)(this.jitterMs(reactionDelay, 0.4));
|
|
@@ -776,13 +755,24 @@ var Humanize = {
|
|
|
776
755
|
} else {
|
|
777
756
|
element = target;
|
|
778
757
|
}
|
|
758
|
+
const restoreOnce2 = async () => {
|
|
759
|
+
if (restoreOnce2.restored) return;
|
|
760
|
+
restoreOnce2.restored = true;
|
|
761
|
+
if (typeof restoreOnce2.do !== "function") return;
|
|
762
|
+
try {
|
|
763
|
+
await (0, import_delay2.default)(this.jitterMs(1e3));
|
|
764
|
+
await restoreOnce2.do();
|
|
765
|
+
} catch (restoreError) {
|
|
766
|
+
logger4.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
779
769
|
if (scrollIfNeeded) {
|
|
780
|
-
const
|
|
781
|
-
|
|
770
|
+
const { restore, didScroll } = await this.humanScroll(page, element);
|
|
771
|
+
restoreOnce2.do = didScroll ? restore : null;
|
|
782
772
|
}
|
|
783
773
|
const box = await element.boundingBox();
|
|
784
774
|
if (!box) {
|
|
785
|
-
await
|
|
775
|
+
await restoreOnce2();
|
|
786
776
|
if (throwOnMissing) {
|
|
787
777
|
throw new Error("\u65E0\u6CD5\u83B7\u53D6\u5143\u7D20\u4F4D\u7F6E");
|
|
788
778
|
}
|
|
@@ -794,8 +784,7 @@ var Humanize = {
|
|
|
794
784
|
await cursor.actions.move({ x, y });
|
|
795
785
|
await (0, import_delay2.default)(this.jitterMs(reactionDelay, 0.4));
|
|
796
786
|
await cursor.actions.click();
|
|
797
|
-
await (
|
|
798
|
-
await restoreOnce();
|
|
787
|
+
await restoreOnce2();
|
|
799
788
|
logger4.success("humanClick");
|
|
800
789
|
return true;
|
|
801
790
|
} catch (error) {
|