mirror of
https://github.com/CloakHQ/CloakBrowser.git
synced 2026-06-23 11:41:46 +02:00
feat(js): add types to humanized method options (#205)
Replace `any` with proper TypeScript types on all humanized method options (Playwright, Puppeteer, ElementHandle, Frame). Support flat per-call config overrides alongside existing `human_config` style. Internalize `humanIdle` duration computation with backward-compatible overloads. Co-authored-by: Eternal <chasezou09@gmail.com>
This commit is contained in:
@@ -319,7 +319,11 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
}
|
||||
|
||||
// ==== goto ====
|
||||
const humanGoto = async (url: string, options?: any) => {
|
||||
const humanGoto = async (url: string, options?: {
|
||||
referer?: string;
|
||||
timeout?: number;
|
||||
waitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
|
||||
}) => {
|
||||
const response = await originals.goto(url, options);
|
||||
stealth.invalidate();
|
||||
patchFrames(page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
@@ -327,11 +331,18 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// ==== click (with clickCount support for dblclick) ====
|
||||
const humanClickFn = async (selector: string, options?: any) => {
|
||||
const humanClickFn = async (selector: string, options?: Partial<HumanConfig> & ({
|
||||
button?: 'left' | 'right' | 'middle' | 'back' | 'forward';
|
||||
clickCount?: number;
|
||||
count?: number;
|
||||
delay?: number;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
timeout?: number;
|
||||
})) => {
|
||||
await ensureCursorInit();
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
const { box, cursorX, cursorY } = await scrollToElement(page, raw, selector, cursor.x, cursor.y, callCfg, options?.timeout);
|
||||
cursor.x = cursorX;
|
||||
@@ -355,11 +366,11 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// ==== hover ====
|
||||
const humanHoverFn = async (selector: string, options?: any) => {
|
||||
const humanHoverFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await ensureCursorInit();
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
const { box, cursorX, cursorY } = await scrollToElement(page, raw, selector, cursor.x, cursor.y, callCfg, options?.timeout);
|
||||
cursor.x = cursorX;
|
||||
@@ -371,8 +382,12 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// ==== type ====
|
||||
const humanTypeFn = async (selector: string, text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const humanTypeFn = async (selector: string, text: string, options?: Partial<HumanConfig> & ({
|
||||
delay?: number;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
timeout?: number;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
await sleep(randRange(callCfg.field_switch_delay));
|
||||
await humanClickFn(selector, options);
|
||||
await sleep(rand(100, 250));
|
||||
@@ -395,7 +410,7 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// ==== tap ====
|
||||
const humanTapFn = async (selector: string, options?: any) => {
|
||||
const humanTapFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await humanClickFn(selector, options);
|
||||
};
|
||||
|
||||
@@ -413,14 +428,19 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
// ============================================================
|
||||
// Mouse patches
|
||||
// ============================================================
|
||||
page.mouse.move = async (x: number, y: number, options?: any) => {
|
||||
page.mouse.move = async (x: number, y: number, options?: { steps?: number }) => {
|
||||
await ensureCursorInit();
|
||||
await humanMove(raw, cursor.x, cursor.y, x, y, cfg);
|
||||
cursor.x = x;
|
||||
cursor.y = y;
|
||||
};
|
||||
|
||||
page.mouse.click = async (x: number, y: number, options?: any) => {
|
||||
page.mouse.click = async (x: number, y: number, options?: {
|
||||
button?: 'left' | 'right' | 'middle' | 'back' | 'forward';
|
||||
clickCount?: number;
|
||||
count?: number;
|
||||
delay?: number;
|
||||
}) => {
|
||||
await ensureCursorInit();
|
||||
await humanMove(raw, cursor.x, cursor.y, x, y, cfg);
|
||||
cursor.x = x;
|
||||
@@ -455,7 +475,7 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
(page.mouse as any).dragAndDrop = async (
|
||||
start: { x: number; y: number },
|
||||
target: { x: number; y: number },
|
||||
options?: any,
|
||||
options?: { delay?: number },
|
||||
) => {
|
||||
await ensureCursorInit();
|
||||
await humanMove(raw, cursor.x, cursor.y, start.x, start.y, cfg);
|
||||
@@ -475,12 +495,12 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
// ============================================================
|
||||
// Keyboard patches
|
||||
// ============================================================
|
||||
page.keyboard.type = async (text: string, options?: any) => {
|
||||
page.keyboard.type = async (text: string, options?: { delay?: number }) => {
|
||||
const cdp = await ensureCdp();
|
||||
await humanType(page, rawKb, text, cfg, cdp);
|
||||
};
|
||||
|
||||
page.keyboard.press = async (key: any, options?: any) => {
|
||||
page.keyboard.press = async (key: any, options?: { delay?: number }) => {
|
||||
await sleep(rand(20, 60));
|
||||
await originals.keyboardDown(key as any);
|
||||
await sleep(randRange(cfg.key_hold));
|
||||
@@ -551,7 +571,11 @@ function patchElementHandle(
|
||||
return els;
|
||||
};
|
||||
|
||||
(page as any).waitForSelector = async (selector: string, options?: any) => {
|
||||
(page as any).waitForSelector = async (selector: string, options?: {
|
||||
hidden?: boolean;
|
||||
timeout?: number;
|
||||
visible?: boolean;
|
||||
}) => {
|
||||
const el = await origWaitForSelector(selector, options);
|
||||
if (el) patchSingleElementHandle(el, page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
return el;
|
||||
@@ -603,14 +627,18 @@ function patchSingleElementHandle(
|
||||
return children;
|
||||
};
|
||||
|
||||
(el as any).waitForSelector = async (selector: string, options?: any) => {
|
||||
(el as any).waitForSelector = async (selector: string, options?: {
|
||||
hidden?: boolean;
|
||||
timeout?: number;
|
||||
visible?: boolean;
|
||||
}) => {
|
||||
const child = await origElWaitForSelector(selector, options);
|
||||
if (child) patchSingleElementHandle(child, page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
return child;
|
||||
};
|
||||
|
||||
// --- Helper: get box and move cursor. Accepts a per-call ``callCfg``
|
||||
// so type/fill overrides like ``el.type(text, { human_config: {...} })``
|
||||
// so type/fill overrides like ``el.type(text, { typing_delay: 30 })``
|
||||
// carry through to mouse timing for that single call. Also scrolls into
|
||||
// view first so off-screen elements work (#129, #172 follow-up).
|
||||
const moveToElement = async (callCfg: HumanConfig = cfg) => {
|
||||
@@ -633,7 +661,7 @@ function patchSingleElementHandle(
|
||||
const target = clickTarget(box, isInp, callCfg);
|
||||
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
|
||||
await humanMove(raw, cursor.x, cursor.y, target.x, target.y, callCfg);
|
||||
@@ -643,8 +671,14 @@ function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.click() ---
|
||||
(el as any).click = async (options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).click = async (options?: Partial<HumanConfig> & ({
|
||||
button?: 'left' | 'right' | 'middle' | 'back' | 'forward';
|
||||
clickCount?: number;
|
||||
count?: number;
|
||||
delay?: number;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElClick(options);
|
||||
|
||||
@@ -667,8 +701,8 @@ function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.type() ---
|
||||
(el as any).type = async (text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).type = async (text: string, options?: Partial<HumanConfig> & ({ delay?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElType(text, options);
|
||||
await humanClick(raw, info.isInp, callCfg);
|
||||
@@ -684,8 +718,8 @@ function patchSingleElementHandle(
|
||||
// page.click(). Only patched when the underlying ElementHandle exposes
|
||||
// ``scrollIntoView`` (Puppeteer v22+).
|
||||
if (origElScrollIntoView) {
|
||||
(el as any).scrollIntoView = async (options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).scrollIntoView = async (options?: Partial<HumanConfig> & { human_config?: Partial<HumanConfig> }) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
await (page as any)._ensureCursorInit();
|
||||
try {
|
||||
const { cursorX, cursorY } = await humanScrollIntoView(
|
||||
@@ -703,7 +737,7 @@ function patchSingleElementHandle(
|
||||
|
||||
// --- el.press() ---
|
||||
if (origElPress) {
|
||||
(el as any).press = async (key: string, options?: any) => {
|
||||
(el as any).press = async (key: string, options?: { delay?: number }) => {
|
||||
await sleep(rand(20, 60));
|
||||
await originals.keyboardDown(key as any);
|
||||
await sleep(randRange(cfg.key_hold));
|
||||
@@ -742,7 +776,7 @@ function patchSingleElementHandle(
|
||||
|
||||
// --- el.drop() ---
|
||||
if (origElDrop) {
|
||||
(el as any).drop = async (draggable: ElementHandle, options?: any) => {
|
||||
(el as any).drop = async (draggable: ElementHandle, options?: { delay?: number }) => {
|
||||
const srcBox = await draggable.boundingBox();
|
||||
const tgtBox = await el.boundingBox();
|
||||
|
||||
@@ -772,7 +806,7 @@ function patchSingleElementHandle(
|
||||
|
||||
// --- el.dragAndDrop() ---
|
||||
if (origElDragAndDrop) {
|
||||
(el as any).dragAndDrop = async (targetEl: ElementHandle, options?: any) => {
|
||||
(el as any).dragAndDrop = async (targetEl: ElementHandle, options?: { delay?: number }) => {
|
||||
const srcBox = await el.boundingBox();
|
||||
const tgtBox = await targetEl.boundingBox();
|
||||
|
||||
@@ -836,15 +870,26 @@ function patchSingleFrame(
|
||||
|
||||
const origFrameSelect = frame.select.bind(frame);
|
||||
|
||||
(frame as any).click = async (selector: string, options?: any) => {
|
||||
(frame as any).click = async (selector: string, options?: Partial<HumanConfig> & ({
|
||||
button?: 'left' | 'right' | 'middle' | 'back' | 'forward';
|
||||
clickCount?: number;
|
||||
count?: number;
|
||||
delay?: number;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
timeout?: number;
|
||||
})) => {
|
||||
await (page as any).click(selector, options);
|
||||
};
|
||||
|
||||
(frame as any).hover = async (selector: string, options?: any) => {
|
||||
(frame as any).hover = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await (page as any).hover(selector, options);
|
||||
};
|
||||
|
||||
(frame as any).type = async (selector: string, text: string, options?: any) => {
|
||||
(frame as any).type = async (selector: string, text: string, options?: Partial<HumanConfig> & ({
|
||||
delay?: number;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
timeout?: number;
|
||||
})) => {
|
||||
await (page as any).type(selector, text, options);
|
||||
};
|
||||
|
||||
@@ -858,7 +903,7 @@ function patchSingleFrame(
|
||||
await (page as any).focus(selector);
|
||||
};
|
||||
|
||||
(frame as any).tap = async (selector: string, options?: any) => {
|
||||
(frame as any).tap = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await (page as any).click(selector, options);
|
||||
};
|
||||
|
||||
@@ -881,7 +926,11 @@ function patchSingleFrame(
|
||||
return els;
|
||||
};
|
||||
|
||||
(frame as any).waitForSelector = async (selector: string, options?: any) => {
|
||||
(frame as any).waitForSelector = async (selector: string, options?: {
|
||||
hidden?: boolean;
|
||||
timeout?: number;
|
||||
visible?: boolean;
|
||||
}) => {
|
||||
const el = await origFrameWaitForSelector(selector, options);
|
||||
if (el) patchSingleElementHandle(el, page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
return el;
|
||||
@@ -927,7 +976,7 @@ export function patchBrowser(browser: Browser, cfg: HumanConfig): void {
|
||||
for (const methodName of ['createBrowserContext', 'createIncognitoBrowserContext'] as const) {
|
||||
if (typeof (browser as any)[methodName] === 'function') {
|
||||
const origCreateContext = (browser as any)[methodName].bind(browser);
|
||||
(browser as any)[methodName] = async (options?: any) => {
|
||||
(browser as any)[methodName] = async (options?: Parameters<typeof origCreateContext>[0]) => {
|
||||
const context: BrowserContext = await origCreateContext(options);
|
||||
|
||||
const origCtxNewPage = context.newPage.bind(context);
|
||||
|
||||
+107
-27
@@ -125,16 +125,21 @@ export function patchSingleElementHandle(
|
||||
return children;
|
||||
};
|
||||
|
||||
(el as any).waitForSelector = async (selector: string, options?: any) => {
|
||||
const child = await origElWaitForSelector(selector, options);
|
||||
(el as any).waitForSelector = async (selector: string, options?: {
|
||||
state?: 'attached' | 'detached' | 'visible' | 'hidden';
|
||||
strict?: boolean;
|
||||
timeout?: number;
|
||||
}) => {
|
||||
const child = await origElWaitForSelector(selector, options ?? {});
|
||||
if (child) patchSingleElementHandle(child, page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
return child;
|
||||
};
|
||||
|
||||
// --- Helper: get bounding box and move cursor to element ---
|
||||
// Accepts a per-call ``callCfg`` so type/fill overrides like
|
||||
// ``el.type(text, { human_config: { typing_delay: 30 } })`` carry through to
|
||||
// mouse movement & idle timing for that single call.
|
||||
// ``el.type(text, { human_config: { typing_delay: 30 } })`` or
|
||||
// ``el.type(text, { typing_delay: 30 })`` carry through to mouse movement
|
||||
// & idle timing for that single call.
|
||||
// Also scrolls the element into view first so off-screen elements work
|
||||
// (#129, #172 follow-up): otherwise boundingBox() returns null and we'd
|
||||
// silently fall back to the unpatched native method.
|
||||
@@ -164,7 +169,7 @@ export function patchSingleElementHandle(
|
||||
const target = clickTarget(box, isInp, callCfg);
|
||||
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
|
||||
await humanMove(raw, cursor.x, cursor.y, target.x, target.y, callCfg);
|
||||
@@ -174,16 +179,37 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.click() ---
|
||||
(el as any).click = async (options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).click = async (options?: Partial<HumanConfig> & ({
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
clickCount?: number;
|
||||
delay?: number;
|
||||
force?: boolean;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
modifiers?: Array<'Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift'>;
|
||||
noWaitAfter?: boolean;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElClick(options);
|
||||
await humanClick(raw, info.isInp, callCfg);
|
||||
};
|
||||
|
||||
// --- el.dblclick() ---
|
||||
(el as any).dblclick = async (options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).dblclick = async (options?: Partial<HumanConfig> & ({
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
delay?: number;
|
||||
force?: boolean;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
modifiers?: Array<'Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift'>;
|
||||
noWaitAfter?: boolean;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElDblclick(options);
|
||||
await raw.down({ clickCount: 2 });
|
||||
@@ -192,16 +218,28 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.hover() ---
|
||||
(el as any).hover = async (options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).hover = async (options?: Partial<HumanConfig> & ({
|
||||
force?: boolean;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
modifiers?: Array<'Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift'>;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElHover(options);
|
||||
// Just move — no click
|
||||
};
|
||||
|
||||
// --- el.type() ---
|
||||
(el as any).type = async (text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).type = async (text: string, options?: Partial<HumanConfig> & ({
|
||||
delay?: number;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
noWaitAfter?: boolean;
|
||||
timeout?: number;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElType(text, options);
|
||||
await humanClick(raw, info.isInp, callCfg);
|
||||
@@ -212,8 +250,13 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.fill() ---
|
||||
(el as any).fill = async (value: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).fill = async (value: string, options?: Partial<HumanConfig> & ({
|
||||
force?: boolean;
|
||||
human_config?: Partial<HumanConfig>;
|
||||
noWaitAfter?: boolean;
|
||||
timeout?: number;
|
||||
})) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const info = await moveToElement(callCfg);
|
||||
if (!info) return origElFill(value, options);
|
||||
await humanClick(raw, info.isInp, callCfg);
|
||||
@@ -229,7 +272,7 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.press() ---
|
||||
(el as any).press = async (key: string, options?: any) => {
|
||||
(el as any).press = async (key: string, options?: { delay?: number; noWaitAfter?: boolean; timeout?: number }) => {
|
||||
await sleep(rand(20, 60));
|
||||
await originals.keyboardDown(key);
|
||||
await sleep(randRange(cfg.key_hold));
|
||||
@@ -237,7 +280,11 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.selectOption() ---
|
||||
(el as any).selectOption = async (values: any, options?: any) => {
|
||||
(el as any).selectOption = async (values: any, options?: {
|
||||
force?: boolean;
|
||||
noWaitAfter?: boolean;
|
||||
timeout?: number;
|
||||
}) => {
|
||||
const info = await moveToElement();
|
||||
if (!info) return origElSelectOption(values, options);
|
||||
await humanClick(raw, false, cfg);
|
||||
@@ -246,7 +293,13 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.check() ---
|
||||
(el as any).check = async (options?: any) => {
|
||||
(el as any).check = async (options?: {
|
||||
force?: boolean;
|
||||
noWaitAfter?: boolean;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
const checked = await el.isChecked();
|
||||
if (checked) return; // Already checked
|
||||
@@ -257,7 +310,13 @@ export function patchSingleElementHandle(
|
||||
};
|
||||
|
||||
// --- el.uncheck() ---
|
||||
(el as any).uncheck = async (options?: any) => {
|
||||
(el as any).uncheck = async (options?: {
|
||||
force?: boolean;
|
||||
noWaitAfter?: boolean;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
const checked = await el.isChecked();
|
||||
if (!checked) return; // Already unchecked
|
||||
@@ -269,7 +328,13 @@ export function patchSingleElementHandle(
|
||||
|
||||
// --- el.setChecked() ---
|
||||
if (origElSetChecked) {
|
||||
(el as any).setChecked = async (checked: boolean, options?: any) => {
|
||||
(el as any).setChecked = async (checked: boolean, options?: {
|
||||
force?: boolean;
|
||||
noWaitAfter?: boolean;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
const current = await el.isChecked();
|
||||
if (current === checked) return;
|
||||
@@ -281,7 +346,14 @@ export function patchSingleElementHandle(
|
||||
}
|
||||
|
||||
// --- el.tap() ---
|
||||
(el as any).tap = async (options?: any) => {
|
||||
(el as any).tap = async (options?: {
|
||||
force?: boolean;
|
||||
modifiers?: Array<'Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift'>;
|
||||
noWaitAfter?: boolean;
|
||||
position?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
}) => {
|
||||
const info = await moveToElement();
|
||||
if (!info) return origElTap(options);
|
||||
await humanClick(raw, info.isInp, cfg);
|
||||
@@ -302,8 +374,8 @@ export function patchSingleElementHandle(
|
||||
// wheel sequence used by page.click() etc. Falls back to the native
|
||||
// method if the element is detached or scrolling fails.
|
||||
if (origElScrollIntoViewIfNeeded) {
|
||||
(el as any).scrollIntoViewIfNeeded = async (options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(el as any).scrollIntoViewIfNeeded = async (options?: Partial<HumanConfig> & ({ human_config?: Partial<HumanConfig>; timeout?: number })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
const ensureCursorInit = (page as any)._ensureCursorInit;
|
||||
if (ensureCursorInit) await ensureCursorInit();
|
||||
try {
|
||||
@@ -360,8 +432,12 @@ export function patchPageElementHandles(
|
||||
// Patch page.waitForSelector()
|
||||
if (typeof page.waitForSelector === 'function') {
|
||||
const origWaitForSelector = page.waitForSelector.bind(page);
|
||||
(page as any).waitForSelector = async (selector: string, options?: any) => {
|
||||
const el = await origWaitForSelector(selector, options);
|
||||
(page as any).waitForSelector = async (selector: string, options?: {
|
||||
state?: 'attached' | 'detached' | 'visible' | 'hidden';
|
||||
strict?: boolean;
|
||||
timeout?: number;
|
||||
}) => {
|
||||
const el = await origWaitForSelector(selector, options ?? {});
|
||||
if (el) patchSingleElementHandle(el, page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
return el;
|
||||
};
|
||||
@@ -408,8 +484,12 @@ export function patchFrameElementHandles(
|
||||
// Patch frame.waitForSelector()
|
||||
if (typeof frame.waitForSelector === 'function') {
|
||||
const origFrameWaitForSelector = frame.waitForSelector.bind(frame);
|
||||
(frame as any).waitForSelector = async (selector: string, options?: any) => {
|
||||
const el = await origFrameWaitForSelector(selector, options);
|
||||
(frame as any).waitForSelector = async (selector: string, options?: {
|
||||
state?: 'attached' | 'detached' | 'visible' | 'hidden';
|
||||
strict?: boolean;
|
||||
timeout?: number;
|
||||
}) => {
|
||||
const el = await origFrameWaitForSelector(selector, options ?? {});
|
||||
if (el) patchSingleElementHandle(el, page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
return el;
|
||||
};
|
||||
|
||||
+76
-55
@@ -295,7 +295,11 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
}
|
||||
|
||||
// --- goto (invalidate isolated world on navigation) ---
|
||||
const humanGoto = async (url: string, options?: any) => {
|
||||
const humanGoto = async (url: string, options?: {
|
||||
referer?: string;
|
||||
timeout?: number;
|
||||
waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit';
|
||||
}) => {
|
||||
const response = await originals.goto(url, options);
|
||||
stealth.invalidate();
|
||||
patchFrames(page, cfg, cursor, raw, rawKb, originals, stealth);
|
||||
@@ -303,11 +307,11 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- click ---
|
||||
const humanClickFn = async (selector: string, options?: any) => {
|
||||
const humanClickFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await ensureCursorInit();
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
const { box, cursorX, cursorY } = await scrollToElement(page, raw, selector, cursor.x, cursor.y, callCfg, options?.timeout);
|
||||
cursor.x = cursorX;
|
||||
@@ -321,12 +325,13 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- dblclick ---
|
||||
const humanDblclickFn = async (selector: string, options?: any) => {
|
||||
const humanDblclickFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await ensureCursorInit();
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
|
||||
const { box, cursorX, cursorY } = await scrollToElement(page, raw, selector, cursor.x, cursor.y, callCfg, options?.timeout);
|
||||
cursor.x = cursorX;
|
||||
cursor.y = cursorY;
|
||||
@@ -341,11 +346,11 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- hover ---
|
||||
const humanHoverFn = async (selector: string, options?: any) => {
|
||||
const humanHoverFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await ensureCursorInit();
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
const { box, cursorX, cursorY } = await scrollToElement(page, raw, selector, cursor.x, cursor.y, callCfg, options?.timeout);
|
||||
cursor.x = cursorX;
|
||||
@@ -357,8 +362,8 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- type ---
|
||||
const humanTypeFn = async (selector: string, text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const humanTypeFn = async (selector: string, text: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
await sleep(randRange(callCfg.field_switch_delay));
|
||||
await humanClickFn(selector, options);
|
||||
await sleep(rand(100, 250));
|
||||
@@ -367,8 +372,8 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- fill (clears existing content first) ---
|
||||
const humanFillFn = async (selector: string, value: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const humanFillFn = async (selector: string, value: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
await sleep(randRange(callCfg.field_switch_delay));
|
||||
await humanClickFn(selector, options);
|
||||
await sleep(rand(100, 250));
|
||||
@@ -381,9 +386,9 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- clear ---
|
||||
const humanClearFn = async (selector: string, options?: any) => {
|
||||
const humanClearFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
if (!await isSelectorFocused(stealth, page, selector)) {
|
||||
await humanClickFn(selector);
|
||||
await humanClickFn(selector, options);
|
||||
}
|
||||
await sleep(rand(50, 150));
|
||||
await originals.keyboardPress(SELECT_ALL);
|
||||
@@ -392,46 +397,48 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- check ---
|
||||
const humanCheckFn = async (selector: string, options?: any) => {
|
||||
if (cfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(cfg.idle_between_duration[0], cfg.idle_between_duration[1]), cursor.x, cursor.y, cfg);
|
||||
const humanCheckFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
const checked = await originals.isChecked(selector).catch(() => false);
|
||||
if (!checked) {
|
||||
await humanClickFn(selector);
|
||||
await humanClickFn(selector, options);
|
||||
}
|
||||
};
|
||||
|
||||
// --- uncheck ---
|
||||
const humanUncheckFn = async (selector: string, options?: any) => {
|
||||
if (cfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(cfg.idle_between_duration[0], cfg.idle_between_duration[1]), cursor.x, cursor.y, cfg);
|
||||
const humanUncheckFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
const checked = await originals.isChecked(selector).catch(() => true);
|
||||
if (checked) {
|
||||
await humanClickFn(selector);
|
||||
await humanClickFn(selector, options);
|
||||
}
|
||||
};
|
||||
|
||||
// --- selectOption ---
|
||||
const humanSelectOptionFn = async (selector: string, values: any, options?: any) => {
|
||||
await humanHoverFn(selector);
|
||||
const humanSelectOptionFn = async (selector: string, values: any, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await humanHoverFn(selector, options);
|
||||
await sleep(rand(100, 300));
|
||||
return originals.selectOption(selector, values, options);
|
||||
};
|
||||
|
||||
// --- press (checks focus first — avoids redundant mouse moves) ---
|
||||
const humanPressFn = async (selector: string, key: string, options?: any) => {
|
||||
const humanPressFn = async (selector: string, key: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
if (!await isSelectorFocused(stealth, page, selector)) {
|
||||
await humanClickFn(selector);
|
||||
await humanClickFn(selector, options);
|
||||
}
|
||||
await sleep(rand(50, 150));
|
||||
await originals.keyboardPress(key);
|
||||
};
|
||||
|
||||
// --- pressSequentially ---
|
||||
const humanPressSequentiallyFn = async (selector: string, text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const humanPressSequentiallyFn = async (selector: string, text: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (!await isSelectorFocused(stealth, page, selector)) {
|
||||
await humanClickFn(selector, options);
|
||||
}
|
||||
@@ -441,7 +448,7 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- tap ---
|
||||
const humanTapFn = async (selector: string, options?: any) => {
|
||||
const humanTapFn = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await humanClickFn(selector, options);
|
||||
};
|
||||
|
||||
@@ -461,14 +468,20 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
(page as any).clear = humanClearFn;
|
||||
|
||||
// --- mouse patches ---
|
||||
page.mouse.move = async (x: number, y: number, options?: any) => {
|
||||
page.mouse.move = async (x: number, y: number, options?: {
|
||||
steps?: number;
|
||||
}) => {
|
||||
await ensureCursorInit();
|
||||
await humanMove(raw, cursor.x, cursor.y, x, y, cfg);
|
||||
cursor.x = x;
|
||||
cursor.y = y;
|
||||
};
|
||||
|
||||
page.mouse.click = async (x: number, y: number, options?: any) => {
|
||||
page.mouse.click = async (x: number, y: number, options?: {
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
clickCount?: number;
|
||||
delay?: number;
|
||||
}) => {
|
||||
await ensureCursorInit();
|
||||
await humanMove(raw, cursor.x, cursor.y, x, y, cfg);
|
||||
cursor.x = x;
|
||||
@@ -477,7 +490,7 @@ function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
|
||||
};
|
||||
|
||||
// --- keyboard patches ---
|
||||
page.keyboard.type = async (text: string, options?: any) => {
|
||||
page.keyboard.type = async (text: string, options?: { delay?: number }) => {
|
||||
const cdp = await ensureCdp();
|
||||
await humanType(page, rawKb, text, cfg, cdp);
|
||||
};
|
||||
@@ -580,10 +593,10 @@ function patchSingleFrame(
|
||||
const origFrameTap = (frame as any).tap?.bind(frame);
|
||||
const origFrameDragAndDrop = frame.dragAndDrop.bind(frame);
|
||||
|
||||
const moveToFrameSelector = async (selector: string, options?: any, inputBias = false) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
const moveToFrameSelector = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> }), inputBias = false) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (callCfg.idle_between_actions) {
|
||||
await humanIdle(raw, rand(callCfg.idle_between_duration[0], callCfg.idle_between_duration[1]), cursor.x, cursor.y, callCfg);
|
||||
await humanIdle(raw, cursor.x, cursor.y, callCfg);
|
||||
}
|
||||
|
||||
const locator = firstFrameLocator(frame, selector);
|
||||
@@ -601,7 +614,7 @@ function patchSingleFrame(
|
||||
return { callCfg, isInput };
|
||||
};
|
||||
|
||||
const frameClick = async (selector: string, options?: any) => {
|
||||
const frameClick = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const moved = await moveToFrameSelector(selector, options);
|
||||
if (!moved) return origFrameClick(selector, options);
|
||||
await humanClick(raw, moved.isInput, moved.callCfg);
|
||||
@@ -609,14 +622,14 @@ function patchSingleFrame(
|
||||
|
||||
const getFrameCdp = async () => stealth.getCdpSession().catch(() => null);
|
||||
|
||||
const frameHover = async (selector: string, options?: any) => {
|
||||
const frameHover = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const moved = await moveToFrameSelector(selector, options, false);
|
||||
if (!moved) return origFrameHover(selector, options);
|
||||
};
|
||||
|
||||
(frame as any).click = frameClick;
|
||||
|
||||
(frame as any).dblclick = async (selector: string, options?: any) => {
|
||||
(frame as any).dblclick = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const moved = await moveToFrameSelector(selector, options);
|
||||
if (!moved) return origFrameDblclick(selector, options);
|
||||
await raw.down({ clickCount: 2 });
|
||||
@@ -626,8 +639,8 @@ function patchSingleFrame(
|
||||
|
||||
(frame as any).hover = frameHover;
|
||||
|
||||
(frame as any).type = async (selector: string, text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(frame as any).type = async (selector: string, text: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
await sleep(randRange(callCfg.field_switch_delay));
|
||||
await frameClick(selector, options);
|
||||
await sleep(rand(100, 250));
|
||||
@@ -635,8 +648,8 @@ function patchSingleFrame(
|
||||
await humanType(page, rawKb, text, callCfg, cdp).catch(() => origFrameType(selector, text, options));
|
||||
};
|
||||
|
||||
(frame as any).fill = async (selector: string, value: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(frame as any).fill = async (selector: string, value: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
await sleep(randRange(callCfg.field_switch_delay));
|
||||
await frameClick(selector, options);
|
||||
await sleep(rand(100, 250));
|
||||
@@ -648,23 +661,23 @@ function patchSingleFrame(
|
||||
await humanType(page, rawKb, value, callCfg, cdp).catch(() => origFrameFill(selector, value, options));
|
||||
};
|
||||
|
||||
(frame as any).check = async (selector: string, options?: any) => {
|
||||
(frame as any).check = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const checked = await firstFrameLocator(frame, selector).isChecked?.().catch(() => false) ?? false;
|
||||
if (!checked) await frameClick(selector, options).catch(() => origFrameCheck(selector, options));
|
||||
};
|
||||
|
||||
(frame as any).uncheck = async (selector: string, options?: any) => {
|
||||
(frame as any).uncheck = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const checked = await firstFrameLocator(frame, selector).isChecked?.().catch(() => true) ?? true;
|
||||
if (checked) await frameClick(selector, options).catch(() => origFrameUncheck(selector, options));
|
||||
};
|
||||
|
||||
(frame as any).selectOption = async (selector: string, values: any, options?: any) => {
|
||||
(frame as any).selectOption = async (selector: string, values: any, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await frameHover(selector, options);
|
||||
await sleep(rand(100, 300));
|
||||
return origFrameSelectOption(selector, values, options);
|
||||
};
|
||||
|
||||
(frame as any).press = async (selector: string, key: string, options?: any) => {
|
||||
(frame as any).press = async (selector: string, key: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
if (!await isFrameSelectorFocused(frame, selector)) {
|
||||
await frameClick(selector, options);
|
||||
}
|
||||
@@ -672,8 +685,8 @@ function patchSingleFrame(
|
||||
await originals.keyboardPress(key);
|
||||
};
|
||||
|
||||
(frame as any).pressSequentially = async (selector: string, text: string, options?: any) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config);
|
||||
(frame as any).pressSequentially = async (selector: string, text: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
const callCfg = mergeConfig(cfg, options?.human_config ?? options);
|
||||
if (!await isFrameSelectorFocused(frame, selector)) {
|
||||
await frameClick(selector, options);
|
||||
}
|
||||
@@ -682,11 +695,11 @@ function patchSingleFrame(
|
||||
await humanType(page, rawKb, text, callCfg, cdp).catch(() => origFramePressSequentially?.(selector, text, options));
|
||||
};
|
||||
|
||||
(frame as any).tap = async (selector: string, options?: any) => {
|
||||
(frame as any).tap = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
await frameClick(selector, options).catch(() => origFrameTap?.(selector, options));
|
||||
};
|
||||
|
||||
(frame as any).clear = async (selector: string, options?: any) => {
|
||||
(frame as any).clear = async (selector: string, options?: Partial<HumanConfig> & ({ timeout?: number; human_config?: Partial<HumanConfig> })) => {
|
||||
if (!await isFrameSelectorFocused(frame, selector)) {
|
||||
await frameClick(selector, options);
|
||||
}
|
||||
@@ -696,7 +709,15 @@ function patchSingleFrame(
|
||||
await originals.keyboardPress('Backspace');
|
||||
};
|
||||
|
||||
(frame as any).dragAndDrop = async (source: string, target: string, options?: any) => {
|
||||
(frame as any).dragAndDrop = async (source: string, target: string, options?: {
|
||||
force?: boolean;
|
||||
noWaitAfter?: boolean;
|
||||
sourcePosition?: { x: number; y: number };
|
||||
strict?: boolean;
|
||||
targetPosition?: { x: number; y: number };
|
||||
timeout?: number;
|
||||
trial?: boolean;
|
||||
}) => {
|
||||
const srcBox = await firstFrameLocator(frame, source).boundingBox({ timeout: options?.timeout ?? 30000 }).catch(() => null);
|
||||
const tgtBox = await firstFrameLocator(frame, target).boundingBox({ timeout: options?.timeout ?? 30000 }).catch(() => null);
|
||||
|
||||
@@ -767,14 +788,14 @@ export function patchBrowser(browser: Browser, cfg: HumanConfig): void {
|
||||
}
|
||||
|
||||
const origNewContext = browser.newContext.bind(browser);
|
||||
(browser as any).newContext = async (options?: any) => {
|
||||
(browser as any).newContext = async (options?: Parameters<typeof origNewContext>[0]) => {
|
||||
const context = await origNewContext(options);
|
||||
patchContext(context, cfg);
|
||||
return context;
|
||||
};
|
||||
|
||||
const origNewPage = browser.newPage.bind(browser);
|
||||
(browser as any).newPage = async (options?: any) => {
|
||||
(browser as any).newPage = async (options?: Parameters<typeof origNewPage>[0]) => {
|
||||
const page = await origNewPage(options);
|
||||
if (!(page as any)._original) {
|
||||
const ctx = page.context();
|
||||
|
||||
+21
-1
@@ -172,13 +172,33 @@ export async function humanClick(
|
||||
// Human idle / drift
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function humanIdle(
|
||||
export function humanIdle(
|
||||
raw: RawMouse,
|
||||
cx: number,
|
||||
cy: number,
|
||||
cfg: HumanConfig,
|
||||
): Promise<void>;
|
||||
export function humanIdle(
|
||||
raw: RawMouse,
|
||||
seconds: number,
|
||||
cx: number,
|
||||
cy: number,
|
||||
cfg: HumanConfig,
|
||||
): Promise<void>;
|
||||
export async function humanIdle(
|
||||
raw: RawMouse,
|
||||
secondsOrCx: number,
|
||||
cxOrCy: number,
|
||||
cyOrCfg: number | HumanConfig,
|
||||
maybeCfg?: HumanConfig,
|
||||
): Promise<void> {
|
||||
const hasExplicitSeconds = maybeCfg !== undefined;
|
||||
const seconds = hasExplicitSeconds
|
||||
? secondsOrCx
|
||||
: rand((cyOrCfg as HumanConfig).idle_between_duration[0], (cyOrCfg as HumanConfig).idle_between_duration[1]);
|
||||
const cx = hasExplicitSeconds ? cxOrCy : secondsOrCx;
|
||||
const cy = hasExplicitSeconds ? (cyOrCfg as number) : cxOrCy;
|
||||
const cfg = hasExplicitSeconds ? maybeCfg! : (cyOrCfg as HumanConfig);
|
||||
const endTime = Date.now() + seconds * 1000;
|
||||
let x = cx;
|
||||
let y = cy;
|
||||
|
||||
+185
-185
@@ -106,9 +106,9 @@ describe("humanMove", () => {
|
||||
return {
|
||||
raw: {
|
||||
move: vi.fn(async (x: number, y: number) => { moves.push({ x, y }); }),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
},
|
||||
moves,
|
||||
};
|
||||
@@ -180,10 +180,10 @@ describe("humanClick", () => {
|
||||
const cfg = resolveConfig("default");
|
||||
const callOrder: string[] = [];
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { callOrder.push("down"); }),
|
||||
up: vi.fn(async () => { callOrder.push("up"); }),
|
||||
wheel: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
await humanClick(raw, false, cfg);
|
||||
expect(raw.down).toHaveBeenCalledTimes(1);
|
||||
@@ -199,12 +199,12 @@ describe("humanIdle", () => {
|
||||
it("calls raw.move at least once during idle", async () => {
|
||||
const cfg = resolveConfig("default");
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
await humanIdle(raw, 10, 100, 100, cfg);
|
||||
await humanIdle(raw, 100, 100, cfg);
|
||||
expect(raw.move).toHaveBeenCalled();
|
||||
}, 15000);
|
||||
});
|
||||
@@ -265,7 +265,7 @@ describe("patchPage fill", () => {
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
try { await (page as any).fill("input#name", "hello"); } catch (_) {}
|
||||
try { await (page as any).fill("input#name", "hello"); } catch (_) { }
|
||||
|
||||
const expected = process.platform === "darwin" ? "Meta+a" : "Control+a";
|
||||
const wrong = process.platform === "darwin" ? "Control+a" : "Meta+a";
|
||||
@@ -298,7 +298,7 @@ describe("patchPage check/uncheck idle", () => {
|
||||
const cursor = { x: 100, y: 100, initialized: true };
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
try { await (page as any).check("input#cb"); } catch (_) {}
|
||||
try { await (page as any).check("input#cb"); } catch (_) { }
|
||||
|
||||
// humanCheckFn → humanIdle → humanClickFn → humanClick → raw.down
|
||||
expect(downCalled).toBe(true);
|
||||
@@ -321,7 +321,7 @@ describe("patchPage check/uncheck idle", () => {
|
||||
const cursor = { x: 100, y: 100, initialized: true };
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
try { await (page as any).uncheck("input#cb"); } catch (_) {}
|
||||
try { await (page as any).uncheck("input#cb"); } catch (_) { }
|
||||
|
||||
expect(downCalled).toBe(true);
|
||||
}, 30000);
|
||||
@@ -354,7 +354,7 @@ describe("patchPage press focus", () => {
|
||||
const cursor = { x: 50, y: 50, initialized: true };
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
try { await (page as any).press("input#field", "Enter"); } catch (_) {}
|
||||
try { await (page as any).press("input#field", "Enter"); } catch (_) { }
|
||||
|
||||
expect(downCount).toBeGreaterThan(0);
|
||||
});
|
||||
@@ -372,7 +372,7 @@ describe("patchPage press focus", () => {
|
||||
const cursor = { x: 50, y: 50, initialized: true };
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
try { await (page as any).press("input#field", "Enter"); } catch (_) {}
|
||||
try { await (page as any).press("input#field", "Enter"); } catch (_) { }
|
||||
|
||||
expect(downCount).toBe(0);
|
||||
});
|
||||
@@ -528,7 +528,7 @@ describe("patchBrowser CDP-connected workflow", () => {
|
||||
pages: vi.fn(() => [page]),
|
||||
on: vi.fn(),
|
||||
newPage: vi.fn(async () => buildMockPage()),
|
||||
addInitScript: vi.fn(async () => {}),
|
||||
addInitScript: vi.fn(async () => { }),
|
||||
};
|
||||
const browser: any = {
|
||||
contexts: vi.fn(() => [context]),
|
||||
@@ -557,7 +557,7 @@ describe("patchBrowser CDP-connected workflow", () => {
|
||||
pages: vi.fn(() => [page]),
|
||||
on: vi.fn(),
|
||||
newPage: vi.fn(async () => buildMockPage()),
|
||||
addInitScript: vi.fn(async () => {}),
|
||||
addInitScript: vi.fn(async () => { }),
|
||||
};
|
||||
const browser: any = {
|
||||
contexts: vi.fn(() => [context]),
|
||||
@@ -568,7 +568,7 @@ describe("patchBrowser CDP-connected workflow", () => {
|
||||
patchBrowser(browser, resolveConfig("default"));
|
||||
|
||||
// Click through the patched method — should go through humanize path
|
||||
try { await (page as any).click("button"); } catch (_) {}
|
||||
try { await (page as any).click("button"); } catch (_) { }
|
||||
|
||||
expect(downCalled).toBe(true);
|
||||
}, 30000);
|
||||
@@ -581,7 +581,7 @@ describe("patchBrowser CDP-connected workflow", () => {
|
||||
pages: vi.fn(() => [newPage]),
|
||||
on: vi.fn(),
|
||||
newPage: vi.fn(async () => buildMockPage()),
|
||||
addInitScript: vi.fn(async () => {}),
|
||||
addInitScript: vi.fn(async () => { }),
|
||||
};
|
||||
const browser: any = {
|
||||
contexts: vi.fn(() => []),
|
||||
@@ -605,27 +605,27 @@ describe("patchBrowser CDP-connected workflow", () => {
|
||||
function buildMockPage(overrides: Record<string, any> = {}): any {
|
||||
const mainFrameObj = overrides.mainFrameReturn ?? {
|
||||
childFrames: vi.fn(() => []),
|
||||
click: vi.fn(async () => {}),
|
||||
dblclick: vi.fn(async () => {}),
|
||||
hover: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
fill: vi.fn(async () => {}),
|
||||
check: vi.fn(async () => {}),
|
||||
uncheck: vi.fn(async () => {}),
|
||||
selectOption: vi.fn(async () => {}),
|
||||
press: vi.fn(async () => {}),
|
||||
clear: vi.fn(async () => {}),
|
||||
dragAndDrop: vi.fn(async () => {}),
|
||||
click: vi.fn(async () => { }),
|
||||
dblclick: vi.fn(async () => { }),
|
||||
hover: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
fill: vi.fn(async () => { }),
|
||||
check: vi.fn(async () => { }),
|
||||
uncheck: vi.fn(async () => { }),
|
||||
selectOption: vi.fn(async () => { }),
|
||||
press: vi.fn(async () => { }),
|
||||
clear: vi.fn(async () => { }),
|
||||
dragAndDrop: vi.fn(async () => { }),
|
||||
locator: vi.fn(() => ({
|
||||
boundingBox: vi.fn(async () => ({ x: 0, y: 0, width: 100, height: 30 })),
|
||||
first: vi.fn(function(this: any) { return this; }),
|
||||
first: vi.fn(function (this: any) { return this; }),
|
||||
})),
|
||||
};
|
||||
|
||||
const makeLocator = () => {
|
||||
const loc: any = {
|
||||
boundingBox: vi.fn(async () => ({ x: 100, y: 100, width: 200, height: 30 })),
|
||||
scrollIntoViewIfNeeded: vi.fn(async () => {}),
|
||||
scrollIntoViewIfNeeded: vi.fn(async () => { }),
|
||||
isChecked: overrides.isChecked ?? vi.fn(async () => false),
|
||||
};
|
||||
loc.first = vi.fn(() => loc);
|
||||
@@ -634,33 +634,33 @@ function buildMockPage(overrides: Record<string, any> = {}): any {
|
||||
|
||||
const page: any = {
|
||||
evaluate: overrides.evaluate ?? vi.fn(async () => false),
|
||||
addInitScript: vi.fn(async () => {}),
|
||||
addInitScript: vi.fn(async () => { }),
|
||||
mouse: {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
click: vi.fn(async () => {}),
|
||||
dblclick: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
click: vi.fn(async () => { }),
|
||||
dblclick: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
},
|
||||
keyboard: {
|
||||
press: overrides.keyboardPress
|
||||
? vi.fn(overrides.keyboardPress)
|
||||
: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
insertText: vi.fn(async () => {}),
|
||||
: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async () => { }),
|
||||
},
|
||||
click: vi.fn(async () => {}),
|
||||
dblclick: vi.fn(async () => {}),
|
||||
hover: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
fill: vi.fn(async () => {}),
|
||||
check: vi.fn(async () => {}),
|
||||
uncheck: vi.fn(async () => {}),
|
||||
selectOption: vi.fn(async () => {}),
|
||||
press: vi.fn(async () => {}),
|
||||
click: vi.fn(async () => { }),
|
||||
dblclick: vi.fn(async () => { }),
|
||||
hover: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
fill: vi.fn(async () => { }),
|
||||
check: vi.fn(async () => { }),
|
||||
uncheck: vi.fn(async () => { }),
|
||||
selectOption: vi.fn(async () => { }),
|
||||
press: vi.fn(async () => { }),
|
||||
goto: vi.fn(async () => ({})),
|
||||
isChecked: overrides.isChecked ?? vi.fn(async () => false),
|
||||
locator: vi.fn(() => makeLocator()),
|
||||
@@ -669,10 +669,10 @@ function buildMockPage(overrides: Record<string, any> = {}): any {
|
||||
frames: vi.fn(() => []),
|
||||
context: vi.fn(() => ({
|
||||
pages: vi.fn(() => []),
|
||||
addInitScript: vi.fn(async () => {}),
|
||||
addInitScript: vi.fn(async () => { }),
|
||||
})),
|
||||
url: vi.fn(() => "about:blank"),
|
||||
waitForTimeout: vi.fn(async () => {}),
|
||||
waitForTimeout: vi.fn(async () => { }),
|
||||
};
|
||||
return page;
|
||||
}
|
||||
@@ -686,8 +686,8 @@ describe("humanType non-ASCII", () => {
|
||||
const insertedChars: string[] = [];
|
||||
const raw = {
|
||||
down: vi.fn(async (k: string) => { downKeys.push(k); }),
|
||||
up: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async (t: string) => { insertedChars.push(t); }),
|
||||
};
|
||||
return { raw, downKeys, insertedChars };
|
||||
@@ -759,18 +759,18 @@ describe("humanType non-ASCII", () => {
|
||||
|
||||
function buildMockElementHandle(overrides: Record<string, any> = {}): any {
|
||||
const el: any = {
|
||||
click: vi.fn(async () => {}),
|
||||
dblclick: vi.fn(async () => {}),
|
||||
hover: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
fill: vi.fn(async () => {}),
|
||||
press: vi.fn(async () => {}),
|
||||
selectOption: vi.fn(async () => {}),
|
||||
check: vi.fn(async () => {}),
|
||||
uncheck: vi.fn(async () => {}),
|
||||
setChecked: vi.fn(async () => {}),
|
||||
tap: vi.fn(async () => {}),
|
||||
focus: vi.fn(async () => {}),
|
||||
click: vi.fn(async () => { }),
|
||||
dblclick: vi.fn(async () => { }),
|
||||
hover: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
fill: vi.fn(async () => { }),
|
||||
press: vi.fn(async () => { }),
|
||||
selectOption: vi.fn(async () => { }),
|
||||
check: vi.fn(async () => { }),
|
||||
uncheck: vi.fn(async () => { }),
|
||||
setChecked: vi.fn(async () => { }),
|
||||
tap: vi.fn(async () => { }),
|
||||
focus: vi.fn(async () => { }),
|
||||
boundingBox: overrides.boundingBox ?? vi.fn(async () => ({ x: 100, y: 100, width: 200, height: 30 })),
|
||||
evaluate: overrides.evaluate ?? vi.fn(async () => false),
|
||||
isChecked: overrides.isChecked ?? vi.fn(async () => false),
|
||||
@@ -788,21 +788,21 @@ describe("patchSingleElementHandle", () => {
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 100, y: 100, initialized: true };
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
const rawKb = {
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
insertText: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async () => { }),
|
||||
};
|
||||
const originals = {
|
||||
keyboardPress: vi.fn(async () => {}),
|
||||
keyboardDown: vi.fn(async () => {}),
|
||||
keyboardUp: vi.fn(async () => {}),
|
||||
keyboardPress: vi.fn(async () => { }),
|
||||
keyboardDown: vi.fn(async () => { }),
|
||||
keyboardUp: vi.fn(async () => { }),
|
||||
};
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
@@ -825,23 +825,23 @@ describe("patchSingleElementHandle", () => {
|
||||
move: vi.fn(async () => { moveCount++; }),
|
||||
down: vi.fn(async () => { downCalled = true; }),
|
||||
up: vi.fn(async () => { upCalled = true; }),
|
||||
wheel: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
const rawKb = {
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
insertText: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async () => { }),
|
||||
};
|
||||
const originals = {
|
||||
keyboardPress: vi.fn(async () => {}),
|
||||
keyboardDown: vi.fn(async () => {}),
|
||||
keyboardUp: vi.fn(async () => {}),
|
||||
keyboardPress: vi.fn(async () => { }),
|
||||
keyboardDown: vi.fn(async () => { }),
|
||||
keyboardUp: vi.fn(async () => { }),
|
||||
};
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
|
||||
@@ -859,22 +859,22 @@ describe("patchSingleElementHandle", () => {
|
||||
|
||||
let downCalled = false;
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { downCalled = true; }),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
const rawKb = {
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
insertText: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async () => { }),
|
||||
};
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
|
||||
@@ -890,22 +890,22 @@ describe("patchSingleElementHandle", () => {
|
||||
const cursor = { x: 50, y: 50, initialized: true };
|
||||
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
const rawKb = {
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
insertText: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async () => { }),
|
||||
};
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle({ evaluate: vi.fn(async () => true) }); // isInput = true
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
|
||||
@@ -923,26 +923,26 @@ describe("patchSingleElementHandle", () => {
|
||||
|
||||
const pressedKeys: string[] = [];
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
const rawKb = {
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
insertText: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
insertText: vi.fn(async () => { }),
|
||||
};
|
||||
const originals = {
|
||||
keyboardPress: vi.fn(async (key: string) => { pressedKeys.push(key); }),
|
||||
keyboardDown: vi.fn(async () => {}),
|
||||
keyboardUp: vi.fn(async () => {}),
|
||||
keyboardDown: vi.fn(async () => { }),
|
||||
keyboardUp: vi.fn(async () => { }),
|
||||
};
|
||||
|
||||
const el = buildMockElementHandle({ evaluate: vi.fn(async () => true) });
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
|
||||
@@ -957,9 +957,9 @@ describe("patchSingleElementHandle", () => {
|
||||
const { patchSingleElementHandle } = await import("../src/human/elementhandle.js");
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
const page = buildMockPage();
|
||||
@@ -976,9 +976,9 @@ describe("patchSingleElementHandle", () => {
|
||||
const { patchSingleElementHandle } = await import("../src/human/elementhandle.js");
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const child = buildMockElementHandle();
|
||||
const el = buildMockElementHandle();
|
||||
@@ -998,9 +998,9 @@ describe("patchPageElementHandles", () => {
|
||||
const { patchPageElementHandles } = await import("../src/human/elementhandle.js");
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
const page = buildMockPage();
|
||||
@@ -1018,9 +1018,9 @@ describe("patchPageElementHandles", () => {
|
||||
const { patchPageElementHandles } = await import("../src/human/elementhandle.js");
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el1 = buildMockElementHandle();
|
||||
const el2 = buildMockElementHandle();
|
||||
@@ -1040,9 +1040,9 @@ describe("patchPageElementHandles", () => {
|
||||
const { patchPageElementHandles } = await import("../src/human/elementhandle.js");
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
const page = buildMockPage();
|
||||
@@ -1060,9 +1060,9 @@ describe("patchPageElementHandles", () => {
|
||||
const { patchPageElementHandles } = await import("../src/human/elementhandle.js");
|
||||
const cfg = resolveConfig("default");
|
||||
const cursor = { x: 0, y: 0, initialized: false };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const page = buildMockPage();
|
||||
(page as any).$ = vi.fn(async () => null);
|
||||
@@ -1106,19 +1106,19 @@ function buildMockFrame(): any {
|
||||
locator.first = vi.fn(() => locator);
|
||||
|
||||
return {
|
||||
click: vi.fn(async () => {}),
|
||||
dblclick: vi.fn(async () => {}),
|
||||
hover: vi.fn(async () => {}),
|
||||
type: vi.fn(async () => {}),
|
||||
fill: vi.fn(async () => {}),
|
||||
check: vi.fn(async () => {}),
|
||||
uncheck: vi.fn(async () => {}),
|
||||
selectOption: vi.fn(async () => {}),
|
||||
press: vi.fn(async () => {}),
|
||||
pressSequentially: vi.fn(async () => {}),
|
||||
tap: vi.fn(async () => {}),
|
||||
clear: vi.fn(async () => {}),
|
||||
dragAndDrop: vi.fn(async () => {}),
|
||||
click: vi.fn(async () => { }),
|
||||
dblclick: vi.fn(async () => { }),
|
||||
hover: vi.fn(async () => { }),
|
||||
type: vi.fn(async () => { }),
|
||||
fill: vi.fn(async () => { }),
|
||||
check: vi.fn(async () => { }),
|
||||
uncheck: vi.fn(async () => { }),
|
||||
selectOption: vi.fn(async () => { }),
|
||||
press: vi.fn(async () => { }),
|
||||
pressSequentially: vi.fn(async () => { }),
|
||||
tap: vi.fn(async () => { }),
|
||||
clear: vi.fn(async () => { }),
|
||||
dragAndDrop: vi.fn(async () => { }),
|
||||
locator: vi.fn(() => locator),
|
||||
childFrames: vi.fn(() => []),
|
||||
};
|
||||
@@ -1169,10 +1169,10 @@ describe("page.click(selector, { timeout }) forwards timeout to scroll", () => {
|
||||
locator: vi.fn(() => ({ first: () => ({ boundingBox }) })),
|
||||
};
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
|
||||
await scrollToElement(page, raw, "#x", 0, 0, cfg, 5000);
|
||||
@@ -1189,10 +1189,10 @@ describe("page.click(selector, { timeout }) forwards timeout to scroll", () => {
|
||||
locator: vi.fn(() => ({ first: () => ({ boundingBox }) })),
|
||||
};
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
|
||||
await scrollToElement(page, raw, "#x", 0, 0, cfg);
|
||||
@@ -1224,10 +1224,10 @@ describe("page.click(selector, { timeout }) forwards timeout to scroll", () => {
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Per-call human_config override
|
||||
// Per-call human config override
|
||||
// =========================================================================
|
||||
describe("page.type / page.fill accept per-call human_config override", () => {
|
||||
it("page.type forwards merged config to humanType", async () => {
|
||||
describe("page.type / page.fill accept per-call human config override", () => {
|
||||
it("page.type forwards nested human_config to humanType", async () => {
|
||||
const keyboardMod = await import("../src/human/keyboard.js");
|
||||
const scrollMod = await import("../src/human/scroll.js");
|
||||
const { patchPage } = await import("../src/human/index.js");
|
||||
@@ -1267,7 +1267,7 @@ describe("page.type / page.fill accept per-call human_config override", () => {
|
||||
scrollSpy.mockRestore();
|
||||
}, 30000);
|
||||
|
||||
it("page.fill forwards merged config to humanType", async () => {
|
||||
it("page.fill forwards flat config to humanType", async () => {
|
||||
const keyboardMod = await import("../src/human/keyboard.js");
|
||||
const scrollMod = await import("../src/human/scroll.js");
|
||||
const { patchPage } = await import("../src/human/index.js");
|
||||
@@ -1293,7 +1293,7 @@ describe("page.type / page.fill accept per-call human_config override", () => {
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
await (page as any).fill("#password", "secret", {
|
||||
human_config: { typing_delay: 150 },
|
||||
typing_delay: 150,
|
||||
});
|
||||
|
||||
expect(captured.typing_delay).toBe(150);
|
||||
@@ -1313,13 +1313,13 @@ describe("page.type / page.fill accept per-call human_config override", () => {
|
||||
async (_page, _raw, _text, callCfg) => { captured = callCfg; },
|
||||
);
|
||||
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle({ evaluate: vi.fn(async () => true) });
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
|
||||
@@ -1341,10 +1341,10 @@ describe("humanScrollIntoView", () => {
|
||||
|
||||
const page: any = { viewportSize: () => ({ width: 1280, height: 720 }) };
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
// Box centered in viewport — squarely in scroll_target_zone
|
||||
const inViewBox = { x: 200, y: 300, width: 50, height: 30 };
|
||||
@@ -1366,10 +1366,10 @@ describe("humanScrollIntoView", () => {
|
||||
|
||||
const page: any = { viewportSize: () => ({ width: 1280, height: 720 }) };
|
||||
const raw = {
|
||||
move: vi.fn(async () => {}),
|
||||
down: vi.fn(async () => {}),
|
||||
up: vi.fn(async () => {}),
|
||||
wheel: vi.fn(async () => {}),
|
||||
move: vi.fn(async () => { }),
|
||||
down: vi.fn(async () => { }),
|
||||
up: vi.fn(async () => { }),
|
||||
wheel: vi.fn(async () => { }),
|
||||
};
|
||||
|
||||
const boxes = [
|
||||
@@ -1403,14 +1403,14 @@ describe("el.scrollIntoViewIfNeeded humanization", () => {
|
||||
|
||||
const cfg = resolveConfig("default", { idle_between_actions: false });
|
||||
const cursor = { x: 50, y: 50, initialized: true };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const el = buildMockElementHandle();
|
||||
el.scrollIntoViewIfNeeded = vi.fn(async () => {});
|
||||
el.scrollIntoViewIfNeeded = vi.fn(async () => { });
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
await el.scrollIntoViewIfNeeded();
|
||||
@@ -1429,15 +1429,15 @@ describe("el.scrollIntoViewIfNeeded humanization", () => {
|
||||
|
||||
const cfg = resolveConfig("default", { idle_between_actions: false });
|
||||
const cursor = { x: 50, y: 50, initialized: true };
|
||||
const raw = { move: vi.fn(async () => {}), down: vi.fn(async () => {}), up: vi.fn(async () => {}), wheel: vi.fn(async () => {}) };
|
||||
const rawKb = { down: vi.fn(async () => {}), up: vi.fn(async () => {}), type: vi.fn(async () => {}), insertText: vi.fn(async () => {}) };
|
||||
const originals = { keyboardPress: vi.fn(async () => {}), keyboardDown: vi.fn(async () => {}), keyboardUp: vi.fn(async () => {}) };
|
||||
const raw = { move: vi.fn(async () => { }), down: vi.fn(async () => { }), up: vi.fn(async () => { }), wheel: vi.fn(async () => { }) };
|
||||
const rawKb = { down: vi.fn(async () => { }), up: vi.fn(async () => { }), type: vi.fn(async () => { }), insertText: vi.fn(async () => { }) };
|
||||
const originals = { keyboardPress: vi.fn(async () => { }), keyboardDown: vi.fn(async () => { }), keyboardUp: vi.fn(async () => { }) };
|
||||
|
||||
const nativeFallback = vi.fn(async () => {});
|
||||
const nativeFallback = vi.fn(async () => { });
|
||||
const el = buildMockElementHandle();
|
||||
el.scrollIntoViewIfNeeded = nativeFallback;
|
||||
const page = buildMockPage();
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => {});
|
||||
(page as any)._ensureCursorInit = vi.fn(async () => { });
|
||||
|
||||
patchSingleElementHandle(el, page as any, cfg, cursor as any, raw, rawKb, originals, null);
|
||||
await el.scrollIntoViewIfNeeded();
|
||||
|
||||
@@ -532,6 +532,52 @@ describe("Puppeteer: non-ASCII text avoids CDP shift path", () => {
|
||||
});
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Per-call human config override (Puppeteer page-level)
|
||||
// =========================================================================
|
||||
describe("Puppeteer: page.type accepts per-call human config override", () => {
|
||||
it("page.type forwards merged config to humanType", async () => {
|
||||
const keyboardMod = await import("../src/human-puppeteer/keyboard.js");
|
||||
const scrollMod = await import("../src/human-puppeteer/scroll.js");
|
||||
|
||||
const cfg = resolveConfig("default", {
|
||||
idle_between_actions: false,
|
||||
field_switch_delay: [0, 1],
|
||||
});
|
||||
expect(cfg.typing_delay).toBe(70);
|
||||
|
||||
let captured: any = null;
|
||||
const typeSpy = vi.spyOn(keyboardMod, "humanType").mockImplementation(
|
||||
async (_page, _raw, _text, callCfg) => { captured = callCfg; },
|
||||
);
|
||||
const scrollSpy = vi.spyOn(scrollMod, "scrollToElement").mockImplementation(
|
||||
async (_page, _raw, _sel, cx, cy) => ({
|
||||
box: { x: 100, y: 100, width: 50, height: 30 },
|
||||
cursorX: cx,
|
||||
cursorY: cy,
|
||||
}),
|
||||
);
|
||||
|
||||
const { patchPage } = await import("../src/human-puppeteer/index.js");
|
||||
const page = buildMockPage();
|
||||
const cursor = { x: 100, y: 100, initialized: true };
|
||||
patchPage(page as any, cfg, cursor as any);
|
||||
|
||||
await (page as any).type("#email", "hi", {
|
||||
typing_delay: 30,
|
||||
mistype_chance: 0,
|
||||
});
|
||||
|
||||
expect(captured.typing_delay).toBe(30);
|
||||
expect(captured.mistype_chance).toBe(0);
|
||||
expect(cfg.typing_delay).toBe(70);
|
||||
|
||||
typeSpy.mockRestore();
|
||||
scrollSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// patchPage stealth infrastructure (Puppeteer)
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user