ftmocks-utils 1.4.7 → 1.4.9

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ftmocks-utils",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "Util functions for FtMocks",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -19,6 +19,8 @@
19
19
  "homepage": "https://github.com/SodhanaLibrary/ftmocks-utils#readme",
20
20
  "dependencies": {
21
21
  "uui": "^1.0.7",
22
- "uuid": "^11.1.0"
22
+ "uuid": "^11.1.0",
23
+ "pixelmatch": "^5.3.1",
24
+ "pngjs": "^7.0.1"
23
25
  }
24
26
  }
@@ -2,9 +2,80 @@ const path = require("path");
2
2
  const fs = require("fs");
3
3
  const { getMockDir, nameToFolder } = require("./common-utils");
4
4
 
5
+ const createDiffImage = async (img1, img2, diffPath) => {
6
+ const pixelmatch = (await import("pixelmatch")).default;
7
+ const { PNG } = (await import("pngjs")).default;
8
+
9
+ const { width, height } = img1;
10
+
11
+ const diff = new PNG({ width, height });
12
+
13
+ const numDiffPixels = pixelmatch(
14
+ img1.data,
15
+ img2.data,
16
+ diff.data,
17
+ width,
18
+ height,
19
+ {
20
+ threshold: 0.1, // sensitivity
21
+ diffColor: [255, 0, 0], // highlight color (red)
22
+ diffMask: false,
23
+ }
24
+ );
25
+
26
+ fs.writeFileSync(diffPath, PNG.sync.write(diff));
27
+
28
+ return numDiffPixels;
29
+ };
30
+
31
+ const matchAndReplaceScreenshot = async (page, event, screenshotsDir) => {
32
+ const pixelmatch = (await import("pixelmatch")).default;
33
+ const { PNG } = (await import("pngjs")).default;
34
+
35
+ const file = path.join(screenshotsDir, `${event.id}.png`);
36
+ const newFile = path.join(screenshotsDir, `__temp_${event.id}.png`);
37
+
38
+ await page.screenshot({ path: newFile, fullPage: false });
39
+
40
+ if (!fs.existsSync(file)) {
41
+ fs.renameSync(newFile, file);
42
+ return {
43
+ replaced: true,
44
+ diffPath: null,
45
+ };
46
+ }
47
+
48
+ const img1 = PNG.sync.read(fs.readFileSync(file));
49
+ const img2 = PNG.sync.read(fs.readFileSync(newFile));
50
+
51
+ const diff = pixelmatch(img1.data, img2.data, null, img1.width, img1.height, {
52
+ threshold: 0.1,
53
+ });
54
+
55
+ if (diff > 0) {
56
+ console.log(`Screenshot changed → replacing: ${file}`);
57
+ fs.renameSync(newFile, file); // overwrite only when mismatch
58
+ await createDiffImage(
59
+ img1,
60
+ img2,
61
+ path.join(screenshotsDir, `diff_${event.id}.png`)
62
+ );
63
+ return {
64
+ replaced: true,
65
+ diffPath: `diff_${event.id}.png`,
66
+ };
67
+ } else {
68
+ console.log(`Screenshot did not change → removing temp file: ${newFile}`);
69
+ fs.unlinkSync(newFile); // no change → remove temp file
70
+ return {
71
+ replaced: false,
72
+ diffPath: null,
73
+ };
74
+ }
75
+ };
76
+
5
77
  const getLocator = async (page, event) => {
6
78
  // Check if the event.target exists on the page before returning it.
7
- console.log("➡ Getting locator for event", event);
8
79
  if (event && event.target && typeof page !== "undefined" && page.locator) {
9
80
  let locator = null;
10
81
  while (!locator) {
@@ -32,7 +103,6 @@ const getLocator = async (page, event) => {
32
103
  } catch (error) {
33
104
  console.error("Error getting locator", error, selector);
34
105
  }
35
- console.log("➡ Waiting for locator", event);
36
106
  await page.waitForTimeout(500);
37
107
  }
38
108
  return locator;
@@ -102,20 +172,26 @@ const runEvent = async ({
102
172
  healSelectors = false,
103
173
  }) => {
104
174
  try {
105
- console.log("➡ Running event", event);
106
175
  const beforeEvent = async () => {
107
176
  await page.waitForTimeout(delay);
108
177
  if (screenshots) {
109
- const locator = await getLocator(page, event);
110
- const position = await getSelectorPosition(page, locator);
111
- event.screenshotInfo = {
112
- name: `${event.id}.png`,
113
- position,
114
- };
115
- await page.screenshot({
116
- path: path.join(screenshotsDir, `${event.id}.png`),
117
- fullPage: false,
118
- });
178
+ const res = await matchAndReplaceScreenshot(
179
+ page,
180
+ event,
181
+ screenshotsDir
182
+ );
183
+ if (res.replaced) {
184
+ const locator = await getLocator(page, event);
185
+ const position = await getSelectorPosition(page, locator);
186
+ event.screenshotInfo = {
187
+ name: `${event.id}.png`,
188
+ position,
189
+ time: new Date().toISOString(),
190
+ diffPath: res.diffPath
191
+ ? res.diffPath
192
+ : event.screenshotInfo?.diffPath,
193
+ };
194
+ }
119
195
  }
120
196
  if (healSelectors) {
121
197
  const locator = await getLocator(page, event);
@@ -207,7 +283,6 @@ const runEvent = async ({
207
283
 
208
284
  const isValidEvent = (event) => {
209
285
  try {
210
- console.log("➡ Validating event", event);
211
286
  switch (event?.type) {
212
287
  case "click":
213
288
  return true;
@@ -285,9 +360,7 @@ const runEventsInPresentationMode = async (page, ftmocksConifg, testName) => {
285
360
 
286
361
  // Expose Node function
287
362
  await page.exposeFunction("nextEvent", async () => {
288
- console.log("➡ Next event triggered!");
289
363
  if (currentEventIndex === events.length) {
290
- console.log("➡ No more events to run!");
291
364
  return false;
292
365
  }
293
366
  let result = await runEvent({ page, event: events[currentEventIndex] });