ectrol 0.0.3 → 0.1.0
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.d.mts +52 -15
- package/dist/index.mjs +174 -133
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -41,7 +41,6 @@ interface ISize {
|
|
|
41
41
|
declare class ElementHandle {
|
|
42
42
|
readonly contents: WebContents;
|
|
43
43
|
readonly selector: string;
|
|
44
|
-
isNotFound: boolean;
|
|
45
44
|
constructor(contents: WebContents, selector: string);
|
|
46
45
|
/**
|
|
47
46
|
* 生成在 renderer 端执行的查找脚本。
|
|
@@ -53,7 +52,9 @@ declare class ElementHandle {
|
|
|
53
52
|
/**
|
|
54
53
|
* 触发元素 mouseover 事件(不移动真实鼠标)。
|
|
55
54
|
*/
|
|
56
|
-
hover(
|
|
55
|
+
hover(options?: {
|
|
56
|
+
timeout?: number;
|
|
57
|
+
}): Promise<any>;
|
|
57
58
|
/**
|
|
58
59
|
* 点击
|
|
59
60
|
* - 通过 WebContents.sendInputEvent 发送 mouseDown/mouseUp 事件
|
|
@@ -83,14 +84,23 @@ declare class ElementHandle {
|
|
|
83
84
|
/**
|
|
84
85
|
* 将复选框或单选按钮设为选中,并派发 change 事件。
|
|
85
86
|
*/
|
|
86
|
-
check(
|
|
87
|
+
check(options?: {
|
|
88
|
+
timeout?: number;
|
|
89
|
+
}): Promise<any>;
|
|
87
90
|
/**
|
|
88
91
|
* 填充输入框内容。
|
|
89
92
|
* - 适用于 `<input>`、`<textarea>` 或 `contenteditable` 元素。
|
|
90
93
|
* - 会触发 `input` 事件(冒泡)。
|
|
94
|
+
* - 支持自定义输入间隔, 'auto' 表示模拟自然输入节奏
|
|
95
|
+
* @example
|
|
96
|
+
* ```
|
|
97
|
+
* await element.fill('Hello, World!', { interval: 100 });
|
|
98
|
+
* ```
|
|
91
99
|
*/
|
|
92
100
|
fill(value: string, options?: {
|
|
93
101
|
timeout?: number;
|
|
102
|
+
interval?: number | 'auto';
|
|
103
|
+
append?: boolean;
|
|
94
104
|
}): Promise<void>;
|
|
95
105
|
/**
|
|
96
106
|
* 在元素上发送按键事件。
|
|
@@ -124,55 +134,77 @@ declare class ElementHandle {
|
|
|
124
134
|
* @param value 选项的值
|
|
125
135
|
* @returns
|
|
126
136
|
*/
|
|
127
|
-
selectOption(value: string
|
|
137
|
+
selectOption(value: string, options?: {
|
|
138
|
+
timeout?: number;
|
|
139
|
+
}): Promise<any>;
|
|
128
140
|
/**
|
|
129
141
|
* 获取元素的 HTML 内容。
|
|
130
142
|
*/
|
|
131
|
-
innerHTML(
|
|
143
|
+
innerHTML(options?: {
|
|
144
|
+
timeout?: number;
|
|
145
|
+
}): Promise<any>;
|
|
132
146
|
/**
|
|
133
147
|
* 获取元素的文本内容。
|
|
134
148
|
*/
|
|
135
|
-
innerText(
|
|
149
|
+
innerText(options?: {
|
|
150
|
+
timeout?: number;
|
|
151
|
+
}): Promise<any>;
|
|
136
152
|
/**
|
|
137
153
|
* 获取元素的文本内容。
|
|
138
154
|
* @returns 元素的文本内容
|
|
139
155
|
*/
|
|
140
|
-
textContent(
|
|
156
|
+
textContent(options?: {
|
|
157
|
+
timeout?: number;
|
|
158
|
+
}): Promise<any>;
|
|
141
159
|
/**
|
|
142
160
|
* 获取元素的输入值。
|
|
143
161
|
* @returns 元素的输入值
|
|
144
162
|
*/
|
|
145
|
-
inputValue(
|
|
163
|
+
inputValue(options?: {
|
|
164
|
+
timeout?: number;
|
|
165
|
+
}): Promise<any>;
|
|
146
166
|
/**
|
|
147
167
|
* 获取元素的选中状态。
|
|
148
168
|
* @returns 元素的选中状态
|
|
149
169
|
*/
|
|
150
|
-
isChecked(
|
|
170
|
+
isChecked(options?: {
|
|
171
|
+
timeout?: number;
|
|
172
|
+
}): Promise<any>;
|
|
151
173
|
/**
|
|
152
174
|
* 获取元素的禁用状态。
|
|
153
175
|
* @returns 元素的禁用状态
|
|
154
176
|
*/
|
|
155
|
-
isDisabled(
|
|
177
|
+
isDisabled(options?: {
|
|
178
|
+
timeout?: number;
|
|
179
|
+
}): Promise<any>;
|
|
156
180
|
/**
|
|
157
181
|
* 获取元素的可见状态。
|
|
158
182
|
* @returns 元素的可见状态
|
|
159
183
|
*/
|
|
160
|
-
isVisible(
|
|
184
|
+
isVisible(options?: {
|
|
185
|
+
timeout?: number;
|
|
186
|
+
}): Promise<any>;
|
|
161
187
|
/**
|
|
162
188
|
* 获取元素的可用状态。
|
|
163
189
|
* @returns 元素的可用状态
|
|
164
190
|
*/
|
|
165
|
-
isEnabled(
|
|
191
|
+
isEnabled(options?: {
|
|
192
|
+
timeout?: number;
|
|
193
|
+
}): Promise<any>;
|
|
166
194
|
/**
|
|
167
195
|
* 获取元素的可编辑状态。
|
|
168
196
|
* @returns 元素的可编辑状态
|
|
169
197
|
*/
|
|
170
|
-
isEditable(
|
|
198
|
+
isEditable(options?: {
|
|
199
|
+
timeout?: number;
|
|
200
|
+
}): Promise<any>;
|
|
171
201
|
/**
|
|
172
202
|
* 获取元素的隐藏状态。
|
|
173
203
|
* @returns 元素的隐藏状态
|
|
174
204
|
*/
|
|
175
|
-
isHidden(
|
|
205
|
+
isHidden(options?: {
|
|
206
|
+
timeout?: number;
|
|
207
|
+
}): Promise<any>;
|
|
176
208
|
/**
|
|
177
209
|
* 选中元素的文本内容。
|
|
178
210
|
*/
|
|
@@ -237,6 +269,11 @@ declare class SessionStorage {
|
|
|
237
269
|
removeItem(key: string): Promise<void>;
|
|
238
270
|
}
|
|
239
271
|
//#endregion
|
|
272
|
+
//#region src/utils.d.ts
|
|
273
|
+
declare function randomInRange(min: number, max: number, integer?: boolean): number;
|
|
274
|
+
declare function getHumanTypingDelay(index: number, text: string): number;
|
|
275
|
+
declare function sleep(ms: number): Promise<unknown>;
|
|
276
|
+
//#endregion
|
|
240
277
|
//#region src/index.d.ts
|
|
241
278
|
/**
|
|
242
279
|
* Ectrol
|
|
@@ -261,4 +298,4 @@ declare class Ectrol {
|
|
|
261
298
|
$(selector: string): ElementHandle;
|
|
262
299
|
}
|
|
263
300
|
//#endregion
|
|
264
|
-
export { Ectrol, Ectrol as default, ElementHandle, IBoundingClientRect, ISize, IVector2D, LocalStorage, SessionStorage };
|
|
301
|
+
export { Ectrol, Ectrol as default, ElementHandle, IBoundingClientRect, ISize, IVector2D, LocalStorage, SessionStorage, getHumanTypingDelay, randomInRange, sleep };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
//#region src/utils.ts
|
|
2
|
+
function randomInRange(min, max, integer = false) {
|
|
3
|
+
if (min > max) [min, max] = [max, min];
|
|
4
|
+
const value = Math.random() * (max - min) + min;
|
|
5
|
+
return integer ? Math.floor(value) : value;
|
|
6
|
+
}
|
|
7
|
+
function getHumanTypingDelay(index, text) {
|
|
8
|
+
const char = text[index];
|
|
9
|
+
let delay = randomInRange(16, 100);
|
|
10
|
+
const progress = index / text.length;
|
|
11
|
+
if (progress < .15) delay *= 1.3;
|
|
12
|
+
else if (progress > .85) delay *= 1.25;
|
|
13
|
+
if (char === " ") delay += 16;
|
|
14
|
+
if (/[,.!?;:]/.test(char)) delay += randomInRange(16, 50);
|
|
15
|
+
if (Math.random() < .03) delay += randomInRange(100, 200);
|
|
16
|
+
return Math.max(40, Math.round(delay));
|
|
17
|
+
}
|
|
18
|
+
async function sleep(ms) {
|
|
19
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
1
23
|
//#region src/element.ts
|
|
2
24
|
/**
|
|
3
25
|
* ElementHandle
|
|
@@ -7,7 +29,6 @@
|
|
|
7
29
|
* - 大多数 API 接受 `timeout`,轮询查找元素直到超时
|
|
8
30
|
*/
|
|
9
31
|
var ElementHandle = class {
|
|
10
|
-
isNotFound = false;
|
|
11
32
|
constructor(contents, selector) {
|
|
12
33
|
this.contents = contents;
|
|
13
34
|
this.selector = selector;
|
|
@@ -20,104 +41,116 @@ var ElementHandle = class {
|
|
|
20
41
|
*/
|
|
21
42
|
_getElement(timeout = 0) {
|
|
22
43
|
return `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
new Promise((resolve) => {
|
|
45
|
+
function getElement() {
|
|
46
|
+
const selector = "${this.selector}";
|
|
47
|
+
if (!selector) return null;
|
|
48
|
+
|
|
49
|
+
window.__ELECTROL__ = window.__ELECTROL__ || {};
|
|
50
|
+
if (window.__ELECTROL__[selector]) {
|
|
51
|
+
return window.__ELECTROL__[selector];
|
|
52
|
+
}
|
|
28
53
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
32
|
-
}
|
|
54
|
+
// 分层选择器,支持 iframe 穿越
|
|
55
|
+
const parts = selector.split('|>').map(s => s.trim());
|
|
56
|
+
if (!parts.length) return null;
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
58
|
+
let currentDocument = document;
|
|
59
|
+
let offsetLeft = 0;
|
|
60
|
+
let offsetTop = 0;
|
|
61
|
+
let element = null;
|
|
37
62
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
let offsetTop = 0;
|
|
41
|
-
let element = null;
|
|
63
|
+
for (let i = 0; i < parts.length; i++) {
|
|
64
|
+
if (!currentDocument) return null;
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
const part = parts[i];
|
|
67
|
+
// 在当前文档查找该层选择器
|
|
68
|
+
const el = currentDocument.querySelector(part);
|
|
45
69
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
70
|
+
if (!el) {
|
|
71
|
+
console.warn('未找到元素:', part);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
console.warn('未找到元素:', part);
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
75
|
+
const rect = el.getBoundingClientRect();
|
|
54
76
|
|
|
55
|
-
|
|
77
|
+
// 累加当前层级偏移
|
|
78
|
+
offsetLeft += rect.left;
|
|
79
|
+
offsetTop += rect.top;
|
|
56
80
|
|
|
57
|
-
|
|
58
|
-
offsetLeft += rect.left;
|
|
59
|
-
offsetTop += rect.top;
|
|
81
|
+
element = el;
|
|
60
82
|
|
|
61
|
-
|
|
83
|
+
const isLast = i === parts.length - 1;
|
|
84
|
+
if (!isLast) {
|
|
85
|
+
if (el.tagName !== 'IFRAME') {
|
|
86
|
+
console.warn('非 iframe 元素却尝试进入下一层:', part);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
62
89
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
90
|
+
const nextDoc = el.contentDocument || el.contentWindow?.document;
|
|
91
|
+
if (!nextDoc) {
|
|
92
|
+
console.warn('无法访问 iframe.contentDocument(可能跨域)');
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
69
95
|
|
|
70
|
-
|
|
71
|
-
if (!nextDoc) {
|
|
72
|
-
console.warn('无法访问 iframe.contentDocument(可能跨域)');
|
|
73
|
-
return null;
|
|
96
|
+
currentDocument = nextDoc;
|
|
74
97
|
}
|
|
75
|
-
|
|
76
|
-
currentDocument = nextDoc;
|
|
77
98
|
}
|
|
78
|
-
}
|
|
79
99
|
|
|
80
|
-
|
|
100
|
+
if (!element) return null;
|
|
81
101
|
|
|
82
|
-
|
|
102
|
+
const finalRect = element.getBoundingClientRect();
|
|
83
103
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
// 缓存元素
|
|
105
|
+
// 缓存以避免重复查找
|
|
106
|
+
window.__ELECTROL__[selector] = {
|
|
107
|
+
element,
|
|
108
|
+
rect: new DOMRect(
|
|
109
|
+
offsetLeft,
|
|
110
|
+
offsetTop,
|
|
111
|
+
finalRect.width,
|
|
112
|
+
finalRect.height
|
|
113
|
+
)
|
|
114
|
+
};
|
|
95
115
|
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
return window.__ELECTROL__[selector]
|
|
117
|
+
}
|
|
98
118
|
|
|
99
|
-
|
|
119
|
+
const timeout = ${timeout}
|
|
100
120
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
// 轮询查找元素
|
|
122
|
+
const interval = setInterval(() => {
|
|
123
|
+
const element = getElement();
|
|
124
|
+
if (element) {
|
|
125
|
+
clearInterval(interval);
|
|
126
|
+
clearTimeout(timeoutId);
|
|
127
|
+
resolve(element);
|
|
128
|
+
}
|
|
129
|
+
}, 100);
|
|
130
|
+
|
|
131
|
+
// 超时处理
|
|
132
|
+
const timeoutId = setTimeout(() => {
|
|
133
|
+
clearInterval(interval);
|
|
134
|
+
resolve(null);
|
|
135
|
+
}, timeout);
|
|
104
136
|
|
|
105
|
-
const startTime = Date.now()
|
|
106
|
-
while (Date.now() - startTime < timeout) {
|
|
107
137
|
const element = getElement();
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
138
|
+
|
|
139
|
+
if (element) {
|
|
140
|
+
clearInterval(interval);
|
|
141
|
+
clearTimeout(timeoutId);
|
|
142
|
+
resolve(element);
|
|
143
|
+
}
|
|
144
|
+
})
|
|
112
145
|
`;
|
|
113
146
|
}
|
|
114
147
|
/**
|
|
115
148
|
* 触发元素 mouseover 事件(不移动真实鼠标)。
|
|
116
149
|
*/
|
|
117
|
-
hover() {
|
|
150
|
+
hover(options) {
|
|
118
151
|
return this.contents.executeJavaScript(`
|
|
119
|
-
(
|
|
120
|
-
const target = ${this._getElement()};
|
|
152
|
+
(async () => {
|
|
153
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
121
154
|
if (!target) return null;
|
|
122
155
|
|
|
123
156
|
const event = new MouseEvent('mouseover', {
|
|
@@ -147,7 +180,7 @@ var ElementHandle = class {
|
|
|
147
180
|
button: options?.button || "left",
|
|
148
181
|
clickCount: 1
|
|
149
182
|
});
|
|
150
|
-
await
|
|
183
|
+
await sleep(options?.delay || 10);
|
|
151
184
|
this.contents.sendInputEvent({
|
|
152
185
|
type: "mouseUp",
|
|
153
186
|
x: rect.centerX,
|
|
@@ -180,10 +213,10 @@ var ElementHandle = class {
|
|
|
180
213
|
/**
|
|
181
214
|
* 将复选框或单选按钮设为选中,并派发 change 事件。
|
|
182
215
|
*/
|
|
183
|
-
check() {
|
|
216
|
+
check(options) {
|
|
184
217
|
return this.contents.executeJavaScript(`
|
|
185
|
-
(
|
|
186
|
-
const target = ${this._getElement()};
|
|
218
|
+
(async () => {
|
|
219
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
187
220
|
if (!target) return null;
|
|
188
221
|
|
|
189
222
|
// 验证输入框是否是复选框或单选按钮
|
|
@@ -198,22 +231,32 @@ var ElementHandle = class {
|
|
|
198
231
|
* 填充输入框内容。
|
|
199
232
|
* - 适用于 `<input>`、`<textarea>` 或 `contenteditable` 元素。
|
|
200
233
|
* - 会触发 `input` 事件(冒泡)。
|
|
234
|
+
* - 支持自定义输入间隔, 'auto' 表示模拟自然输入节奏
|
|
235
|
+
* @example
|
|
236
|
+
* ```
|
|
237
|
+
* await element.fill('Hello, World!', { interval: 100 });
|
|
238
|
+
* ```
|
|
201
239
|
*/
|
|
202
240
|
async fill(value, options) {
|
|
203
|
-
await this.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (target
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
target.element.value = ${JSON.stringify(value)};
|
|
214
|
-
target.element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
241
|
+
await this.focus({ timeout: options?.timeout });
|
|
242
|
+
let interval;
|
|
243
|
+
if (typeof options?.interval === "number") interval = options?.interval;
|
|
244
|
+
if (!options?.append) await this.contents.executeJavaScript(`
|
|
245
|
+
(async () => {
|
|
246
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
247
|
+
if (target && target.element) {
|
|
248
|
+
if (target.element.tagName !== 'INPUT' && target.element.tagName !== 'TEXTAREA' && !target.element.isContentEditable) return null;
|
|
249
|
+
target.element.value = "";
|
|
250
|
+
}
|
|
215
251
|
})()
|
|
216
252
|
`);
|
|
253
|
+
for (let index = 0; index < value.length; index++) {
|
|
254
|
+
await sleep(interval || getHumanTypingDelay(index, value));
|
|
255
|
+
this.contents.sendInputEvent({
|
|
256
|
+
type: "char",
|
|
257
|
+
keyCode: value[index]
|
|
258
|
+
});
|
|
259
|
+
}
|
|
217
260
|
}
|
|
218
261
|
/**
|
|
219
262
|
* 在元素上发送按键事件。
|
|
@@ -230,7 +273,7 @@ var ElementHandle = class {
|
|
|
230
273
|
type: "keyDown",
|
|
231
274
|
keyCode: key$1
|
|
232
275
|
});
|
|
233
|
-
await
|
|
276
|
+
await sleep(options?.delay || 10);
|
|
234
277
|
for (const key$1 of keys) this.contents.sendInputEvent({
|
|
235
278
|
type: "keyUp",
|
|
236
279
|
keyCode: key$1
|
|
@@ -241,8 +284,8 @@ var ElementHandle = class {
|
|
|
241
284
|
*/
|
|
242
285
|
async focus(options) {
|
|
243
286
|
return this.contents.executeJavaScript(`
|
|
244
|
-
(
|
|
245
|
-
const target = ${this._getElement(options?.timeout)};
|
|
287
|
+
(async () => {
|
|
288
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
246
289
|
if (!target) return null;
|
|
247
290
|
|
|
248
291
|
// 验证输入框是否是文本类型
|
|
@@ -263,11 +306,9 @@ var ElementHandle = class {
|
|
|
263
306
|
* - 返回扩展的 DOMRect 信息并包含元素中心点坐标。
|
|
264
307
|
*/
|
|
265
308
|
getBoundingClientRect(timeout = 0) {
|
|
266
|
-
if (this.isNotFound) return Promise.resolve(null);
|
|
267
309
|
return this.contents.executeJavaScript(`
|
|
268
|
-
(
|
|
269
|
-
const target = ${this._getElement(timeout)};
|
|
270
|
-
|
|
310
|
+
(async () => {
|
|
311
|
+
const target = await ${this._getElement(timeout)};
|
|
271
312
|
if (!target) return null;
|
|
272
313
|
|
|
273
314
|
const rect = target.rect;
|
|
@@ -291,10 +332,10 @@ var ElementHandle = class {
|
|
|
291
332
|
* @param value 选项的值
|
|
292
333
|
* @returns
|
|
293
334
|
*/
|
|
294
|
-
async selectOption(value) {
|
|
335
|
+
async selectOption(value, options) {
|
|
295
336
|
return this.contents.executeJavaScript(`
|
|
296
|
-
(
|
|
297
|
-
const target = ${this._getElement()};
|
|
337
|
+
(async () => {
|
|
338
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
298
339
|
if (!target) return null;
|
|
299
340
|
|
|
300
341
|
// 验证元素是否为选择框
|
|
@@ -308,10 +349,10 @@ var ElementHandle = class {
|
|
|
308
349
|
/**
|
|
309
350
|
* 获取元素的 HTML 内容。
|
|
310
351
|
*/
|
|
311
|
-
async innerHTML() {
|
|
352
|
+
async innerHTML(options) {
|
|
312
353
|
return this.contents.executeJavaScript(`
|
|
313
|
-
(
|
|
314
|
-
const target = ${this._getElement()};
|
|
354
|
+
(async () => {
|
|
355
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
315
356
|
if (!target) return null;
|
|
316
357
|
|
|
317
358
|
return target.element.innerHTML;
|
|
@@ -321,10 +362,10 @@ var ElementHandle = class {
|
|
|
321
362
|
/**
|
|
322
363
|
* 获取元素的文本内容。
|
|
323
364
|
*/
|
|
324
|
-
async innerText() {
|
|
365
|
+
async innerText(options) {
|
|
325
366
|
return this.contents.executeJavaScript(`
|
|
326
|
-
(
|
|
327
|
-
const target = ${this._getElement()};
|
|
367
|
+
(async () => {
|
|
368
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
328
369
|
if (!target) return null;
|
|
329
370
|
|
|
330
371
|
return target.element.innerText;
|
|
@@ -335,10 +376,10 @@ var ElementHandle = class {
|
|
|
335
376
|
* 获取元素的文本内容。
|
|
336
377
|
* @returns 元素的文本内容
|
|
337
378
|
*/
|
|
338
|
-
async textContent() {
|
|
379
|
+
async textContent(options) {
|
|
339
380
|
return this.contents.executeJavaScript(`
|
|
340
|
-
(
|
|
341
|
-
const target = ${this._getElement()};
|
|
381
|
+
(async () => {
|
|
382
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
342
383
|
if (!target) return null;
|
|
343
384
|
|
|
344
385
|
return target.element.textContent;
|
|
@@ -349,10 +390,10 @@ var ElementHandle = class {
|
|
|
349
390
|
* 获取元素的输入值。
|
|
350
391
|
* @returns 元素的输入值
|
|
351
392
|
*/
|
|
352
|
-
async inputValue() {
|
|
393
|
+
async inputValue(options) {
|
|
353
394
|
return this.contents.executeJavaScript(`
|
|
354
|
-
(
|
|
355
|
-
const target = ${this._getElement()};
|
|
395
|
+
(async () => {
|
|
396
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
356
397
|
if (!target) return null;
|
|
357
398
|
|
|
358
399
|
// 验证输入框是否是文本类型
|
|
@@ -366,10 +407,10 @@ var ElementHandle = class {
|
|
|
366
407
|
* 获取元素的选中状态。
|
|
367
408
|
* @returns 元素的选中状态
|
|
368
409
|
*/
|
|
369
|
-
async isChecked() {
|
|
410
|
+
async isChecked(options) {
|
|
370
411
|
return this.contents.executeJavaScript(`
|
|
371
|
-
(
|
|
372
|
-
const target = ${this._getElement()};
|
|
412
|
+
(async () => {
|
|
413
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
373
414
|
if (!target) return null;
|
|
374
415
|
|
|
375
416
|
// 验证输入框是否是复选框或单选框
|
|
@@ -383,10 +424,10 @@ var ElementHandle = class {
|
|
|
383
424
|
* 获取元素的禁用状态。
|
|
384
425
|
* @returns 元素的禁用状态
|
|
385
426
|
*/
|
|
386
|
-
async isDisabled() {
|
|
427
|
+
async isDisabled(options) {
|
|
387
428
|
return this.contents.executeJavaScript(`
|
|
388
|
-
(
|
|
389
|
-
const target = ${this._getElement()};
|
|
429
|
+
(async () => {
|
|
430
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
390
431
|
if (!target) return null;
|
|
391
432
|
|
|
392
433
|
return target.element.disabled;
|
|
@@ -397,10 +438,10 @@ var ElementHandle = class {
|
|
|
397
438
|
* 获取元素的可见状态。
|
|
398
439
|
* @returns 元素的可见状态
|
|
399
440
|
*/
|
|
400
|
-
async isVisible() {
|
|
441
|
+
async isVisible(options) {
|
|
401
442
|
return this.contents.executeJavaScript(`
|
|
402
|
-
(
|
|
403
|
-
const target = ${this._getElement()};
|
|
443
|
+
(async () => {
|
|
444
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
404
445
|
if (!target) return null;
|
|
405
446
|
|
|
406
447
|
return target.element.offsetParent !== null;
|
|
@@ -411,10 +452,10 @@ var ElementHandle = class {
|
|
|
411
452
|
* 获取元素的可用状态。
|
|
412
453
|
* @returns 元素的可用状态
|
|
413
454
|
*/
|
|
414
|
-
async isEnabled() {
|
|
455
|
+
async isEnabled(options) {
|
|
415
456
|
return this.contents.executeJavaScript(`
|
|
416
|
-
(
|
|
417
|
-
const target = ${this._getElement()};
|
|
457
|
+
(async () => {
|
|
458
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
418
459
|
if (!target) return null;
|
|
419
460
|
|
|
420
461
|
return !target.element.disabled;
|
|
@@ -425,10 +466,10 @@ var ElementHandle = class {
|
|
|
425
466
|
* 获取元素的可编辑状态。
|
|
426
467
|
* @returns 元素的可编辑状态
|
|
427
468
|
*/
|
|
428
|
-
async isEditable() {
|
|
469
|
+
async isEditable(options) {
|
|
429
470
|
return this.contents.executeJavaScript(`
|
|
430
|
-
(
|
|
431
|
-
const target = ${this._getElement()};
|
|
471
|
+
(async () => {
|
|
472
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
432
473
|
if (!target) return null;
|
|
433
474
|
|
|
434
475
|
return target.element.isContentEditable;
|
|
@@ -439,10 +480,10 @@ var ElementHandle = class {
|
|
|
439
480
|
* 获取元素的隐藏状态。
|
|
440
481
|
* @returns 元素的隐藏状态
|
|
441
482
|
*/
|
|
442
|
-
async isHidden() {
|
|
483
|
+
async isHidden(options) {
|
|
443
484
|
return this.contents.executeJavaScript(`
|
|
444
|
-
(
|
|
445
|
-
const target = ${this._getElement()};
|
|
485
|
+
(async () => {
|
|
486
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
446
487
|
if (!target) return null;
|
|
447
488
|
|
|
448
489
|
return target.element.offsetParent === null;
|
|
@@ -454,8 +495,8 @@ var ElementHandle = class {
|
|
|
454
495
|
*/
|
|
455
496
|
async selectText(options) {
|
|
456
497
|
return this.contents.executeJavaScript(`
|
|
457
|
-
(
|
|
458
|
-
const target = ${this._getElement(options.timeout)};
|
|
498
|
+
(async () => {
|
|
499
|
+
const target = await ${this._getElement(options.timeout)};
|
|
459
500
|
if (!target) return null;
|
|
460
501
|
|
|
461
502
|
// 选中元素的文本内容
|
|
@@ -474,8 +515,8 @@ var ElementHandle = class {
|
|
|
474
515
|
*/
|
|
475
516
|
async uncheck(options) {
|
|
476
517
|
return this.contents.executeJavaScript(`
|
|
477
|
-
(
|
|
478
|
-
const target = ${this._getElement(options?.timeout)};
|
|
518
|
+
(async () => {
|
|
519
|
+
const target = await ${this._getElement(options?.timeout)};
|
|
479
520
|
if (!target) return null;
|
|
480
521
|
|
|
481
522
|
// 验证输入框是否是复选框或单选框
|
|
@@ -587,4 +628,4 @@ var Ectrol = class {
|
|
|
587
628
|
var src_default = Ectrol;
|
|
588
629
|
|
|
589
630
|
//#endregion
|
|
590
|
-
export { Ectrol, ElementHandle, LocalStorage, SessionStorage, src_default as default };
|
|
631
|
+
export { Ectrol, ElementHandle, LocalStorage, SessionStorage, src_default as default, getHumanTypingDelay, randomInRange, sleep };
|
package/package.json
CHANGED