screenci 0.0.17 → 0.0.19
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/README.md +3 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +38 -31
- package/dist/cli.js.map +1 -1
- package/dist/e2e/instrument.e2e.js +21 -2
- package/dist/e2e/instrument.e2e.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/src/autoZoom.d.ts +5 -1
- package/dist/src/autoZoom.d.ts.map +1 -1
- package/dist/src/autoZoom.js +30 -1
- package/dist/src/autoZoom.js.map +1 -1
- package/dist/src/chromiumProfile.d.ts +6 -0
- package/dist/src/chromiumProfile.d.ts.map +1 -0
- package/dist/src/chromiumProfile.js +20 -0
- package/dist/src/chromiumProfile.js.map +1 -0
- package/dist/src/cue.d.ts +0 -63
- package/dist/src/cue.d.ts.map +1 -1
- package/dist/src/cue.js +0 -84
- package/dist/src/cue.js.map +1 -1
- package/dist/src/dimensions.d.ts +7 -0
- package/dist/src/dimensions.d.ts.map +1 -1
- package/dist/src/dimensions.js +6 -0
- package/dist/src/dimensions.js.map +1 -1
- package/dist/src/events.d.ts +19 -8
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +20 -13
- package/dist/src/events.js.map +1 -1
- package/dist/src/instrument.d.ts.map +1 -1
- package/dist/src/instrument.js +354 -172
- package/dist/src/instrument.js.map +1 -1
- package/dist/src/recordingData.d.ts +14 -7
- package/dist/src/recordingData.d.ts.map +1 -1
- package/dist/src/scroll.d.ts +47 -0
- package/dist/src/scroll.d.ts.map +1 -0
- package/dist/src/scroll.js +347 -0
- package/dist/src/scroll.js.map +1 -0
- package/dist/src/types.d.ts +21 -22
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +2 -2
- package/dist/src/types.js.map +1 -1
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +30 -3
- package/dist/src/video.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skills/screenci/SKILL.md +20 -7
- package/skills/screenci/references/record.md +7 -5
package/dist/src/instrument.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { logger } from './logger.js';
|
|
2
|
-
import { isInsideAutoZoom, getZoomDuration, getPostZoomInOutDelay, getLastZoomLocation, setLastZoomLocation, } from './autoZoom.js';
|
|
2
|
+
import { isInsideAutoZoom, getZoomDuration, getZoomEasing, getPostZoomInOutDelay, getLastZoomLocation, setLastZoomLocation, } from './autoZoom.js';
|
|
3
|
+
import { isInsideHide } from './hide.js';
|
|
4
|
+
import { ZoomScrollHandler } from './scroll.js';
|
|
3
5
|
let activeClickRecorder = null;
|
|
4
6
|
export function setActiveClickRecorder(recorder) {
|
|
5
7
|
activeClickRecorder = recorder;
|
|
@@ -105,6 +107,18 @@ const FRAME_LOCATOR_SELF_RETURN_METHODS = [
|
|
|
105
107
|
'last',
|
|
106
108
|
'nth',
|
|
107
109
|
];
|
|
110
|
+
function canUseDirectMouseClickAfterScroll(options) {
|
|
111
|
+
if (!options)
|
|
112
|
+
return true;
|
|
113
|
+
const unsupported = [
|
|
114
|
+
'force',
|
|
115
|
+
'modifiers',
|
|
116
|
+
'noWaitAfter',
|
|
117
|
+
'timeout',
|
|
118
|
+
'trial',
|
|
119
|
+
];
|
|
120
|
+
return unsupported.every((key) => options[key] === undefined);
|
|
121
|
+
}
|
|
108
122
|
// Per-page storage for the most recently captured DOM click event data.
|
|
109
123
|
// Reset to null before each instrumented click; set by the exposeFunction callback.
|
|
110
124
|
const pendingClickData = new WeakMap();
|
|
@@ -144,43 +158,10 @@ export function scrollIntoViewAsync(locator, options = {}) {
|
|
|
144
158
|
}));
|
|
145
159
|
}), { behavior, block, timeout, postScrollTimeout });
|
|
146
160
|
}
|
|
147
|
-
/**
|
|
148
|
-
* Scrolls the locator into view only if it is at least partially outside the
|
|
149
|
-
* viewport, then returns the (possibly updated) bounding box as an ElementRect.
|
|
150
|
-
* Returns undefined if the bounding box cannot be determined.
|
|
151
|
-
*/
|
|
152
|
-
async function scrollIntoViewIfNeeded(locator) {
|
|
153
|
-
const page = locator.page();
|
|
154
|
-
const bb = await locator.boundingBox();
|
|
155
|
-
if (!bb)
|
|
156
|
-
return undefined;
|
|
157
|
-
const viewportSize = page.viewportSize();
|
|
158
|
-
if (viewportSize) {
|
|
159
|
-
const isFullyInViewport = bb.x >= 0 &&
|
|
160
|
-
bb.y >= 0 &&
|
|
161
|
-
bb.x + bb.width <= viewportSize.width &&
|
|
162
|
-
bb.y + bb.height <= viewportSize.height;
|
|
163
|
-
if (!isFullyInViewport) {
|
|
164
|
-
await scrollIntoViewAsync(locator);
|
|
165
|
-
const newBb = await locator.boundingBox();
|
|
166
|
-
if (newBb)
|
|
167
|
-
return {
|
|
168
|
-
x: newBb.x,
|
|
169
|
-
y: newBb.y,
|
|
170
|
-
width: newBb.width,
|
|
171
|
-
height: newBb.height,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
logger.warn('[screenci] Unable to determine viewport size; skipping auto-scroll check.');
|
|
177
|
-
}
|
|
178
|
-
return { x: bb.x, y: bb.y, width: bb.width, height: bb.height };
|
|
179
|
-
}
|
|
180
161
|
const CURSOR_STEP_MS = 1000 / 60;
|
|
181
162
|
/**
|
|
182
163
|
* Physically moves the mouse from its current tracked position to (targetX,
|
|
183
|
-
* targetY) over `duration` ms using `easing`, then returns a
|
|
164
|
+
* targetY) over `duration` ms using `easing`, then returns a FocusChangeEvent
|
|
184
165
|
* whose startMs is `eventStartMs` (which may predate this call, e.g. when a
|
|
185
166
|
* scroll consumed part of moveDuration). When duration ≤ 0 the cursor is
|
|
186
167
|
* snapped directly to the target with a single move call.
|
|
@@ -208,32 +189,40 @@ async function animateMouseMove(page, mouseMoveInternal, targetX, targetY, durat
|
|
|
208
189
|
mousePositions.set(page, { x: targetX, y: targetY });
|
|
209
190
|
const endMs = Date.now();
|
|
210
191
|
return {
|
|
211
|
-
type: '
|
|
192
|
+
type: 'focusChange',
|
|
212
193
|
startMs: eventStartMs,
|
|
213
194
|
endMs,
|
|
214
|
-
duration: Math.max(0, endMs - eventStartMs),
|
|
215
195
|
x: targetX,
|
|
216
196
|
y: targetY,
|
|
217
197
|
easing,
|
|
218
198
|
...(elementRect !== undefined ? { elementRect } : {}),
|
|
219
199
|
};
|
|
220
200
|
}
|
|
201
|
+
function resolveMoveStartMs(scrollResult, fallbackStartMs) {
|
|
202
|
+
return scrollResult.shouldScrollBeforeMouseMove
|
|
203
|
+
? scrollResult.scrollEndMs
|
|
204
|
+
: fallbackStartMs;
|
|
205
|
+
}
|
|
206
|
+
function resolveMoveDuration(scrollResult, resolvedDuration) {
|
|
207
|
+
return scrollResult.shouldScrollBeforeMouseMove
|
|
208
|
+
? resolvedDuration
|
|
209
|
+
: Math.max(0, resolvedDuration - scrollResult.scrollElapsedMs);
|
|
210
|
+
}
|
|
221
211
|
/**
|
|
222
212
|
* Performs all click mechanics (scroll-check, zoom handling, cursor animation,
|
|
223
213
|
* click, post-click move) and returns the collected timing/position data.
|
|
224
214
|
* Returns null if coordinates could not be determined (no DOM event and no
|
|
225
215
|
* locator bounding box).
|
|
226
216
|
*/
|
|
227
|
-
async function performClickActions(locator, doClick, clickOptions, position, moveDuration, moveSpeed, beforeClickPause = CLICK_DURATION_MS / 2, moveEasing = 'ease-in-out', postClickPause = CLICK_DURATION_MS / 2, postClickMove) {
|
|
217
|
+
async function performClickActions(locator, doClick, clickOptions, autoZoomOptions, position, moveDuration, moveSpeed, beforeClickPause = CLICK_DURATION_MS / 2, moveEasing = 'ease-in-out', postClickPause = CLICK_DURATION_MS / 2, postClickMove) {
|
|
228
218
|
const page = locator.page();
|
|
229
219
|
pendingClickData.set(page, null);
|
|
230
220
|
const halfClickDuration = CLICK_DURATION_MS / 2;
|
|
231
221
|
const mouseMoveInternal = originalMouseMoves.get(page) ?? page.mouse.move.bind(page.mouse);
|
|
232
|
-
// Capture before any setLastZoomLocation call changes the state.
|
|
233
|
-
const isFirstAutoZoomEvent = isInsideAutoZoom() && getLastZoomLocation() === null;
|
|
234
222
|
const moveStartTime = Date.now();
|
|
235
|
-
const
|
|
236
|
-
const scrollElapsedMs =
|
|
223
|
+
const scrollResult = await new ZoomScrollHandler(autoZoomOptions).scroll(locator);
|
|
224
|
+
const { locatorRect, scrollElapsedMs } = scrollResult;
|
|
225
|
+
const isFirstAutoZoomEvent = scrollResult.isFirstAutoZoomInteraction;
|
|
237
226
|
if (!locatorRect) {
|
|
238
227
|
logger.warn('[screenci] Unable to get locator bounding box; skipping auto-scroll check.');
|
|
239
228
|
}
|
|
@@ -269,11 +258,6 @@ async function performClickActions(locator, doClick, clickOptions, position, mov
|
|
|
269
258
|
y: locatorRect.height / 2,
|
|
270
259
|
}
|
|
271
260
|
: undefined;
|
|
272
|
-
// No physical mouse movement happens during the scroll. moveStartTime is
|
|
273
|
-
// captured before the scroll so the recorded event spans scroll-start →
|
|
274
|
-
// animation-end, making the cursor appear to move during the scroll in the
|
|
275
|
-
// video. After the scroll, the remaining duration drives the physical easing
|
|
276
|
-
// animation via the shared helper.
|
|
277
261
|
if (targetPos && locatorRect) {
|
|
278
262
|
const targetX = locatorRect.x + targetPos.x;
|
|
279
263
|
const targetY = locatorRect.y + targetPos.y;
|
|
@@ -283,19 +267,40 @@ async function performClickActions(locator, doClick, clickOptions, position, mov
|
|
|
283
267
|
defaultDuration: 1000,
|
|
284
268
|
context: 'click move',
|
|
285
269
|
});
|
|
286
|
-
|
|
287
|
-
|
|
270
|
+
if (scrollElapsedMs > 0 && !scrollResult.shouldScrollBeforeMouseMove) {
|
|
271
|
+
await mouseMoveInternal(targetX, targetY);
|
|
272
|
+
mousePositions.set(page, { x: targetX, y: targetY });
|
|
273
|
+
const remainingDuration = Math.max(0, resolvedDuration - scrollElapsedMs);
|
|
274
|
+
if (remainingDuration > 0) {
|
|
275
|
+
await new Promise((resolve) => setTimeout(resolve, remainingDuration));
|
|
276
|
+
}
|
|
277
|
+
const moveEndTime = Date.now();
|
|
278
|
+
innerEvents.push({
|
|
279
|
+
type: 'focusChange',
|
|
280
|
+
startMs: moveStartTime,
|
|
281
|
+
endMs: moveEndTime,
|
|
282
|
+
x: targetX,
|
|
283
|
+
y: targetY,
|
|
284
|
+
easing: moveEasing,
|
|
285
|
+
focusOnly: false,
|
|
286
|
+
elementRect: locatorRect,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, targetX, targetY, resolveMoveDuration(scrollResult, resolvedDuration), moveEasing, resolveMoveStartMs(scrollResult, moveStartTime), locatorRect));
|
|
291
|
+
}
|
|
288
292
|
}
|
|
289
293
|
else {
|
|
290
294
|
assertDurationOrSpeed(moveDuration, moveSpeed, 'click move');
|
|
291
|
-
const remainingMs = Math.max(0,
|
|
295
|
+
const remainingMs = Math.max(0, moveSpeed === undefined ? (moveDuration ?? 1000) : 0);
|
|
292
296
|
if (remainingMs > 0) {
|
|
293
297
|
await new Promise((resolve) => setTimeout(resolve, remainingMs));
|
|
294
298
|
}
|
|
295
299
|
}
|
|
300
|
+
const zoomDur = isInsideAutoZoom() && locatorRect ? (getZoomDuration() ?? 0) : 0;
|
|
296
301
|
const effectiveBeforeClickPause = isFirstAutoZoomEvent
|
|
297
|
-
? Math.max(beforeClickPause, getPostZoomInOutDelay() ?? 0)
|
|
298
|
-
: beforeClickPause;
|
|
302
|
+
? Math.max(beforeClickPause, getPostZoomInOutDelay() ?? 0, zoomDur)
|
|
303
|
+
: Math.max(beforeClickPause, zoomDur);
|
|
299
304
|
await new Promise((resolve) => setTimeout(resolve, effectiveBeforeClickPause));
|
|
300
305
|
await new Promise((resolve) => setTimeout(resolve, halfClickDuration));
|
|
301
306
|
// Note click can take some time, but better to show it before than after
|
|
@@ -307,11 +312,47 @@ async function performClickActions(locator, doClick, clickOptions, position, mov
|
|
|
307
312
|
endMs: clickTime,
|
|
308
313
|
easing: 'ease-in-out',
|
|
309
314
|
});
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
315
|
+
if (scrollElapsedMs > 0 &&
|
|
316
|
+
targetPos &&
|
|
317
|
+
locatorRect &&
|
|
318
|
+
canUseDirectMouseClickAfterScroll(clickOptions)) {
|
|
319
|
+
const mouseClickOptions = {
|
|
320
|
+
...(clickOptions?.button !== undefined
|
|
321
|
+
? { button: clickOptions.button }
|
|
322
|
+
: {}),
|
|
323
|
+
...(clickOptions?.clickCount !== undefined
|
|
324
|
+
? { clickCount: clickOptions.clickCount }
|
|
325
|
+
: {}),
|
|
326
|
+
};
|
|
327
|
+
await page.mouse.down(mouseClickOptions);
|
|
328
|
+
if (clickOptions?.delay) {
|
|
329
|
+
await new Promise((resolve) => setTimeout(resolve, clickOptions.delay));
|
|
330
|
+
}
|
|
331
|
+
await page.mouse.up(mouseClickOptions);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
await doClick({
|
|
335
|
+
...clickOptions,
|
|
336
|
+
...(targetPos ? { position: targetPos } : {}),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
314
339
|
const domClickData = pendingClickData.get(page);
|
|
340
|
+
if (domClickData) {
|
|
341
|
+
const lastMouseMoveIndex = innerEvents.findIndex((e) => e.type === 'focusChange' || e.type === 'mouseMove');
|
|
342
|
+
if (lastMouseMoveIndex !== -1) {
|
|
343
|
+
const existingMove = innerEvents[lastMouseMoveIndex];
|
|
344
|
+
if (existingMove?.type === 'focusChange' ||
|
|
345
|
+
existingMove?.type === 'mouseMove') {
|
|
346
|
+
innerEvents[lastMouseMoveIndex] = {
|
|
347
|
+
...existingMove,
|
|
348
|
+
x: domClickData.x,
|
|
349
|
+
y: domClickData.y,
|
|
350
|
+
elementRect: domClickData.targetRect,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
mousePositions.set(page, { x: domClickData.x, y: domClickData.y });
|
|
355
|
+
}
|
|
315
356
|
const mouseUpEnd = Date.now() + halfClickDuration;
|
|
316
357
|
innerEvents.push({
|
|
317
358
|
type: 'mouseUp',
|
|
@@ -385,26 +426,23 @@ async function performClickActions(locator, doClick, clickOptions, position, mov
|
|
|
385
426
|
}
|
|
386
427
|
const postClickMoveEndMs = Date.now();
|
|
387
428
|
innerEvents.push({
|
|
388
|
-
type: '
|
|
429
|
+
type: 'focusChange',
|
|
389
430
|
startMs: postClickMoveStartMs,
|
|
390
431
|
endMs: postClickMoveEndMs,
|
|
391
|
-
duration: Math.max(0, postClickMoveEndMs - postClickMoveStartMs),
|
|
392
432
|
x: targetX,
|
|
393
433
|
y: targetY,
|
|
394
434
|
easing,
|
|
395
|
-
zoomFollow: false,
|
|
396
435
|
});
|
|
397
436
|
mousePositions.set(page, { x: targetX, y: targetY });
|
|
398
437
|
}
|
|
399
438
|
}
|
|
400
439
|
let elementRect;
|
|
401
|
-
if (
|
|
402
|
-
elementRect = locatorRect;
|
|
403
|
-
}
|
|
404
|
-
else if (domClickData) {
|
|
405
|
-
console.warn('[screenci] using DOM click data as fallback for elementRect');
|
|
440
|
+
if (domClickData) {
|
|
406
441
|
elementRect = domClickData.targetRect;
|
|
407
442
|
}
|
|
443
|
+
else if (locatorRect) {
|
|
444
|
+
elementRect = locatorRect ?? undefined;
|
|
445
|
+
}
|
|
408
446
|
if (elementRect) {
|
|
409
447
|
return {
|
|
410
448
|
elementRect,
|
|
@@ -416,41 +454,57 @@ async function performClickActions(locator, doClick, clickOptions, position, mov
|
|
|
416
454
|
return null;
|
|
417
455
|
}
|
|
418
456
|
}
|
|
419
|
-
async function
|
|
457
|
+
async function prepareAutoZoomForLocator(locator, eventType, autoZoomOptions) {
|
|
458
|
+
const scrollHandler = new ZoomScrollHandler(autoZoomOptions);
|
|
459
|
+
const zoomDur = scrollHandler.isInsideAutoZoom ? (getZoomDuration() ?? 0) : 0;
|
|
460
|
+
if (scrollHandler.isInsideAutoZoom &&
|
|
461
|
+
scrollHandler.hadPreviousZoomLocation &&
|
|
462
|
+
zoomDur > 0) {
|
|
463
|
+
await sleep(zoomDur);
|
|
464
|
+
}
|
|
465
|
+
const { locatorRect, isFirstAutoZoomInteraction } = await scrollHandler.scroll(locator);
|
|
466
|
+
if (scrollHandler.isInsideAutoZoom && locatorRect) {
|
|
467
|
+
if (isFirstAutoZoomInteraction && zoomDur > 0) {
|
|
468
|
+
await sleep(zoomDur);
|
|
469
|
+
}
|
|
470
|
+
setLastZoomLocation({
|
|
471
|
+
x: locatorRect.x + locatorRect.width / 2,
|
|
472
|
+
y: locatorRect.y + locatorRect.height / 2,
|
|
473
|
+
elementRect: locatorRect,
|
|
474
|
+
eventType,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
return { locatorRect, isFirstAutoZoomInteraction };
|
|
478
|
+
}
|
|
479
|
+
async function performSimpleAction(locator, doAction, options, subType, clickOpt, autoZoomOptions, position, recordMousePress = subType === 'tap') {
|
|
420
480
|
await sleep(PRE_ACTION_SLEEP);
|
|
421
481
|
let innerEvents = [];
|
|
422
482
|
let elementRect;
|
|
423
483
|
if (clickOpt !== undefined) {
|
|
424
484
|
const { moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove, } = clickOpt;
|
|
425
|
-
const clickActionResult = await performClickActions(locator, doAction, {}, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
485
|
+
const clickActionResult = await performClickActions(locator, doAction, {}, autoZoomOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
426
486
|
innerEvents = clickActionResult?.innerEvents ?? [];
|
|
427
487
|
elementRect = clickActionResult?.elementRect;
|
|
428
488
|
}
|
|
429
489
|
else {
|
|
430
|
-
const
|
|
431
|
-
const locatorRect = await scrollIntoViewIfNeeded(locator);
|
|
490
|
+
const locatorRect = await locator.boundingBox();
|
|
432
491
|
if (isInsideAutoZoom() && locatorRect) {
|
|
433
|
-
const
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
492
|
+
const focusStart = Date.now();
|
|
493
|
+
const zoomDur = getZoomDuration() ?? 0;
|
|
494
|
+
if (zoomDur > 0)
|
|
495
|
+
await sleep(zoomDur);
|
|
496
|
+
const focusEnd = Date.now();
|
|
497
|
+
innerEvents.push({
|
|
498
|
+
type: 'focusChange',
|
|
499
|
+
startMs: focusStart,
|
|
500
|
+
endMs: focusEnd,
|
|
501
|
+
x: locatorRect.x + locatorRect.width / 2,
|
|
502
|
+
y: locatorRect.y + locatorRect.height / 2,
|
|
503
|
+
easing: getZoomEasing() ?? 'ease-in-out',
|
|
504
|
+
focusOnly: true,
|
|
445
505
|
elementRect: locatorRect,
|
|
446
|
-
eventType: 'fill',
|
|
447
506
|
});
|
|
448
507
|
}
|
|
449
|
-
if (isFirstAutoZoomEvent) {
|
|
450
|
-
const postDelay = getPostZoomInOutDelay() ?? 0;
|
|
451
|
-
if (postDelay > 0)
|
|
452
|
-
await sleep(postDelay);
|
|
453
|
-
}
|
|
454
508
|
const targetPosition = locatorRect
|
|
455
509
|
? {
|
|
456
510
|
x: locatorRect.width / 2,
|
|
@@ -463,18 +517,20 @@ async function performSimpleAction(locator, doAction, options, subType, clickOpt
|
|
|
463
517
|
...(targetPosition ? { position: targetPosition } : {}),
|
|
464
518
|
});
|
|
465
519
|
const endTime = Date.now();
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
520
|
+
elementRect = locatorRect ?? undefined;
|
|
521
|
+
if (recordMousePress) {
|
|
522
|
+
const midTime = (startTime + endTime) / 2;
|
|
523
|
+
innerEvents.push({
|
|
524
|
+
type: 'mouseDown',
|
|
525
|
+
startMs: startTime,
|
|
526
|
+
endMs: midTime,
|
|
527
|
+
});
|
|
528
|
+
innerEvents.push({
|
|
529
|
+
type: 'mouseUp',
|
|
530
|
+
startMs: midTime,
|
|
531
|
+
endMs: endTime,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
478
534
|
}
|
|
479
535
|
const simpleWaitStart = Date.now();
|
|
480
536
|
await sleep(POST_ACTION_SLEEP);
|
|
@@ -488,9 +544,9 @@ async function performSimpleAction(locator, doAction, options, subType, clickOpt
|
|
|
488
544
|
activeClickRecorder.addInput(subType, elementRect, innerEvents);
|
|
489
545
|
}
|
|
490
546
|
}
|
|
491
|
-
async function recordedClick(locator, doClick, clickOptions, position, moveDuration, moveSpeed, beforeClickPause = CLICK_DURATION_MS / 2, moveEasing = 'ease-in-out', postClickPause = CLICK_DURATION_MS / 2, postClickMove) {
|
|
547
|
+
async function recordedClick(locator, doClick, clickOptions, autoZoomOptions, position, moveDuration, moveSpeed, beforeClickPause = CLICK_DURATION_MS / 2, moveEasing = 'ease-in-out', postClickPause = CLICK_DURATION_MS / 2, postClickMove) {
|
|
492
548
|
await sleep(PRE_ACTION_SLEEP);
|
|
493
|
-
const result = await performClickActions(locator, doClick, clickOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
549
|
+
const result = await performClickActions(locator, doClick, clickOptions, autoZoomOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
494
550
|
const clickWaitStart = Date.now();
|
|
495
551
|
await sleep(POST_ACTION_SLEEP);
|
|
496
552
|
const clickWaitEnd = Date.now();
|
|
@@ -530,12 +586,22 @@ export function instrumentLocator(locator) {
|
|
|
530
586
|
instrumented.add(locator);
|
|
531
587
|
const originalClick = locator.click.bind(locator);
|
|
532
588
|
locator.click = async (options) => {
|
|
533
|
-
const { moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove, position, steps: _steps, ...clickOptions } = options ?? {};
|
|
589
|
+
const { moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove, autoZoomOptions, position, steps: _steps, ...clickOptions } = options ?? {};
|
|
590
|
+
if (isInsideHide()) {
|
|
591
|
+
return originalClick({
|
|
592
|
+
...clickOptions,
|
|
593
|
+
...(position !== undefined && { position }),
|
|
594
|
+
});
|
|
595
|
+
}
|
|
534
596
|
assertDurationOrSpeed(moveDuration, moveSpeed, 'click move');
|
|
535
|
-
return recordedClick(locator, (options) => originalClick(options), clickOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
597
|
+
return recordedClick(locator, (options) => originalClick(options), clickOptions, autoZoomOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
536
598
|
};
|
|
537
599
|
const originalPressSequentially = locator.pressSequentially.bind(locator);
|
|
538
600
|
locator.pressSequentially = async (text, options) => {
|
|
601
|
+
const { click: _click, autoZoomOptions, hideMouse: _hideMouse, position: _position, ...pressOptions } = options ?? {};
|
|
602
|
+
if (isInsideHide()) {
|
|
603
|
+
return originalPressSequentially(text, pressOptions);
|
|
604
|
+
}
|
|
539
605
|
await sleep(PRE_ACTION_SLEEP);
|
|
540
606
|
const innerEvents = [];
|
|
541
607
|
let elementRect;
|
|
@@ -544,37 +610,24 @@ export function instrumentLocator(locator) {
|
|
|
544
610
|
const clickOpt = options.click;
|
|
545
611
|
const position = options.position;
|
|
546
612
|
const { moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove, ...clickOptions } = clickOpt;
|
|
547
|
-
const clickActionResult = await performClickActions(locator, (options) => originalClick(options), clickOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
613
|
+
const clickActionResult = await performClickActions(locator, (options) => originalClick(options), clickOptions, options.autoZoomOptions, position, moveDuration, moveSpeed, beforeClickPause, moveEasing, postClickPause, postClickMove);
|
|
548
614
|
innerEvents.push(...(clickActionResult?.innerEvents ?? []));
|
|
549
615
|
elementRect = clickActionResult?.elementRect;
|
|
550
616
|
}
|
|
551
617
|
else {
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
// If inside autoZoom: await zoom duration for camera pan, then update zoom tracker
|
|
555
|
-
if (isInsideAutoZoom() && locatorRect) {
|
|
556
|
-
const targetX = locatorRect.x + locatorRect.width / 2;
|
|
557
|
-
const targetY = locatorRect.y + locatorRect.height / 2;
|
|
558
|
-
const lastLoc = getLastZoomLocation();
|
|
559
|
-
if (lastLoc !== null) {
|
|
560
|
-
const zoomDur = getZoomDuration() ?? 0;
|
|
561
|
-
if (zoomDur > 0) {
|
|
562
|
-
await sleep(zoomDur);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
setLastZoomLocation({
|
|
566
|
-
x: targetX,
|
|
567
|
-
y: targetY,
|
|
568
|
-
elementRect: locatorRect,
|
|
569
|
-
eventType: 'fill',
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
if (isFirstAutoZoomEvent) {
|
|
618
|
+
const { locatorRect, isFirstAutoZoomInteraction } = await prepareAutoZoomForLocator(locator, 'fill', options?.autoZoomOptions);
|
|
619
|
+
if (isFirstAutoZoomInteraction) {
|
|
573
620
|
const postDelay = getPostZoomInOutDelay() ?? 0;
|
|
574
621
|
if (postDelay > 0)
|
|
575
622
|
await sleep(postDelay);
|
|
576
623
|
}
|
|
577
624
|
elementRect = locatorRect;
|
|
625
|
+
if (isInsideAutoZoom() && locatorRect) {
|
|
626
|
+
const zoomDur = getZoomDuration() ?? 0;
|
|
627
|
+
if (zoomDur > 0) {
|
|
628
|
+
await sleep(zoomDur);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
578
631
|
}
|
|
579
632
|
// Hide cursor while typing (will be shown again on next mouse move)
|
|
580
633
|
const page = locator.page();
|
|
@@ -591,62 +644,192 @@ export function instrumentLocator(locator) {
|
|
|
591
644
|
});
|
|
592
645
|
}
|
|
593
646
|
}
|
|
594
|
-
// Strip the click and hideMouse options before forwarding to the original Playwright method
|
|
595
|
-
const { click: _click, hideMouse: _hideMouse, ...pressOptions } = options ?? {};
|
|
596
|
-
const typingStart = Date.now();
|
|
597
647
|
await originalPressSequentially(text, pressOptions);
|
|
598
|
-
const typingEnd = Date.now();
|
|
599
648
|
const pressWaitStart = Date.now();
|
|
600
649
|
await sleep(POST_ACTION_SLEEP);
|
|
601
650
|
const pressWaitEnd = Date.now();
|
|
602
651
|
if (activeClickRecorder) {
|
|
603
|
-
|
|
604
|
-
// fallback span when no other inner events (click, mouseHide) were collected.
|
|
605
|
-
const eventsToRecord = innerEvents.length > 0
|
|
606
|
-
? innerEvents
|
|
607
|
-
: [
|
|
608
|
-
{ type: 'mouseDown', startMs: typingStart, endMs: typingStart },
|
|
609
|
-
{ type: 'mouseUp', startMs: typingStart, endMs: typingEnd },
|
|
610
|
-
];
|
|
611
|
-
eventsToRecord.push({
|
|
652
|
+
innerEvents.push({
|
|
612
653
|
type: 'mouseWait',
|
|
613
654
|
startMs: pressWaitStart,
|
|
614
655
|
endMs: pressWaitEnd,
|
|
615
656
|
});
|
|
616
|
-
activeClickRecorder.addInput('pressSequentially', elementRect,
|
|
657
|
+
activeClickRecorder.addInput('pressSequentially', elementRect, innerEvents);
|
|
617
658
|
}
|
|
618
659
|
};
|
|
660
|
+
const originalFill = locator.fill.bind(locator);
|
|
619
661
|
locator.fill = async (value, options) => {
|
|
620
|
-
|
|
662
|
+
if (isInsideHide()) {
|
|
663
|
+
const { duration: _duration, click: _click, position: _position, hideMouse: _hideMouse, autoZoomOptions: _autoZoomOptions, ...fillOptions } = options ?? {};
|
|
664
|
+
return originalFill(value, fillOptions);
|
|
665
|
+
}
|
|
666
|
+
if (options?.click !== undefined) {
|
|
667
|
+
await sleep(PRE_ACTION_SLEEP);
|
|
668
|
+
const clickActionResult = await performClickActions(locator, (clickOptions) => originalClick(clickOptions), {}, options.autoZoomOptions, options.position, options.click.moveDuration, options.click.moveSpeed, options.click.beforeClickPause, options.click.moveEasing, options.click.postClickPause, options.click.postClickMove);
|
|
669
|
+
const innerEvents = [...(clickActionResult?.innerEvents ?? [])];
|
|
670
|
+
const elementRect = clickActionResult?.elementRect;
|
|
671
|
+
const page = locator.page();
|
|
672
|
+
if (options.hideMouse === true) {
|
|
673
|
+
const cursorVisible = mouseVisibilities.get(page) ?? true;
|
|
674
|
+
if (cursorVisible) {
|
|
675
|
+
mouseVisibilities.set(page, false);
|
|
676
|
+
const hideMs = Date.now();
|
|
677
|
+
innerEvents.push({
|
|
678
|
+
type: 'mouseHide',
|
|
679
|
+
startMs: hideMs,
|
|
680
|
+
endMs: hideMs,
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
await locator.evaluate((element) => {
|
|
685
|
+
if (element instanceof HTMLInputElement ||
|
|
686
|
+
element instanceof HTMLTextAreaElement) {
|
|
687
|
+
element.focus();
|
|
688
|
+
element.select();
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (element instanceof HTMLElement && element.isContentEditable) {
|
|
692
|
+
element.focus();
|
|
693
|
+
const selection = element.ownerDocument.getSelection();
|
|
694
|
+
if (!selection)
|
|
695
|
+
return;
|
|
696
|
+
const range = element.ownerDocument.createRange();
|
|
697
|
+
range.selectNodeContents(element);
|
|
698
|
+
selection.removeAllRanges();
|
|
699
|
+
selection.addRange(range);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
const duration = options.duration ?? 1000;
|
|
703
|
+
const delay = value.length > 0 ? duration / value.length : 0;
|
|
704
|
+
await page.keyboard.type(value, { delay });
|
|
705
|
+
const fillWaitStart = Date.now();
|
|
706
|
+
await sleep(POST_ACTION_SLEEP);
|
|
707
|
+
const fillWaitEnd = Date.now();
|
|
708
|
+
innerEvents.push({
|
|
709
|
+
type: 'mouseWait',
|
|
710
|
+
startMs: fillWaitStart,
|
|
711
|
+
endMs: fillWaitEnd,
|
|
712
|
+
});
|
|
713
|
+
if (activeClickRecorder) {
|
|
714
|
+
activeClickRecorder.addInput('pressSequentially', elementRect, innerEvents);
|
|
715
|
+
}
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
await sleep(PRE_ACTION_SLEEP);
|
|
719
|
+
const page = locator.page();
|
|
720
|
+
const innerEvents = [];
|
|
721
|
+
const fillOptions = options ?? {};
|
|
722
|
+
const locatorRect = await locator.boundingBox();
|
|
723
|
+
let elementRect = locatorRect ?? undefined;
|
|
724
|
+
if (fillOptions.hideMouse === true) {
|
|
725
|
+
const cursorVisible = mouseVisibilities.get(page) ?? true;
|
|
726
|
+
if (cursorVisible) {
|
|
727
|
+
mouseVisibilities.set(page, false);
|
|
728
|
+
const hideMs = Date.now();
|
|
729
|
+
innerEvents.push({
|
|
730
|
+
type: 'mouseHide',
|
|
731
|
+
startMs: hideMs,
|
|
732
|
+
endMs: hideMs,
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (isInsideAutoZoom() && locatorRect) {
|
|
737
|
+
const correctedScrollResult = await new ZoomScrollHandler(options?.autoZoomOptions).scroll(locator);
|
|
738
|
+
const correctedRect = correctedScrollResult.locatorRect ?? locatorRect;
|
|
739
|
+
const focusStart = Date.now();
|
|
740
|
+
await locator.evaluate((element) => {
|
|
741
|
+
if (element instanceof HTMLInputElement ||
|
|
742
|
+
element instanceof HTMLTextAreaElement) {
|
|
743
|
+
element.focus();
|
|
744
|
+
element.select();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
if (element instanceof HTMLElement && element.isContentEditable) {
|
|
748
|
+
element.focus();
|
|
749
|
+
const selection = element.ownerDocument.getSelection();
|
|
750
|
+
if (!selection)
|
|
751
|
+
return;
|
|
752
|
+
const range = element.ownerDocument.createRange();
|
|
753
|
+
range.selectNodeContents(element);
|
|
754
|
+
selection.removeAllRanges();
|
|
755
|
+
selection.addRange(range);
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
const focusEnd = Date.now();
|
|
759
|
+
innerEvents.push({
|
|
760
|
+
type: 'focusChange',
|
|
761
|
+
startMs: focusStart,
|
|
762
|
+
endMs: focusEnd,
|
|
763
|
+
x: correctedRect.x + correctedRect.width / 2,
|
|
764
|
+
y: correctedRect.y + correctedRect.height / 2,
|
|
765
|
+
easing: getZoomEasing() ?? 'ease-in-out',
|
|
766
|
+
focusOnly: true,
|
|
767
|
+
elementRect: correctedRect,
|
|
768
|
+
});
|
|
769
|
+
elementRect = correctedRect;
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
await locator.evaluate((element) => {
|
|
773
|
+
if (element instanceof HTMLInputElement ||
|
|
774
|
+
element instanceof HTMLTextAreaElement) {
|
|
775
|
+
element.focus();
|
|
776
|
+
element.select();
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
if (element instanceof HTMLElement && element.isContentEditable) {
|
|
780
|
+
element.focus();
|
|
781
|
+
const selection = element.ownerDocument.getSelection();
|
|
782
|
+
if (!selection)
|
|
783
|
+
return;
|
|
784
|
+
const range = element.ownerDocument.createRange();
|
|
785
|
+
range.selectNodeContents(element);
|
|
786
|
+
selection.removeAllRanges();
|
|
787
|
+
selection.addRange(range);
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
const duration = fillOptions.duration ?? 1000;
|
|
621
792
|
const delay = value.length > 0 ? duration / value.length : 0;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
793
|
+
await page.keyboard.type(value, { delay });
|
|
794
|
+
const fillWaitStart = Date.now();
|
|
795
|
+
await sleep(POST_ACTION_SLEEP);
|
|
796
|
+
const fillWaitEnd = Date.now();
|
|
797
|
+
innerEvents.push({
|
|
798
|
+
type: 'mouseWait',
|
|
799
|
+
startMs: fillWaitStart,
|
|
800
|
+
endMs: fillWaitEnd,
|
|
801
|
+
});
|
|
802
|
+
if (activeClickRecorder &&
|
|
803
|
+
(isInsideAutoZoom() || fillOptions.hideMouse === true)) {
|
|
804
|
+
activeClickRecorder.addInput('pressSequentially', elementRect, innerEvents);
|
|
805
|
+
}
|
|
806
|
+
return;
|
|
632
807
|
};
|
|
633
808
|
const originalTap = locator.tap.bind(locator);
|
|
634
809
|
locator.tap = async (options) => {
|
|
635
810
|
const clickOpt = options?.click;
|
|
636
|
-
const { click: _click, position, ...tapOpts } = options ?? {};
|
|
637
|
-
return performSimpleAction(locator, (options) => originalTap(options), tapOpts, 'tap', clickOpt, position);
|
|
811
|
+
const { click: _click, position, autoZoomOptions, ...tapOpts } = options ?? {};
|
|
812
|
+
return performSimpleAction(locator, (options) => originalTap(options), tapOpts, 'tap', clickOpt, autoZoomOptions, position);
|
|
638
813
|
};
|
|
639
814
|
const originalCheck = locator.check.bind(locator);
|
|
640
815
|
locator.check = async (options) => {
|
|
641
816
|
const clickOpt = options?.click;
|
|
642
|
-
const
|
|
643
|
-
|
|
817
|
+
const position = options?.position;
|
|
818
|
+
const { click: _click, autoZoomOptions, ...checkOpts } = options ?? {};
|
|
819
|
+
if (isInsideHide()) {
|
|
820
|
+
return originalCheck(checkOpts);
|
|
821
|
+
}
|
|
822
|
+
return performSimpleAction(locator, (options) => originalCheck(options), checkOpts, 'check', clickOpt, autoZoomOptions, position, false);
|
|
644
823
|
};
|
|
645
824
|
const originalUncheck = locator.uncheck.bind(locator);
|
|
646
825
|
locator.uncheck = async (options) => {
|
|
647
826
|
const clickOpt = options?.click;
|
|
648
|
-
const
|
|
649
|
-
|
|
827
|
+
const position = options?.position;
|
|
828
|
+
const { click: _click, autoZoomOptions, ...uncheckOpts } = options ?? {};
|
|
829
|
+
if (isInsideHide()) {
|
|
830
|
+
return originalUncheck(uncheckOpts);
|
|
831
|
+
}
|
|
832
|
+
return performSimpleAction(locator, (options) => originalUncheck(options), uncheckOpts, 'uncheck', clickOpt, autoZoomOptions, position, false);
|
|
650
833
|
};
|
|
651
834
|
locator.setChecked = async (checked, options) => {
|
|
652
835
|
if (checked) {
|
|
@@ -659,11 +842,14 @@ export function instrumentLocator(locator) {
|
|
|
659
842
|
const originalSelectOption = locator.selectOption.bind(locator);
|
|
660
843
|
locator.selectOption = async (values, options) => {
|
|
661
844
|
const clickOpt = options?.click;
|
|
662
|
-
const { click: _click, position, ...selectOpts } = options ?? {};
|
|
845
|
+
const { click: _click, position, autoZoomOptions, ...selectOpts } = options ?? {};
|
|
846
|
+
if (isInsideHide()) {
|
|
847
|
+
return originalSelectOption(values, selectOpts);
|
|
848
|
+
}
|
|
663
849
|
let result = [];
|
|
664
850
|
await performSimpleAction(locator, (options) => originalSelectOption(values, options).then((res) => {
|
|
665
851
|
result = res;
|
|
666
|
-
}), selectOpts, 'select', clickOpt, position);
|
|
852
|
+
}), selectOpts, 'select', clickOpt, autoZoomOptions, position, false);
|
|
667
853
|
return result;
|
|
668
854
|
};
|
|
669
855
|
const originalHover = locator.hover.bind(locator);
|
|
@@ -673,8 +859,8 @@ export function instrumentLocator(locator) {
|
|
|
673
859
|
const page = locator.page();
|
|
674
860
|
const mouseMoveInternal = originalMouseMoves.get(page) ?? page.mouse.move.bind(page.mouse);
|
|
675
861
|
const moveStartTime = Date.now();
|
|
676
|
-
const
|
|
677
|
-
const
|
|
862
|
+
const scrollResult = await new ZoomScrollHandler().scroll(locator);
|
|
863
|
+
const { locatorRect } = scrollResult;
|
|
678
864
|
const innerEvents = [];
|
|
679
865
|
const targetPos = position ??
|
|
680
866
|
(locatorRect
|
|
@@ -689,8 +875,7 @@ export function instrumentLocator(locator) {
|
|
|
689
875
|
defaultDuration: 1000,
|
|
690
876
|
context: 'hover move',
|
|
691
877
|
});
|
|
692
|
-
|
|
693
|
-
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, targetX, targetY, effectiveDuration, moveEasing, moveStartTime, locatorRect));
|
|
878
|
+
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, targetX, targetY, resolveMoveDuration(scrollResult, resolvedDuration), moveEasing, resolveMoveStartMs(scrollResult, moveStartTime), locatorRect));
|
|
694
879
|
}
|
|
695
880
|
const waitStartMs = Date.now();
|
|
696
881
|
await originalHover({
|
|
@@ -717,8 +902,8 @@ export function instrumentLocator(locator) {
|
|
|
717
902
|
const page = locator.page();
|
|
718
903
|
const mouseMoveInternal = originalMouseMoves.get(page) ?? page.mouse.move.bind(page.mouse);
|
|
719
904
|
const moveStartTime = Date.now();
|
|
720
|
-
const
|
|
721
|
-
const
|
|
905
|
+
const scrollResult = await new ZoomScrollHandler().scroll(locator);
|
|
906
|
+
const { locatorRect } = scrollResult;
|
|
722
907
|
const innerEvents = [];
|
|
723
908
|
const targetPos = locatorRect
|
|
724
909
|
? { x: locatorRect.width / 2, y: locatorRect.height / 2 }
|
|
@@ -732,8 +917,7 @@ export function instrumentLocator(locator) {
|
|
|
732
917
|
defaultDuration: 1000,
|
|
733
918
|
context: 'selectText move',
|
|
734
919
|
});
|
|
735
|
-
|
|
736
|
-
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, targetX, targetY, effectiveDuration, moveEasing, moveStartTime, locatorRect));
|
|
920
|
+
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, targetX, targetY, resolveMoveDuration(scrollResult, resolvedDuration), moveEasing, resolveMoveStartMs(scrollResult, moveStartTime), locatorRect));
|
|
737
921
|
}
|
|
738
922
|
await sleep(beforeClickPause);
|
|
739
923
|
await originalSelectText(selectOpts);
|
|
@@ -772,8 +956,8 @@ export function instrumentLocator(locator) {
|
|
|
772
956
|
const page = locator.page();
|
|
773
957
|
const mouseMoveInternal = originalMouseMoves.get(page) ?? page.mouse.move.bind(page.mouse);
|
|
774
958
|
const moveStartTime = Date.now();
|
|
775
|
-
const
|
|
776
|
-
const
|
|
959
|
+
const scrollResult = await new ZoomScrollHandler().scroll(locator);
|
|
960
|
+
const { locatorRect: sourceRect } = scrollResult;
|
|
777
961
|
const targetBb = await target.boundingBox();
|
|
778
962
|
const targetRect = targetBb
|
|
779
963
|
? {
|
|
@@ -802,8 +986,7 @@ export function instrumentLocator(locator) {
|
|
|
802
986
|
defaultDuration: 1000,
|
|
803
987
|
context: 'dragTo move',
|
|
804
988
|
});
|
|
805
|
-
|
|
806
|
-
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, toX, toY, effectiveDuration, moveEasing, moveStartTime, sourceRect));
|
|
989
|
+
innerEvents.push(await animateMouseMove(page, mouseMoveInternal, toX, toY, resolveMoveDuration(scrollResult, resolvedDuration), moveEasing, resolveMoveStartMs(scrollResult, moveStartTime), sourceRect));
|
|
807
990
|
}
|
|
808
991
|
// 2. preDragPause + mouseDown
|
|
809
992
|
await sleep(preDragPause);
|
|
@@ -945,15 +1128,14 @@ export async function instrumentPage(page) {
|
|
|
945
1128
|
activeClickRecorder.addInput('mouseShow', undefined, [showEvent]);
|
|
946
1129
|
}
|
|
947
1130
|
const moveEvent = {
|
|
948
|
-
type: '
|
|
1131
|
+
type: 'focusChange',
|
|
949
1132
|
startMs,
|
|
950
1133
|
endMs,
|
|
951
|
-
duration,
|
|
952
1134
|
x,
|
|
953
1135
|
y,
|
|
954
1136
|
...(duration > 0 ? { easing } : {}),
|
|
955
1137
|
};
|
|
956
|
-
activeClickRecorder.addInput('
|
|
1138
|
+
activeClickRecorder.addInput('focusChange', undefined, [moveEvent]);
|
|
957
1139
|
}
|
|
958
1140
|
};
|
|
959
1141
|
mouseVisibilities.set(page, true);
|