arn-browser 0.0.5 → 0.0.7
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/package.json
CHANGED
|
@@ -20,6 +20,22 @@ function sleep(ms) {
|
|
|
20
20
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Simple mutex for serializing keyboard operations
|
|
25
|
+
*/
|
|
26
|
+
let typingLock = Promise.resolve();
|
|
27
|
+
async function withTypingLock(fn) {
|
|
28
|
+
const previousLock = typingLock;
|
|
29
|
+
let releaseLock;
|
|
30
|
+
typingLock = new Promise(resolve => { releaseLock = resolve; });
|
|
31
|
+
await previousLock;
|
|
32
|
+
try {
|
|
33
|
+
return await fn();
|
|
34
|
+
} finally {
|
|
35
|
+
releaseLock();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
/**
|
|
24
40
|
* Creates a HumanLocator that wraps a Playwright Locator
|
|
25
41
|
* with human-like cursor movement for actions
|
|
@@ -70,18 +86,33 @@ function createHumanLocator(cursor, locator) {
|
|
|
70
86
|
if (!cursor.config.humanize) {
|
|
71
87
|
return await locator.fill(value, options);
|
|
72
88
|
}
|
|
73
|
-
|
|
74
|
-
await cursor.
|
|
75
|
-
await
|
|
76
|
-
|
|
89
|
+
// Humanize: Move to element, then fill instantly (Playwright handles focus)
|
|
90
|
+
await cursor._moveToLocator(locator, options);
|
|
91
|
+
return await locator.fill(value, options);
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
async fillSequentially(value, options = {}) {
|
|
95
|
+
if (!cursor.config.humanize) {
|
|
96
|
+
return await locator.fill(value, options);
|
|
97
|
+
}
|
|
98
|
+
return await withTypingLock(async () => {
|
|
99
|
+
await this.click(options);
|
|
100
|
+
// Clear field reliably using fill('') before human typing
|
|
101
|
+
await locator.fill('');
|
|
102
|
+
await sleep(randInt(50, 100));
|
|
103
|
+
// Human type with delays
|
|
104
|
+
await cursor._type(value, options);
|
|
105
|
+
});
|
|
77
106
|
},
|
|
78
107
|
|
|
79
108
|
async type(text, options = {}) {
|
|
80
109
|
if (!cursor.config.humanize) {
|
|
81
110
|
return await locator.type(text, options);
|
|
82
111
|
}
|
|
83
|
-
await
|
|
84
|
-
|
|
112
|
+
return await withTypingLock(async () => {
|
|
113
|
+
await this.click(options);
|
|
114
|
+
await cursor._type(text, options);
|
|
115
|
+
});
|
|
85
116
|
},
|
|
86
117
|
|
|
87
118
|
async hover(options = {}) {
|
|
@@ -99,6 +130,46 @@ function createHumanLocator(cursor, locator) {
|
|
|
99
130
|
await cursor._page.keyboard.press(key);
|
|
100
131
|
},
|
|
101
132
|
|
|
133
|
+
async check(options = {}) {
|
|
134
|
+
if (!cursor.config.humanize) {
|
|
135
|
+
return await locator.check(options);
|
|
136
|
+
}
|
|
137
|
+
if (!(await locator.isChecked())) {
|
|
138
|
+
await this.click(options);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
async uncheck(options = {}) {
|
|
143
|
+
if (!cursor.config.humanize) {
|
|
144
|
+
return await locator.uncheck(options);
|
|
145
|
+
}
|
|
146
|
+
if (await locator.isChecked()) {
|
|
147
|
+
await this.click(options);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
async setChecked(checked, options = {}) {
|
|
152
|
+
if (!cursor.config.humanize) {
|
|
153
|
+
return await locator.setChecked(checked, options);
|
|
154
|
+
}
|
|
155
|
+
if (checked) {
|
|
156
|
+
await this.check(options);
|
|
157
|
+
} else {
|
|
158
|
+
await this.uncheck(options);
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
async pressSequentially(text, options = {}) {
|
|
163
|
+
if (!cursor.config.humanize) {
|
|
164
|
+
return await locator.pressSequentially(text, options);
|
|
165
|
+
}
|
|
166
|
+
return await withTypingLock(async () => {
|
|
167
|
+
await this.click(options);
|
|
168
|
+
await locator.clear();
|
|
169
|
+
await cursor._type(text, options);
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
|
|
102
173
|
// Chainable methods that return new HumanLocators
|
|
103
174
|
filter(options) {
|
|
104
175
|
return createHumanLocator(cursor, locator.filter(options));
|
|
@@ -151,7 +222,7 @@ function createHumanLocator(cursor, locator) {
|
|
|
151
222
|
|
|
152
223
|
/**
|
|
153
224
|
* Create a HumanCursor that acts as a drop-in replacement for Page
|
|
154
|
-
* All page methods work, but click/fill/type/hover use human cursor
|
|
225
|
+
* All page methods work, but click/fill/type/check/hover use human cursor
|
|
155
226
|
*
|
|
156
227
|
* @param {import('playwright').Page} page - Playwright Page object
|
|
157
228
|
* @param {Object} options - Configuration options
|
|
@@ -411,6 +482,27 @@ export function createCursor(page, options = {}) {
|
|
|
411
482
|
if (prop in locatorMethods) {
|
|
412
483
|
return locatorMethods[prop];
|
|
413
484
|
}
|
|
485
|
+
|
|
486
|
+
// Override Page methods to use HumanLocator
|
|
487
|
+
if (['click', 'dblclick', 'fill', 'hover', 'type', 'press', 'check', 'uncheck'].includes(prop)) {
|
|
488
|
+
return async (selector, ...args) => {
|
|
489
|
+
const humanLocator = createHumanLocator(cursor, page.locator(selector));
|
|
490
|
+
return await humanLocator[prop](...args);
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (prop === 'fillSequentially') {
|
|
494
|
+
return async (selector, ...args) => {
|
|
495
|
+
const humanLocator = createHumanLocator(cursor, page.locator(selector));
|
|
496
|
+
return await humanLocator.fillSequentially(...args);
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
if (prop === 'pressSequentially') {
|
|
500
|
+
return async (selector, ...args) => {
|
|
501
|
+
const humanLocator = createHumanLocator(cursor, page.locator(selector));
|
|
502
|
+
return await humanLocator.pressSequentially(...args);
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
414
506
|
// Expose cursor utilities
|
|
415
507
|
if (prop === 'showCursor') {
|
|
416
508
|
return cursor.showCursor.bind(cursor);
|
|
@@ -66,7 +66,9 @@ export interface HumanClickOptions {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export interface HumanFillOptions extends HumanClickOptions {
|
|
69
|
+
/** Minimum delay between keystrokes in ms (default: 50) */
|
|
69
70
|
minDelay?: number;
|
|
71
|
+
/** Maximum delay between keystrokes in ms (default: 150) */
|
|
70
72
|
maxDelay?: number;
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -77,8 +79,13 @@ export interface HumanFillOptions extends HumanClickOptions {
|
|
|
77
79
|
export interface HumanLocator extends Locator {
|
|
78
80
|
click(options?: HumanClickOptions): Promise<void>;
|
|
79
81
|
dblclick(options?: HumanClickOptions): Promise<void>;
|
|
80
|
-
fill(value: string, options?:
|
|
82
|
+
fill(value: string, options?: HumanClickOptions): Promise<void>;
|
|
83
|
+
fillSequentially(value: string, options?: HumanFillOptions): Promise<void>;
|
|
81
84
|
type(text: string, options?: HumanFillOptions): Promise<void>;
|
|
85
|
+
pressSequentially(text: string, options?: HumanFillOptions): Promise<void>;
|
|
86
|
+
check(options?: HumanClickOptions): Promise<void>;
|
|
87
|
+
uncheck(options?: HumanClickOptions): Promise<void>;
|
|
88
|
+
setChecked(checked: boolean, options?: HumanClickOptions): Promise<void>;
|
|
82
89
|
hover(options?: HumanClickOptions): Promise<void>;
|
|
83
90
|
press(key: string, options?: HumanClickOptions): Promise<void>;
|
|
84
91
|
|