ftmocks-utils 1.4.2 → 1.4.4
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 +123 -35
package/package.json
CHANGED
package/src/event-run-utils.js
CHANGED
|
@@ -4,19 +4,35 @@ const { getMockDir, nameToFolder } = require("./common-utils");
|
|
|
4
4
|
|
|
5
5
|
const getLocator = async (page, event) => {
|
|
6
6
|
// Check if the event.target exists on the page before returning it.
|
|
7
|
+
console.log("➡ Getting locator for event", event);
|
|
7
8
|
if (event && event.target && typeof page !== "undefined" && page.locator) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const count = await page.locator(
|
|
9
|
+
let locator = null;
|
|
10
|
+
while (!locator) {
|
|
11
|
+
const selector = event.target.startsWith("/")
|
|
12
|
+
? `xpath=${event.target}`
|
|
13
|
+
: event.target;
|
|
14
|
+
try {
|
|
15
|
+
const count = await page.locator(selector).count();
|
|
15
16
|
if (count === 1) {
|
|
16
|
-
|
|
17
|
+
locator = selector;
|
|
18
|
+
} else {
|
|
19
|
+
for (let i = 0; i < event.selectors.length; i++) {
|
|
20
|
+
const selector = event.selectors[i].value.startsWith("/")
|
|
21
|
+
? `xpath=${event.selectors[i].value}`
|
|
22
|
+
: event.selectors[i].value;
|
|
23
|
+
const count = await page.locator(selector).count();
|
|
24
|
+
if (count === 1) {
|
|
25
|
+
locator = selector;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
17
28
|
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("Error getting locator", error, selector);
|
|
18
31
|
}
|
|
32
|
+
console.log("➡ Waiting for locator", event);
|
|
33
|
+
await page.waitForTimeout(500);
|
|
19
34
|
}
|
|
35
|
+
return locator;
|
|
20
36
|
}
|
|
21
37
|
return event.target;
|
|
22
38
|
};
|
|
@@ -83,8 +99,6 @@ const isValidEvent = (event) => {
|
|
|
83
99
|
try {
|
|
84
100
|
console.log("➡ Validating event", event);
|
|
85
101
|
switch (event?.type) {
|
|
86
|
-
case "url":
|
|
87
|
-
return true;
|
|
88
102
|
case "click":
|
|
89
103
|
return true;
|
|
90
104
|
case "input":
|
|
@@ -152,7 +166,7 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
|
152
166
|
console.log("➡ Next event triggered!");
|
|
153
167
|
if (currentEventIndex === events.length) {
|
|
154
168
|
console.log("➡ No more events to run!");
|
|
155
|
-
return;
|
|
169
|
+
return false;
|
|
156
170
|
}
|
|
157
171
|
let result = await runEvent(page, events[currentEventIndex]);
|
|
158
172
|
while (result === "Unsupported event type") {
|
|
@@ -160,12 +174,17 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
|
160
174
|
result = await runEvent(page, events[currentEventIndex]);
|
|
161
175
|
}
|
|
162
176
|
currentEventIndex = currentEventIndex + 1;
|
|
177
|
+
return true;
|
|
163
178
|
});
|
|
164
179
|
|
|
165
180
|
await page.exposeFunction("focusOnBodyForPresentationMode", async () => {
|
|
166
181
|
await page.bringToFront();
|
|
167
182
|
});
|
|
168
183
|
|
|
184
|
+
await page.exposeFunction("playwrightPageClose", async () => {
|
|
185
|
+
await page.close();
|
|
186
|
+
});
|
|
187
|
+
|
|
169
188
|
// Inject keyboard listener into browser
|
|
170
189
|
await page.addInitScript(() => {
|
|
171
190
|
window.addEventListener("load", async () => {
|
|
@@ -174,10 +193,15 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
|
174
193
|
window.focus();
|
|
175
194
|
document.body.focus();
|
|
176
195
|
});
|
|
177
|
-
window.addEventListener("keydown", (e) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
196
|
+
window.addEventListener("keydown", async (e) => {
|
|
197
|
+
console.log("➡ keydown event triggered!", e);
|
|
198
|
+
if (e.key === "Shift" && !e.repeat) {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
const result = await window.nextEvent();
|
|
201
|
+
if (!result) {
|
|
202
|
+
console.log("➡ No more events to run!");
|
|
203
|
+
await window.playwrightPageClose();
|
|
204
|
+
}
|
|
181
205
|
}
|
|
182
206
|
});
|
|
183
207
|
});
|
|
@@ -186,7 +210,7 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
|
|
|
186
210
|
};
|
|
187
211
|
|
|
188
212
|
const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
189
|
-
|
|
213
|
+
const executedEvents = [];
|
|
190
214
|
const eventsFile = path.join(
|
|
191
215
|
getMockDir(ftmocksConifg),
|
|
192
216
|
`test_${nameToFolder(testName)}`,
|
|
@@ -197,20 +221,31 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
197
221
|
// Expose Node function
|
|
198
222
|
await page.exposeFunction("getNextEvent", async () => {
|
|
199
223
|
let result = false;
|
|
224
|
+
let nonExecutedEvents = events.filter(
|
|
225
|
+
(event) => !executedEvents.includes(event?.id)
|
|
226
|
+
);
|
|
227
|
+
let currentEventIndex = -1;
|
|
200
228
|
while (!result) {
|
|
201
229
|
currentEventIndex = currentEventIndex + 1;
|
|
202
|
-
if (currentEventIndex ===
|
|
230
|
+
if (currentEventIndex === nonExecutedEvents.length) {
|
|
203
231
|
console.log("➡ No more events to validate!");
|
|
204
232
|
return;
|
|
205
233
|
}
|
|
206
|
-
result = isValidEvent(
|
|
234
|
+
result = isValidEvent(nonExecutedEvents[currentEventIndex]);
|
|
207
235
|
}
|
|
208
|
-
if (
|
|
209
|
-
|
|
236
|
+
if (nonExecutedEvents[currentEventIndex]) {
|
|
237
|
+
console.log(
|
|
238
|
+
"➡ Getting locator for event",
|
|
239
|
+
nonExecutedEvents[currentEventIndex]
|
|
240
|
+
);
|
|
241
|
+
const selector = await getLocator(
|
|
242
|
+
page,
|
|
243
|
+
nonExecutedEvents[currentEventIndex]
|
|
244
|
+
);
|
|
210
245
|
const position = await getSelectorPosition(page, selector);
|
|
211
246
|
const element = await page.locator(selector).elementHandle();
|
|
212
247
|
return {
|
|
213
|
-
event:
|
|
248
|
+
event: nonExecutedEvents[currentEventIndex],
|
|
214
249
|
selector,
|
|
215
250
|
position,
|
|
216
251
|
element,
|
|
@@ -219,6 +254,14 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
219
254
|
return null;
|
|
220
255
|
});
|
|
221
256
|
|
|
257
|
+
await page.exposeFunction("addExecutedEvent", (eventId) => {
|
|
258
|
+
executedEvents.push(eventId);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
await page.exposeFunction("playwrightPageClose", async () => {
|
|
262
|
+
await page.close();
|
|
263
|
+
});
|
|
264
|
+
|
|
222
265
|
// Inject keyboard listener into browser
|
|
223
266
|
await page.addInitScript(async () => {
|
|
224
267
|
let currentEventInfo = null;
|
|
@@ -242,6 +285,44 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
242
285
|
popover.style.borderRadius = "8px";
|
|
243
286
|
popover.style.boxShadow = "0 2px 12px rgba(0,0,0,0.25)";
|
|
244
287
|
|
|
288
|
+
// Success Training Popover
|
|
289
|
+
const successPopover = document.createElement("div");
|
|
290
|
+
successPopover.id = "ftmocks-success-popover-training-mode";
|
|
291
|
+
successPopover.style.position = "fixed";
|
|
292
|
+
successPopover.style.top = "32px";
|
|
293
|
+
successPopover.style.left = "50%";
|
|
294
|
+
successPopover.style.transform = "translateX(-50%)";
|
|
295
|
+
successPopover.style.minWidth = "120px";
|
|
296
|
+
successPopover.style.height = "46px";
|
|
297
|
+
successPopover.style.background = "rgba(60,180,75,0.98)";
|
|
298
|
+
successPopover.style.color = "#fff";
|
|
299
|
+
successPopover.style.display = "none";
|
|
300
|
+
successPopover.style.zIndex = "100000";
|
|
301
|
+
successPopover.style.fontFamily = "sans-serif";
|
|
302
|
+
successPopover.style.fontSize = "16px";
|
|
303
|
+
successPopover.style.textAlign = "center";
|
|
304
|
+
successPopover.style.lineHeight = "1.4";
|
|
305
|
+
successPopover.style.padding = "12px 32px";
|
|
306
|
+
successPopover.style.borderRadius = "8px";
|
|
307
|
+
successPopover.style.boxShadow = "0 2px 12px rgba(0,0,0,0.20)";
|
|
308
|
+
successPopover.textContent = "✅ Training Success!";
|
|
309
|
+
|
|
310
|
+
// Utility to show the success popover for a short period
|
|
311
|
+
function showSuccessPopover(message = "✅ Training Success!") {
|
|
312
|
+
successPopover.textContent = message;
|
|
313
|
+
if (!document.getElementById("ftmocks-success-popover-training-mode")) {
|
|
314
|
+
document.body.appendChild(successPopover);
|
|
315
|
+
}
|
|
316
|
+
successPopover.style.display = "block";
|
|
317
|
+
setTimeout(() => {
|
|
318
|
+
successPopover.style.display = "none";
|
|
319
|
+
document
|
|
320
|
+
.getElementById("ftmocks-success-popover-training-mode")
|
|
321
|
+
?.remove();
|
|
322
|
+
window.playwrightPageClose();
|
|
323
|
+
}, 3000);
|
|
324
|
+
}
|
|
325
|
+
|
|
245
326
|
const highlighter = document.createElement("div");
|
|
246
327
|
highlighter.id = "ftmocks-highlighter-training-mode";
|
|
247
328
|
highlighter.style.position = "absolute";
|
|
@@ -253,8 +334,11 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
253
334
|
highlighter.style.border = "2px solid #3fa9f5";
|
|
254
335
|
highlighter.style.display = "none";
|
|
255
336
|
highlighter.style.pointerEvents = "none";
|
|
337
|
+
highlighter.style.zIndex = "99999";
|
|
256
338
|
|
|
257
339
|
function showPopover(eventInfo) {
|
|
340
|
+
console.log("➡ Showing popover", eventInfo);
|
|
341
|
+
window.addExecutedEvent(eventInfo.event.id);
|
|
258
342
|
if (!document.getElementById("ftmocks-popover-training-mode")) {
|
|
259
343
|
document.body.appendChild(popover);
|
|
260
344
|
}
|
|
@@ -278,6 +362,10 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
278
362
|
|
|
279
363
|
function hidePopover() {
|
|
280
364
|
popover.style.display = "none";
|
|
365
|
+
highlighter.style.display = "none";
|
|
366
|
+
document.getElementById("ftmocks-popover-training-mode")?.remove();
|
|
367
|
+
document.getElementById("ftmocks-highlighter-training-mode")?.remove();
|
|
368
|
+
showSuccessPopover("✅ Training Success!");
|
|
281
369
|
}
|
|
282
370
|
|
|
283
371
|
const initialEventRun = async () => {
|
|
@@ -288,15 +376,18 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
288
376
|
};
|
|
289
377
|
|
|
290
378
|
const matchElement = (event, currentEventInfo) => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
379
|
+
const inBoudingBox =
|
|
380
|
+
currentEventInfo?.position?.x <= event.clientX &&
|
|
381
|
+
currentEventInfo?.position?.x + currentEventInfo?.position?.width >=
|
|
382
|
+
event.clientX &&
|
|
383
|
+
currentEventInfo?.position?.y <= event.clientY &&
|
|
384
|
+
currentEventInfo?.position?.y + currentEventInfo?.position?.height >=
|
|
385
|
+
event.clientY;
|
|
386
|
+
console.log("➡ In bounding box?", inBoudingBox);
|
|
387
|
+
const matchingElement =
|
|
388
|
+
currentEventInfo?.element?.isEqualNode(event.target) ||
|
|
389
|
+
currentEventInfo?.element?.contains(event.target);
|
|
390
|
+
return inBoudingBox || matchingElement;
|
|
300
391
|
};
|
|
301
392
|
|
|
302
393
|
window.addEventListener("load", async () => {
|
|
@@ -304,16 +395,13 @@ const runEventsInTrainingMode = async (page, ftmocksConifg, testName) => {
|
|
|
304
395
|
});
|
|
305
396
|
|
|
306
397
|
window.addEventListener("click", async (event) => {
|
|
307
|
-
console.log(
|
|
308
|
-
"➡ Click event triggered!",
|
|
309
|
-
event.target.isEqualNode(currentEventInfo?.element),
|
|
310
|
-
currentEventInfo?.element?.contains(event.target)
|
|
311
|
-
);
|
|
312
398
|
if (
|
|
313
399
|
currentEventInfo?.event?.type === "click" &&
|
|
314
400
|
matchElement(event, currentEventInfo)
|
|
315
401
|
) {
|
|
402
|
+
console.log("➡ Click event triggered!", event);
|
|
316
403
|
currentEventInfo = await window.getNextEvent();
|
|
404
|
+
console.log("➡ Next event", currentEventInfo);
|
|
317
405
|
if (currentEventInfo) {
|
|
318
406
|
showPopover(currentEventInfo);
|
|
319
407
|
} else {
|