ftmocks-utils 1.4.4 → 1.4.6
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 +1 -1
- package/src/event-run-utils.js +209 -58
- package/src/index.js +4 -0
package/package.json
CHANGED
package/src/event-run-utils.js
CHANGED
|
@@ -8,18 +8,21 @@ const getLocator = async (page, event) => {
|
|
|
8
8
|
if (event && event.target && typeof page !== "undefined" && page.locator) {
|
|
9
9
|
let locator = null;
|
|
10
10
|
while (!locator) {
|
|
11
|
-
const selector =
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const selector =
|
|
12
|
+
event.target.startsWith("/") || event.target.startsWith("(/")
|
|
13
|
+
? `xpath=${event.target}`
|
|
14
|
+
: event.target;
|
|
14
15
|
try {
|
|
15
16
|
const count = await page.locator(selector).count();
|
|
16
17
|
if (count === 1) {
|
|
17
18
|
locator = selector;
|
|
18
19
|
} else {
|
|
19
20
|
for (let i = 0; i < event.selectors.length; i++) {
|
|
20
|
-
const selector =
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const selector =
|
|
22
|
+
event.selectors[i].value.startsWith("/") ||
|
|
23
|
+
event.selectors[i].value.startsWith("(/")
|
|
24
|
+
? `xpath=${event.selectors[i].value}`
|
|
25
|
+
: event.selectors[i].value;
|
|
23
26
|
const count = await page.locator(selector).count();
|
|
24
27
|
if (count === 1) {
|
|
25
28
|
locator = selector;
|
|
@@ -37,51 +40,157 @@ const getLocator = async (page, event) => {
|
|
|
37
40
|
return event.target;
|
|
38
41
|
};
|
|
39
42
|
|
|
40
|
-
const
|
|
43
|
+
const healSelector = async (page, event, selector, position) => {
|
|
44
|
+
console.log("➡ Healing selector for event", event);
|
|
45
|
+
let locator = selector;
|
|
46
|
+
if (locator.startsWith("/") || locator.startsWith("(/")) {
|
|
47
|
+
locator = `xpath=${locator}`;
|
|
48
|
+
try {
|
|
49
|
+
// INSERT_YOUR_CODE
|
|
50
|
+
// Get all elements that match the given locator
|
|
51
|
+
const elements = await page.locator(locator).elementHandles();
|
|
52
|
+
if (elements.length > 1) {
|
|
53
|
+
let index = 0;
|
|
54
|
+
for (let i = 0; i < elements.length; i++) {
|
|
55
|
+
const element = elements[i];
|
|
56
|
+
const currPosition = await element.boundingBox();
|
|
57
|
+
if (
|
|
58
|
+
currPosition.x >= position.x &&
|
|
59
|
+
currPosition.y >= position.y &&
|
|
60
|
+
currPosition.x + currPosition.width <=
|
|
61
|
+
position.x + position.width &&
|
|
62
|
+
currPosition.y + currPosition.height <= position.y + position.height
|
|
63
|
+
) {
|
|
64
|
+
index = i;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
value: `(${selector})[${index + 1}]`,
|
|
70
|
+
count: 1,
|
|
71
|
+
};
|
|
72
|
+
} else {
|
|
73
|
+
return {
|
|
74
|
+
value: selector,
|
|
75
|
+
count: elements.length,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error("Error getting locator", error, selector);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
value: selector,
|
|
84
|
+
count: 1,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const getSelectorPosition = async (page, selector) => {
|
|
89
|
+
const element = await page.locator(selector).elementHandle();
|
|
90
|
+
const position = await element.boundingBox();
|
|
91
|
+
console.log("position", position);
|
|
92
|
+
return position;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const runEvent = async ({
|
|
96
|
+
page,
|
|
97
|
+
event,
|
|
98
|
+
delay = 0,
|
|
99
|
+
screenshots = false,
|
|
100
|
+
screenshotsDir = null,
|
|
101
|
+
healSelectors = false,
|
|
102
|
+
}) => {
|
|
41
103
|
try {
|
|
42
104
|
console.log("➡ Running event", event);
|
|
105
|
+
const beforeEvent = async () => {
|
|
106
|
+
await page.waitForTimeout(delay);
|
|
107
|
+
if (screenshots) {
|
|
108
|
+
const locator = await getLocator(page, event);
|
|
109
|
+
const position = await getSelectorPosition(page, locator);
|
|
110
|
+
event.screenshotInfo = {
|
|
111
|
+
name: `${event.id}.png`,
|
|
112
|
+
position,
|
|
113
|
+
};
|
|
114
|
+
await page.screenshot({
|
|
115
|
+
path: path.join(screenshotsDir, `${event.id}.png`),
|
|
116
|
+
fullPage: false,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (healSelectors) {
|
|
120
|
+
const locator = await getLocator(page, event);
|
|
121
|
+
const position = await getSelectorPosition(page, locator);
|
|
122
|
+
const healedTarget = await healSelector(
|
|
123
|
+
page,
|
|
124
|
+
event,
|
|
125
|
+
event.target,
|
|
126
|
+
position
|
|
127
|
+
);
|
|
128
|
+
event.target = healedTarget.value;
|
|
129
|
+
const selectors = [];
|
|
130
|
+
const laterToBeAdded = [];
|
|
131
|
+
for (let i = 0; i < event.selectors.length; i++) {
|
|
132
|
+
const healedSelector = await healSelector(
|
|
133
|
+
page,
|
|
134
|
+
event,
|
|
135
|
+
event.selectors[i].value,
|
|
136
|
+
position
|
|
137
|
+
);
|
|
138
|
+
event.selectors[i].value = healedSelector.value;
|
|
139
|
+
if (healedSelector.count !== 1) {
|
|
140
|
+
laterToBeAdded.push(event.selectors[i]);
|
|
141
|
+
} else {
|
|
142
|
+
selectors.push(event.selectors[i]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
selectors.push(...laterToBeAdded);
|
|
146
|
+
if (healedTarget.count !== 1) {
|
|
147
|
+
event.target = selectors[0].value;
|
|
148
|
+
}
|
|
149
|
+
event.selectors = selectors;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
43
152
|
switch (event.type) {
|
|
44
153
|
case "url":
|
|
45
154
|
await page.goto(event.value);
|
|
46
155
|
break;
|
|
47
156
|
case "click":
|
|
48
|
-
await
|
|
157
|
+
await beforeEvent();
|
|
49
158
|
await page.click(await getLocator(page, event));
|
|
50
159
|
break;
|
|
51
160
|
case "input":
|
|
52
|
-
await
|
|
161
|
+
await beforeEvent();
|
|
53
162
|
await page.fill(await getLocator(page, event), event.value);
|
|
54
163
|
break;
|
|
55
164
|
case "keypress":
|
|
56
|
-
await
|
|
165
|
+
await beforeEvent();
|
|
57
166
|
await page.keyboard.press(await getLocator(page, event), event.key);
|
|
58
167
|
break;
|
|
59
168
|
case "change":
|
|
60
|
-
await
|
|
169
|
+
await beforeEvent();
|
|
61
170
|
await page.select(await getLocator(page, event), event.value);
|
|
62
171
|
break;
|
|
63
172
|
case "url":
|
|
64
|
-
await
|
|
173
|
+
await beforeEvent();
|
|
65
174
|
await page.goto(await getLocator(page, event), event.value);
|
|
66
175
|
break;
|
|
67
176
|
case "dblclick":
|
|
68
|
-
await
|
|
177
|
+
await beforeEvent();
|
|
69
178
|
await page.dblclick(await getLocator(page, event));
|
|
70
179
|
break;
|
|
71
180
|
case "contextmenu":
|
|
72
|
-
await
|
|
181
|
+
await beforeEvent();
|
|
73
182
|
await page.contextmenu(await getLocator(page, event));
|
|
74
183
|
break;
|
|
75
184
|
case "hover":
|
|
76
|
-
await
|
|
185
|
+
await beforeEvent();
|
|
77
186
|
await page.hover(await getLocator(page, event));
|
|
78
187
|
break;
|
|
79
188
|
case "keydown":
|
|
80
|
-
await
|
|
189
|
+
await beforeEvent();
|
|
81
190
|
await page.keyboard.down(await getLocator(page, event), event.key);
|
|
82
191
|
break;
|
|
83
192
|
case "keyup":
|
|
84
|
-
await
|
|
193
|
+
await beforeEvent();
|
|
85
194
|
await page.keyboard.up(await getLocator(page, event), event.key);
|
|
86
195
|
break;
|
|
87
196
|
default:
|
|
@@ -129,9 +238,23 @@ const isValidEvent = (event) => {
|
|
|
129
238
|
return false;
|
|
130
239
|
};
|
|
131
240
|
|
|
132
|
-
const runEvents = async (
|
|
241
|
+
const runEvents = async ({
|
|
242
|
+
page,
|
|
243
|
+
events,
|
|
244
|
+
delay = 1000,
|
|
245
|
+
screenshots = false,
|
|
246
|
+
screenshotsDir = null,
|
|
247
|
+
healSelectors = false,
|
|
248
|
+
}) => {
|
|
133
249
|
for (const event of events) {
|
|
134
|
-
await runEvent(
|
|
250
|
+
await runEvent({
|
|
251
|
+
page,
|
|
252
|
+
event,
|
|
253
|
+
delay,
|
|
254
|
+
screenshots,
|
|
255
|
+
screenshotsDir,
|
|
256
|
+
healSelectors,
|
|
257
|
+
});
|
|
135
258
|
}
|
|
136
259
|
};
|
|
137
260
|
|
|
@@ -142,14 +265,12 @@ const runEventsForTest = async (page, ftmocksConifg, testName) => {
|
|
|
142
265
|
`_events.json`
|
|
143
266
|
);
|
|
144
267
|
const events = JSON.parse(fs.readFileSync(eventsFile, "utf8"));
|
|
145
|
-
await runEvents(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
console.log("position", position);
|
|
152
|
-
return position;
|
|
268
|
+
await runEvents({
|
|
269
|
+
page,
|
|
270
|
+
events,
|
|
271
|
+
delay: ftmocksConifg.delay || 1000,
|
|
272
|
+
screenshots: false,
|
|
273
|
+
});
|
|
153
274
|
};
|
|
154
275
|
|
|
155
276
|
const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
@@ -168,10 +289,10 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
|
168
289
|
console.log("➡ No more events to run!");
|
|
169
290
|
return false;
|
|
170
291
|
}
|
|
171
|
-
let result = await runEvent(page, events[currentEventIndex]);
|
|
292
|
+
let result = await runEvent({ page, event: events[currentEventIndex] });
|
|
172
293
|
while (result === "Unsupported event type") {
|
|
173
294
|
currentEventIndex = currentEventIndex + 1;
|
|
174
|
-
result = await runEvent(page, events[currentEventIndex]);
|
|
295
|
+
result = await runEvent({ page, event: events[currentEventIndex] });
|
|
175
296
|
}
|
|
176
297
|
currentEventIndex = currentEventIndex + 1;
|
|
177
298
|
return true;
|
|
@@ -206,7 +327,7 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
|
206
327
|
});
|
|
207
328
|
});
|
|
208
329
|
|
|
209
|
-
await runEvent(page, events[0]);
|
|
330
|
+
await runEvent({ page, event: events[0] });
|
|
210
331
|
};
|
|
211
332
|
|
|
212
333
|
const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
@@ -375,6 +496,15 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
375
496
|
}
|
|
376
497
|
};
|
|
377
498
|
|
|
499
|
+
const showNextEvent = async () => {
|
|
500
|
+
currentEventInfo = await window.getNextEvent();
|
|
501
|
+
if (currentEventInfo) {
|
|
502
|
+
showPopover(currentEventInfo);
|
|
503
|
+
} else {
|
|
504
|
+
hidePopover();
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
378
508
|
const matchElement = (event, currentEventInfo) => {
|
|
379
509
|
const inBoudingBox =
|
|
380
510
|
currentEventInfo?.position?.x <= event.clientX &&
|
|
@@ -400,13 +530,7 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
400
530
|
matchElement(event, currentEventInfo)
|
|
401
531
|
) {
|
|
402
532
|
console.log("➡ Click event triggered!", event);
|
|
403
|
-
|
|
404
|
-
console.log("➡ Next event", currentEventInfo);
|
|
405
|
-
if (currentEventInfo) {
|
|
406
|
-
showPopover(currentEventInfo);
|
|
407
|
-
} else {
|
|
408
|
-
hidePopover();
|
|
409
|
-
}
|
|
533
|
+
showNextEvent();
|
|
410
534
|
}
|
|
411
535
|
});
|
|
412
536
|
window.addEventListener("dblclick", async (event) => {
|
|
@@ -414,12 +538,7 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
414
538
|
currentEventInfo?.event?.type === "dblclick" &&
|
|
415
539
|
matchElement(event, currentEventInfo)
|
|
416
540
|
) {
|
|
417
|
-
|
|
418
|
-
if (currentEventInfo) {
|
|
419
|
-
showPopover(currentEventInfo);
|
|
420
|
-
} else {
|
|
421
|
-
hidePopover();
|
|
422
|
-
}
|
|
541
|
+
showNextEvent();
|
|
423
542
|
}
|
|
424
543
|
});
|
|
425
544
|
window.addEventListener("contextmenu", async (event) => {
|
|
@@ -427,12 +546,7 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
427
546
|
currentEventInfo?.event?.type === "contextmenu" &&
|
|
428
547
|
matchElement(event, currentEventInfo)
|
|
429
548
|
) {
|
|
430
|
-
|
|
431
|
-
if (currentEventInfo) {
|
|
432
|
-
showPopover(currentEventInfo);
|
|
433
|
-
} else {
|
|
434
|
-
hidePopover();
|
|
435
|
-
}
|
|
549
|
+
showNextEvent();
|
|
436
550
|
}
|
|
437
551
|
});
|
|
438
552
|
window.addEventListener("input", async (event) => {
|
|
@@ -440,10 +554,7 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
440
554
|
currentEventInfo?.event?.type === "input" &&
|
|
441
555
|
matchElement(event, currentEventInfo)
|
|
442
556
|
) {
|
|
443
|
-
|
|
444
|
-
if (currentEventInfo) {
|
|
445
|
-
showPopover(currentEventInfo);
|
|
446
|
-
}
|
|
557
|
+
showNextEvent();
|
|
447
558
|
}
|
|
448
559
|
});
|
|
449
560
|
window.addEventListener("keypress", async (event) => {
|
|
@@ -451,15 +562,53 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
451
562
|
currentEventInfo?.event?.type === "keypress" &&
|
|
452
563
|
matchElement(event, currentEventInfo)
|
|
453
564
|
) {
|
|
454
|
-
|
|
455
|
-
if (currentEventInfo) {
|
|
456
|
-
showPopover(currentEventInfo);
|
|
457
|
-
}
|
|
565
|
+
showNextEvent();
|
|
458
566
|
}
|
|
459
567
|
});
|
|
460
568
|
});
|
|
461
569
|
|
|
462
|
-
await runEvent(page, events[0]);
|
|
570
|
+
await runEvent({ page, event: events[0] });
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
const runEventsForScreenshots = async (page, ftmocksConifg, testName) => {
|
|
574
|
+
const eventsFile = path.join(
|
|
575
|
+
getMockDir(ftmocksConifg),
|
|
576
|
+
`test_${nameToFolder(testName)}`,
|
|
577
|
+
`_events.json`
|
|
578
|
+
);
|
|
579
|
+
const events = JSON.parse(fs.readFileSync(eventsFile, "utf8"));
|
|
580
|
+
await runEvents({
|
|
581
|
+
page,
|
|
582
|
+
events,
|
|
583
|
+
delay: ftmocksConifg.delay || 1000,
|
|
584
|
+
screenshots: true,
|
|
585
|
+
screenshotsDir: path.join(
|
|
586
|
+
getMockDir(ftmocksConifg),
|
|
587
|
+
`test_${nameToFolder(testName)}`,
|
|
588
|
+
`screenshots`
|
|
589
|
+
),
|
|
590
|
+
});
|
|
591
|
+
fs.writeFileSync(eventsFile, JSON.stringify(events, null, 2));
|
|
592
|
+
await page.waitForTimeout(1000);
|
|
593
|
+
await page.close();
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
const runEventsForHealingSelectors = async (page, ftmocksConifg, testName) => {
|
|
597
|
+
const eventsFile = path.join(
|
|
598
|
+
getMockDir(ftmocksConifg),
|
|
599
|
+
`test_${nameToFolder(testName)}`,
|
|
600
|
+
`_events.json`
|
|
601
|
+
);
|
|
602
|
+
const events = JSON.parse(fs.readFileSync(eventsFile, "utf8"));
|
|
603
|
+
await runEvents({
|
|
604
|
+
page,
|
|
605
|
+
events,
|
|
606
|
+
delay: ftmocksConifg.delay || 1000,
|
|
607
|
+
healSelectors: true,
|
|
608
|
+
});
|
|
609
|
+
fs.writeFileSync(eventsFile, JSON.stringify(events, null, 2));
|
|
610
|
+
await page.waitForTimeout(1000);
|
|
611
|
+
await page.close();
|
|
463
612
|
};
|
|
464
613
|
|
|
465
614
|
module.exports = {
|
|
@@ -467,5 +616,7 @@ module.exports = {
|
|
|
467
616
|
runEventsForTest,
|
|
468
617
|
runEventsInPresentationMode,
|
|
469
618
|
runEventsInTrainingMode,
|
|
619
|
+
runEventsForScreenshots,
|
|
620
|
+
runEventsForHealingSelectors,
|
|
470
621
|
runEvent,
|
|
471
622
|
};
|
package/src/index.js
CHANGED
|
@@ -27,6 +27,8 @@ const {
|
|
|
27
27
|
runEvent,
|
|
28
28
|
runEventsInPresentationMode,
|
|
29
29
|
runEventsInTrainingMode,
|
|
30
|
+
runEventsForScreenshots,
|
|
31
|
+
runEventsForHealingSelectors,
|
|
30
32
|
} = require("./event-run-utils");
|
|
31
33
|
|
|
32
34
|
// Export functions as a module
|
|
@@ -54,4 +56,6 @@ module.exports = {
|
|
|
54
56
|
runEvent,
|
|
55
57
|
runEventsInPresentationMode,
|
|
56
58
|
runEventsInTrainingMode,
|
|
59
|
+
runEventsForScreenshots,
|
|
60
|
+
runEventsForHealingSelectors,
|
|
57
61
|
};
|