@xbrowser/cli 1.13.0 → 1.15.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/{browser-T7OVLAEC.js → browser-4FKHBUBJ.js} +2 -2
- package/dist/{browser-OMGJMSBB.js → browser-JNT2I73V.js} +1 -1
- package/dist/{browser-AQ2AT2YZ.js → browser-YYMKZWTU.js} +2 -2
- package/dist/{cdp-driver-VS7Z7W7Y.js → cdp-driver-7RW4NY2X.js} +1 -1
- package/dist/{cdp-driver-ALDNOZYD.js → cdp-driver-FTGIYCFO.js} +224 -44
- package/dist/{cdp-driver-HYCLE4YP.js → cdp-driver-QX72T7SU.js} +1 -1
- package/dist/{chunk-BY5TT6WZ.js → chunk-HFP4QCZL.js} +244 -53
- package/dist/{chunk-CW7L53JE.js → chunk-K5E4CWRV.js} +1 -1
- package/dist/{chunk-R2POF2GP.js → chunk-NTTFKS6T.js} +244 -53
- package/dist/{chunk-T2CNKWPV.js → chunk-UXDGEDT7.js} +244 -53
- package/dist/{chunk-HD4FX2N5.js → chunk-XQ4KOZ37.js} +1 -1
- package/dist/cli.js +75 -21
- package/dist/daemon-main.js +38 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -21
- package/dist/{session-replayer-UVH5FGY2.js → session-replayer-DKHXF3DZ.js} +1 -1
- package/package.json +1 -1
|
@@ -26,7 +26,7 @@ var DEFAULT_STEALTH_CONFIG = {
|
|
|
26
26
|
bezierCurvature: [0.35, 0.6],
|
|
27
27
|
noiseAmplitude: 5.5,
|
|
28
28
|
overshootRange: [6, 14],
|
|
29
|
-
aimPause: [
|
|
29
|
+
aimPause: [80, 280],
|
|
30
30
|
pressDuration: [60, 140],
|
|
31
31
|
releaseDrift: [0.8, 2.5],
|
|
32
32
|
landingOffsetSmall: [0.3, 2.5],
|
|
@@ -47,6 +47,9 @@ var DEFAULT_STEALTH_CONFIG = {
|
|
|
47
47
|
function rand(min, max) {
|
|
48
48
|
return min + Math.random() * (max - min);
|
|
49
49
|
}
|
|
50
|
+
function sleep(ms) {
|
|
51
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
|
+
}
|
|
50
53
|
function cosineEase(t) {
|
|
51
54
|
return 0.5 - 0.5 * Math.cos(Math.PI * t);
|
|
52
55
|
}
|
|
@@ -94,6 +97,18 @@ function landingOffset(width, height, config = DEFAULT_STEALTH_CONFIG) {
|
|
|
94
97
|
const dy = rand(...range) * (Math.random() < 0.5 ? -1 : 1);
|
|
95
98
|
return { dx, dy };
|
|
96
99
|
}
|
|
100
|
+
function typingDelay(config = DEFAULT_STEALTH_CONFIG) {
|
|
101
|
+
const roll = Math.random();
|
|
102
|
+
const r = config.typingRhythm;
|
|
103
|
+
if (roll < r.pauseProb) return rand(...r.pauseRange);
|
|
104
|
+
if (roll < r.pauseProb + r.fastProb) return rand(...r.fastRange);
|
|
105
|
+
return rand(...r.normalRange);
|
|
106
|
+
}
|
|
107
|
+
function wheelDelta(step, config = DEFAULT_STEALTH_CONFIG) {
|
|
108
|
+
return Math.round(
|
|
109
|
+
config.wheelPeak * Math.exp(-step * config.wheelDecayRate) * rand(0.85, 1.15)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
97
112
|
var KEY_MAP = {};
|
|
98
113
|
for (let i = 97; i <= 122; i++) {
|
|
99
114
|
const ch = String.fromCharCode(i);
|
|
@@ -117,6 +132,7 @@ Object.assign(KEY_MAP, {
|
|
|
117
132
|
function buildStealthInitScript() {
|
|
118
133
|
return [
|
|
119
134
|
"(function(){",
|
|
135
|
+
' window.__xbStealthVer="57";',
|
|
120
136
|
// 1. AEL event proxy
|
|
121
137
|
" var o=EventTarget.prototype.addEventListener;",
|
|
122
138
|
" var fc=new InputDeviceCapabilities({firesTouchEvents:false});",
|
|
@@ -124,10 +140,10 @@ function buildStealthInitScript() {
|
|
|
124
140
|
" var op=arguments[2];",
|
|
125
141
|
' if(typeof f!=="function")return o.call(this,t,f,op);',
|
|
126
142
|
" var w=function(e){",
|
|
127
|
-
" if(!e
|
|
143
|
+
" if(!e)return f.call(this,e);",
|
|
128
144
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
129
145
|
' if(p==="sourceCapabilities")return fc;',
|
|
130
|
-
' if(p==="isTrusted")return k
|
|
146
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
131
147
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
132
148
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
133
149
|
" return k[p]+_f;",
|
|
@@ -279,6 +295,70 @@ function buildStealthInitScript() {
|
|
|
279
295
|
' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
|
|
280
296
|
' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
|
|
281
297
|
" }catch(e){}",
|
|
298
|
+
// 3f. getCoalescedEvents 合成(d47):真实鼠标 125Hz 采样被浏览器按帧合并,
|
|
299
|
+
// 快速移动时 pointermove.getCoalescedEvents() 返回 2~6 个事件(群内
|
|
300
|
+
// ≈8ms);CDP Input 逐事件派发不走合并管线,coalesced>1 恒为 0(结构性
|
|
301
|
+
// 暴露,间隔模拟救不了——实测 Chrome 帧对齐时 CDP 连发事件被直接丢弃
|
|
302
|
+
// 而非合并)。按 125Hz 物理插值合成 coalesced 群:期望样本数 = dt/8ms-1,
|
|
303
|
+
// 合成事件用真 PointerEvent 构造 + 实例级 isTrusted/timeStamp 遮蔽。
|
|
304
|
+
" try{",
|
|
305
|
+
" if(window.PointerEvent&&PointerEvent.prototype.getCoalescedEvents){",
|
|
306
|
+
" var _gce=PointerEvent.prototype.getCoalescedEvents;",
|
|
307
|
+
" var _lm=null;",
|
|
308
|
+
" var _gceH=function(){",
|
|
309
|
+
" var list=_gce.call(this);",
|
|
310
|
+
' if(this.type!=="pointermove"||!this.isTrusted)return list;',
|
|
311
|
+
" var cur={x:this.clientX,y:this.clientY,ts:this.timeStamp};",
|
|
312
|
+
" var out=null;",
|
|
313
|
+
" if((!list||list.length<2)&&_lm){",
|
|
314
|
+
" var dt=cur.ts-_lm.ts,dx=cur.x-_lm.x,dy=cur.y-_lm.y;",
|
|
315
|
+
// dt 长(帧丢弃后)不代表群覆盖整个 dt —— 真实 coalesced 群只覆盖
|
|
316
|
+
// 最后一帧窗口(≤16.7ms,群内 ≈8ms=125Hz)。合成群从自身往回推 8ms
|
|
317
|
+
// 链,位移取末端占比(span/dt,dt 远大于 span 时位移趋零=丢弃后实况)。
|
|
318
|
+
" var expN=Math.floor(dt/8)-1;",
|
|
319
|
+
// dt<12ms(不足一帧+采样周期)时真实浏览器不会产生合并群 ——
|
|
320
|
+
// 单采样帧 coalesced=1,强行合成会出现 ~2ms 的超物理群内间隔。
|
|
321
|
+
" if(expN>0&&dt>=12&&Math.random()>0.15){",
|
|
322
|
+
" var n=Math.min(4,expN+(Math.random()<0.3?1:0));",
|
|
323
|
+
" var span=Math.min(dt,n*8.3);",
|
|
324
|
+
" var frac=Math.min(1,span/Math.max(dt,1));",
|
|
325
|
+
" out=[];",
|
|
326
|
+
" for(var i=1;i<=n;i++){",
|
|
327
|
+
// k=距自身的步数(含自身共 n+1 个事件,相邻恒 ≈8.3ms=125Hz 周期)
|
|
328
|
+
" var k=n+1-i;",
|
|
329
|
+
' var ev=new PointerEvent("pointermove",{',
|
|
330
|
+
' pointerId:this.pointerId,pointerType:"mouse",isPrimary:this.isPrimary,',
|
|
331
|
+
" clientX:cur.x-dx*frac*(k/(n+1))+(Math.random()-0.5)*1.5,",
|
|
332
|
+
" clientY:cur.y-dy*frac*(k/(n+1))+(Math.random()-0.5)*1.5,",
|
|
333
|
+
" screenX:this.screenX,screenY:this.screenY,",
|
|
334
|
+
" buttons:this.buttons,button:this.button,",
|
|
335
|
+
" bubbles:true,cancelable:true,composed:true,",
|
|
336
|
+
" width:this.width,height:this.height,pressure:this.pressure,",
|
|
337
|
+
" tiltX:this.tiltX,tiltY:this.tiltY,twist:this.twist});",
|
|
338
|
+
" var tsV=cur.ts-k*8.3-Math.random()*1.2;",
|
|
339
|
+
// isTrusted/timeStamp 是实例不可配置属性(defineProperty 抛
|
|
340
|
+
// "Cannot redefine"),改 Proxy 包装:getPrototypeOf/ownKeys 透传,
|
|
341
|
+
// instanceof 与属性枚举行为与真事件一致。IIFE 捕获本次循环的 tsV
|
|
342
|
+
// 快照 —— var 提升会让所有 Proxy 闭包共享最后一次赋值(实测四个
|
|
343
|
+
// 合成事件同时间戳、群内间隔塌到 ~2ms)。
|
|
344
|
+
" out.push((function(e2,t2){return new Proxy(e2,{get:function(t,p){",
|
|
345
|
+
' if(p==="isTrusted")return true;',
|
|
346
|
+
' if(p==="timeStamp")return t2;',
|
|
347
|
+
' var v=Reflect.get(t,p);return typeof v==="function"?v.bind(t):v;',
|
|
348
|
+
" }});})(ev,tsV));",
|
|
349
|
+
" }",
|
|
350
|
+
" out.push(this);",
|
|
351
|
+
" }",
|
|
352
|
+
" }",
|
|
353
|
+
" _lm=cur;",
|
|
354
|
+
" return out||list;",
|
|
355
|
+
" };",
|
|
356
|
+
" PointerEvent.prototype.getCoalescedEvents=_gceH;",
|
|
357
|
+
// name 反查伪装:var _gceH=fn 的具名推断会暴露 hook(原生 name 是
|
|
358
|
+
// "getCoalescedEvents")——toString 白名单管不到 name 属性。
|
|
359
|
+
' try{Object.defineProperty(_gceH,"name",{value:"getCoalescedEvents"});}catch(e){}',
|
|
360
|
+
" }",
|
|
361
|
+
" }catch(e){}",
|
|
282
362
|
// 3d. Chrome object depth (d24): automation fakes usually only set
|
|
283
363
|
// window.chrome = {}; deep checks hit app.run/runtime/csi/loadTimes.
|
|
284
364
|
" try{",
|
|
@@ -313,11 +393,12 @@ function buildStealthInitScript() {
|
|
|
313
393
|
' if(this===HTMLCanvasElement.prototype.toDataURL)return"function toDataURL() { [native code] }";',
|
|
314
394
|
' if(this===AnalyserNode.prototype.getFloatFrequencyData)return"function getFloatFrequencyData() { [native code] }";',
|
|
315
395
|
' if(this===AudioBuffer.prototype.getChannelData)return"function getChannelData() { [native code] }";',
|
|
396
|
+
' if(this===_gceH)return"function getCoalescedEvents() { [native code] }";',
|
|
316
397
|
" return _ts.call(this);",
|
|
317
398
|
" };",
|
|
318
399
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
319
400
|
" var _ba=function(k,p){",
|
|
320
|
-
' if(p==="isTrusted")return k
|
|
401
|
+
' if(p==="isTrusted")return Reflect.get(k,"isTrusted");',
|
|
321
402
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
322
403
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
323
404
|
" }",
|
|
@@ -373,26 +454,26 @@ var XBMouseImpl = class {
|
|
|
373
454
|
await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: p.x, y: p.y, button: this._button });
|
|
374
455
|
this._x = p.x;
|
|
375
456
|
this._y = p.y;
|
|
376
|
-
await
|
|
457
|
+
await sleep2(p.delay);
|
|
377
458
|
}
|
|
378
459
|
this._x = tx;
|
|
379
460
|
this._y = ty;
|
|
380
461
|
if (_truncated) {
|
|
381
462
|
await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: tx, y: ty, button: this._button });
|
|
382
463
|
}
|
|
383
|
-
await
|
|
464
|
+
await sleep2(rand(...DEFAULT_STEALTH_CONFIG.aimPause));
|
|
384
465
|
} else {
|
|
385
466
|
await this.move(tx, ty);
|
|
386
467
|
}
|
|
387
468
|
await this.down({ button });
|
|
388
|
-
await
|
|
469
|
+
await sleep2(stealth ? rand(...DEFAULT_STEALTH_CONFIG.pressDuration) : opts.delay ?? 0);
|
|
389
470
|
const rx = stealth ? this._x + rand(...DEFAULT_STEALTH_CONFIG.releaseDrift) * (Math.random() < 0.5 ? -1 : 1) : this._x;
|
|
390
471
|
const ry = stealth ? this._y + rand(...DEFAULT_STEALTH_CONFIG.releaseDrift) * (Math.random() < 0.5 ? -1 : 1) : this._y;
|
|
391
472
|
this._x = rx;
|
|
392
473
|
this._y = ry;
|
|
393
474
|
await this.send("Input.dispatchMouseEvent", { type: "mouseReleased", x: rx, y: ry, button, clickCount: opts.clickCount ?? 1 });
|
|
394
475
|
for (let i = 1; i < (opts.clickCount ?? 1); i++) {
|
|
395
|
-
if (opts.delay) await
|
|
476
|
+
if (opts.delay) await sleep2(opts.delay);
|
|
396
477
|
await this.down({ button });
|
|
397
478
|
await this.up({ button });
|
|
398
479
|
}
|
|
@@ -423,12 +504,14 @@ var XBMouseImpl = class {
|
|
|
423
504
|
});
|
|
424
505
|
}
|
|
425
506
|
async move(x, y, opts = {}) {
|
|
507
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
426
508
|
const steps = Math.max(1, opts.steps ?? 1);
|
|
427
509
|
const fromX = this._x;
|
|
428
510
|
const fromY = this._y;
|
|
429
511
|
const dx = x - fromX;
|
|
430
512
|
const dy = y - fromY;
|
|
431
513
|
for (let i = 1; i <= steps; i++) {
|
|
514
|
+
if (stealth) await sleep2(rand(16, 28));
|
|
432
515
|
const t = i / steps;
|
|
433
516
|
this._x = fromX + dx * t;
|
|
434
517
|
this._y = fromY + dy * t;
|
|
@@ -455,7 +538,7 @@ var XBMouseImpl = class {
|
|
|
455
538
|
await this.conn.send(method, params, this.sessionId);
|
|
456
539
|
}
|
|
457
540
|
};
|
|
458
|
-
function
|
|
541
|
+
function sleep2(ms) {
|
|
459
542
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
460
543
|
}
|
|
461
544
|
|
|
@@ -485,17 +568,17 @@ var XBKeyboardImpl = class {
|
|
|
485
568
|
await this.dispatchKeyEvent(downParams);
|
|
486
569
|
if (mapping.text) {
|
|
487
570
|
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
488
|
-
await
|
|
571
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
489
572
|
}
|
|
490
573
|
await this.dispatchKeyEvent({
|
|
491
574
|
type: "char",
|
|
492
575
|
text: mapping.text
|
|
493
576
|
});
|
|
494
577
|
}
|
|
495
|
-
if (delay > 0) await
|
|
578
|
+
if (delay > 0) await sleep3(delay);
|
|
496
579
|
else {
|
|
497
580
|
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
498
|
-
await
|
|
581
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
499
582
|
}
|
|
500
583
|
}
|
|
501
584
|
const upParams = {
|
|
@@ -537,37 +620,61 @@ var XBKeyboardImpl = class {
|
|
|
537
620
|
await this.dispatchKeyEvent(params);
|
|
538
621
|
}
|
|
539
622
|
async type(text, opts = {}) {
|
|
540
|
-
const
|
|
623
|
+
const stealth = opts.stealth ?? process.env.XBROWSER_STEALTH !== "off";
|
|
624
|
+
const fixedDelay = opts.delay ?? 0;
|
|
541
625
|
for (const char of text) {
|
|
542
|
-
if (
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
key: mapping.key,
|
|
547
|
-
code: mapping.code
|
|
548
|
-
};
|
|
549
|
-
if (mapping.text) {
|
|
550
|
-
downParams.text = mapping.text;
|
|
551
|
-
downParams.unmodifiedText = mapping.text;
|
|
626
|
+
if (fixedDelay > 0) {
|
|
627
|
+
await sleep3(fixedDelay);
|
|
628
|
+
} else if (stealth) {
|
|
629
|
+
await sleep3(typingDelay());
|
|
552
630
|
}
|
|
553
|
-
if (
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
text: mapping.text
|
|
561
|
-
});
|
|
631
|
+
if (stealth && char.length === 1 && Math.random() < DEFAULT_STEALTH_CONFIG.typoProbability) {
|
|
632
|
+
const wrong = NEIGHBOR_KEYS[char];
|
|
633
|
+
if (wrong && wrong !== char) {
|
|
634
|
+
await this.dispatchKeySequence(resolveKeyMapping(wrong), stealth);
|
|
635
|
+
await sleep3(rand(120, 420));
|
|
636
|
+
await this.dispatchKeySequence(KEY_MAP2.Backspace, stealth);
|
|
637
|
+
}
|
|
562
638
|
}
|
|
563
|
-
await this.
|
|
564
|
-
type: "keyUp",
|
|
565
|
-
key: mapping.key,
|
|
566
|
-
code: mapping.code,
|
|
567
|
-
...mapping.keyCode ? { windowsVirtualKeyCode: mapping.keyCode } : {}
|
|
568
|
-
});
|
|
639
|
+
await this.dispatchKeySequence(resolveKeyMapping(char), stealth);
|
|
569
640
|
}
|
|
570
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Single-key event sequence: rawKeyDown → (char) → keyUp.
|
|
644
|
+
* Shared by press() and type() — two separate implementations drifted
|
|
645
|
+
* apart once already (S60: type() lost the keyPressDuration that press()
|
|
646
|
+
* had; d50 caught it as 33/33 keys with down→up of 2ms).
|
|
647
|
+
*/
|
|
648
|
+
async dispatchKeySequence(mapping, stealth) {
|
|
649
|
+
const downParams = {
|
|
650
|
+
type: "rawKeyDown",
|
|
651
|
+
key: mapping.key,
|
|
652
|
+
code: mapping.code
|
|
653
|
+
};
|
|
654
|
+
if (mapping.text) {
|
|
655
|
+
downParams.text = mapping.text;
|
|
656
|
+
downParams.unmodifiedText = mapping.text;
|
|
657
|
+
}
|
|
658
|
+
if (mapping.keyCode) {
|
|
659
|
+
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
660
|
+
}
|
|
661
|
+
await this.dispatchKeyEvent(downParams);
|
|
662
|
+
if (mapping.text) {
|
|
663
|
+
await this.dispatchKeyEvent({ type: "char", text: mapping.text });
|
|
664
|
+
}
|
|
665
|
+
if (stealth) {
|
|
666
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
667
|
+
}
|
|
668
|
+
const upParams = {
|
|
669
|
+
type: "keyUp",
|
|
670
|
+
key: mapping.key,
|
|
671
|
+
code: mapping.code
|
|
672
|
+
};
|
|
673
|
+
if (mapping.keyCode) {
|
|
674
|
+
upParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
675
|
+
}
|
|
676
|
+
await this.dispatchKeyEvent(upParams);
|
|
677
|
+
}
|
|
571
678
|
async insertText(text) {
|
|
572
679
|
await this.conn.send(
|
|
573
680
|
"Input.insertText",
|
|
@@ -575,6 +682,35 @@ var XBKeyboardImpl = class {
|
|
|
575
682
|
this.sessionId
|
|
576
683
|
);
|
|
577
684
|
}
|
|
685
|
+
/**
|
|
686
|
+
* Navigation key press using CDP type 'keyDown' (NOT rawKeyDown).
|
|
687
|
+
* rawKeyDown skips browser default actions — select option navigation,
|
|
688
|
+
* arrow-key scrolling etc. never fire under it (d53: ArrowDown on a
|
|
689
|
+
* focused <select> left the value unchanged). keyDown carries the
|
|
690
|
+
* default action.
|
|
691
|
+
*/
|
|
692
|
+
async pressNav(key) {
|
|
693
|
+
const mapping = resolveKeyMapping(key);
|
|
694
|
+
const downParams = {
|
|
695
|
+
type: "keyDown",
|
|
696
|
+
key: mapping.key,
|
|
697
|
+
code: mapping.code
|
|
698
|
+
};
|
|
699
|
+
if (mapping.keyCode) {
|
|
700
|
+
downParams.windowsVirtualKeyCode = mapping.keyCode;
|
|
701
|
+
downParams.nativeVirtualKeyCode = mapping.keyCode;
|
|
702
|
+
}
|
|
703
|
+
await this.dispatchKeyEvent(downParams);
|
|
704
|
+
if (process.env.XBROWSER_STEALTH !== "off") {
|
|
705
|
+
await sleep3(rand(...DEFAULT_STEALTH_CONFIG.keyPressDuration));
|
|
706
|
+
}
|
|
707
|
+
await this.dispatchKeyEvent({
|
|
708
|
+
type: "keyUp",
|
|
709
|
+
key: mapping.key,
|
|
710
|
+
code: mapping.code,
|
|
711
|
+
...mapping.keyCode ? { windowsVirtualKeyCode: mapping.keyCode } : {}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
578
714
|
async dispatchKeyEvent(params) {
|
|
579
715
|
await this.conn.send("Input.dispatchKeyEvent", params, this.sessionId);
|
|
580
716
|
}
|
|
@@ -629,7 +765,44 @@ var KEY_MAP2 = {
|
|
|
629
765
|
F11: { key: "F11", code: "F11", keyCode: 122 },
|
|
630
766
|
F12: { key: "F12", code: "F12", keyCode: 123 }
|
|
631
767
|
};
|
|
632
|
-
|
|
768
|
+
var NEIGHBOR_KEYS = {
|
|
769
|
+
a: "s",
|
|
770
|
+
b: "v",
|
|
771
|
+
c: "x",
|
|
772
|
+
d: "f",
|
|
773
|
+
e: "r",
|
|
774
|
+
f: "g",
|
|
775
|
+
g: "h",
|
|
776
|
+
h: "j",
|
|
777
|
+
i: "o",
|
|
778
|
+
j: "k",
|
|
779
|
+
k: "l",
|
|
780
|
+
l: "k",
|
|
781
|
+
m: "n",
|
|
782
|
+
n: "m",
|
|
783
|
+
o: "p",
|
|
784
|
+
p: "o",
|
|
785
|
+
q: "w",
|
|
786
|
+
r: "t",
|
|
787
|
+
s: "d",
|
|
788
|
+
t: "y",
|
|
789
|
+
u: "i",
|
|
790
|
+
v: "b",
|
|
791
|
+
w: "e",
|
|
792
|
+
x: "c",
|
|
793
|
+
y: "u",
|
|
794
|
+
z: "x",
|
|
795
|
+
"1": "2",
|
|
796
|
+
"2": "3",
|
|
797
|
+
"3": "4",
|
|
798
|
+
"4": "5",
|
|
799
|
+
"5": "6",
|
|
800
|
+
"6": "7",
|
|
801
|
+
"7": "8",
|
|
802
|
+
"8": "9",
|
|
803
|
+
"9": "0"
|
|
804
|
+
};
|
|
805
|
+
function sleep3(ms) {
|
|
633
806
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
634
807
|
}
|
|
635
808
|
|
|
@@ -946,23 +1119,13 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
946
1119
|
async press(key, opts = {}) {
|
|
947
1120
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
948
1121
|
await scrollIntoView(this.page, this.selector);
|
|
949
|
-
await this.
|
|
950
|
-
(function() {
|
|
951
|
-
const el = ${this._q(this.selector)};
|
|
952
|
-
if (el) el.focus();
|
|
953
|
-
})()
|
|
954
|
-
`);
|
|
1122
|
+
await this.click({ timeout: opts.timeout });
|
|
955
1123
|
await this.page.keyboard.press(key);
|
|
956
1124
|
}
|
|
957
1125
|
async pressSequentially(text, opts = {}) {
|
|
958
1126
|
await waitForActionable(this.page, this.selector, { timeout: opts.timeout });
|
|
959
1127
|
await scrollIntoView(this.page, this.selector);
|
|
960
|
-
await this.
|
|
961
|
-
(function() {
|
|
962
|
-
const el = ${this._q(this.selector)};
|
|
963
|
-
if (el) el.focus();
|
|
964
|
-
})()
|
|
965
|
-
`);
|
|
1128
|
+
await this.click({ timeout: opts.timeout });
|
|
966
1129
|
await this.page.keyboard.type(text, { delay: opts.delay });
|
|
967
1130
|
}
|
|
968
1131
|
async hover(opts = {}) {
|
|
@@ -1017,11 +1180,36 @@ var XBLocatorImpl = class _XBLocatorImpl {
|
|
|
1017
1180
|
async selectOption(value) {
|
|
1018
1181
|
await waitForActionable(this.page, this.selector);
|
|
1019
1182
|
const values = Array.isArray(value) ? value : [value];
|
|
1020
|
-
const
|
|
1183
|
+
const info = await this.page.evaluate(`
|
|
1021
1184
|
(function() {
|
|
1022
1185
|
const el = ${this._q(this.selector)};
|
|
1023
1186
|
if (!el || el.tagName !== 'SELECT') throw new Error('Not a select element');
|
|
1024
|
-
|
|
1187
|
+
const values = ${JSON.stringify(values)};
|
|
1188
|
+
let target = -1, targetValue = '';
|
|
1189
|
+
outer:
|
|
1190
|
+
for (let i = 0; i < el.options.length; i++) {
|
|
1191
|
+
const opt = el.options[i];
|
|
1192
|
+
for (const v of values) {
|
|
1193
|
+
const hit = typeof v === 'object'
|
|
1194
|
+
? (v.label !== undefined ? opt.label === v.label
|
|
1195
|
+
: v.value !== undefined ? opt.value === v.value
|
|
1196
|
+
: opt.index === v.index)
|
|
1197
|
+
: (opt.value === v || opt.label === v);
|
|
1198
|
+
if (hit) { target = i; targetValue = opt.value; break outer; }
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return { cur: el.selectedIndex, target: target, targetValue: targetValue, multiple: el.multiple };
|
|
1202
|
+
})()
|
|
1203
|
+
`);
|
|
1204
|
+
if (info.target < 0) {
|
|
1205
|
+
throw new Error(`Option not found: ${JSON.stringify(values)}`);
|
|
1206
|
+
}
|
|
1207
|
+
if (!info.multiple) {
|
|
1208
|
+
await this.click({ timeout: 5e3 });
|
|
1209
|
+
}
|
|
1210
|
+
const selected = await this.page.evaluate(`
|
|
1211
|
+
(function() {
|
|
1212
|
+
const el = ${this._q(this.selector)};
|
|
1025
1213
|
const values = ${JSON.stringify(values)};
|
|
1026
1214
|
const selectedValues = [];
|
|
1027
1215
|
|
|
@@ -3714,6 +3902,9 @@ async function launch(options = {}) {
|
|
|
3714
3902
|
}
|
|
3715
3903
|
|
|
3716
3904
|
export {
|
|
3905
|
+
rand,
|
|
3906
|
+
sleep,
|
|
3907
|
+
wheelDelta,
|
|
3717
3908
|
XBMouseImpl,
|
|
3718
3909
|
XBKeyboardImpl,
|
|
3719
3910
|
waitForActionable,
|