@skrillex1224/playwright-toolkit 2.1.20 → 2.1.21
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 +98 -107
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +98 -107
- 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 restoreOnce = 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 restoreOnce();
|
|
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,6 +755,18 @@ var Humanize = {
|
|
|
776
755
|
} else {
|
|
777
756
|
element = target;
|
|
778
757
|
}
|
|
758
|
+
let restoreScroll = async () => {
|
|
759
|
+
};
|
|
760
|
+
let restored = false;
|
|
761
|
+
const restoreOnce = async () => {
|
|
762
|
+
if (restored) return;
|
|
763
|
+
restored = true;
|
|
764
|
+
try {
|
|
765
|
+
await restoreScroll();
|
|
766
|
+
} catch (restoreError) {
|
|
767
|
+
logger4.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
|
|
768
|
+
}
|
|
769
|
+
};
|
|
779
770
|
if (scrollIfNeeded) {
|
|
780
771
|
const scrollResult = await this.humanScroll(page, element);
|
|
781
772
|
restoreScroll = scrollResult.restore || restoreScroll;
|
|
@@ -799,7 +790,7 @@ var Humanize = {
|
|
|
799
790
|
logger4.success("humanClick");
|
|
800
791
|
return true;
|
|
801
792
|
} catch (error) {
|
|
802
|
-
|
|
793
|
+
logger4.fail("humanClick", error);
|
|
803
794
|
logger4.fail("humanClick", error);
|
|
804
795
|
throw error;
|
|
805
796
|
}
|