@wdio/image-comparison-core 1.1.0 → 1.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @wdio/image-comparison-core
2
2
 
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 0a2b6d0: ## #1111 Respect saveAboveTolerance when deciding to save actual images when alwaysSaveActualImage is false.
8
+
9
+ When `alwaysSaveActualImage` is `false`, the actual image is no longer written to disk if the mismatch is below the configured tolerance, avoiding extra actuals when the comparison still passes.
10
+
11
+ # Committers: 1
12
+
13
+ - Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
14
+
15
+ ## 1.1.1
16
+
17
+ ### Patch Changes
18
+
19
+ - 340fbe6: # 🐛 Bugfixes
20
+
21
+ ## #1098 Improve error message when baseline is missing and both flags are false
22
+
23
+ When `autoSaveBaseline = false` and `alwaysSaveActualImage = false` and a baseline image doesn't exist, the error message now provides clear guidance suggesting users set `alwaysSaveActualImage` to `true` if they need the actual image to create a baseline manually.
24
+
25
+ # Committers: 1
26
+
27
+ - Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
28
+
29
+ - e4e5b5c: # 🐛 Bugfixes
30
+
31
+ ## #1085 autoSaveBaseline collides with the new alwaysSaveActualImage flag
32
+
33
+ When `autoSaveBaseline` is `true` and `alwaysSaveActualImage` is `false`, actual images were still saved. This patch should fix that
34
+
35
+ # Committers: 1
36
+
37
+ - Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
38
+
3
39
  ## 1.1.0
4
40
 
5
41
  ### Minor Changes
@@ -94,7 +94,7 @@ vi.mock('./images.js', async () => {
94
94
  addBlockOuts: vi.fn(),
95
95
  };
96
96
  });
97
- import { executeImageCompare } from './images.js';
97
+ import { executeImageCompare, checkBaselineImageExists } from './images.js';
98
98
  import * as images from './images.js';
99
99
  describe('executeImageCompare', () => {
100
100
  const mockDeviceRectangles = {
@@ -597,6 +597,12 @@ describe('executeImageCompare', () => {
597
597
  autoSaveBaseline: true,
598
598
  }
599
599
  };
600
+ vi.mocked(fsPromises.access).mockImplementation(async (path) => {
601
+ if (path === '/mock/baseline/test.png') {
602
+ throw new Error('File not found');
603
+ }
604
+ return undefined;
605
+ });
600
606
  vi.mocked(compareImages.default).mockResolvedValue({
601
607
  rawMisMatchPercentage: 0,
602
608
  misMatchPercentage: 0,
@@ -640,6 +646,72 @@ describe('executeImageCompare', () => {
640
646
  });
641
647
  expect(fsPromises.writeFile).toHaveBeenCalledWith('/mock/actual/test.png', Buffer.from(base64Image, 'base64'));
642
648
  });
649
+ it('should not save base64 actual when diff is below saveAboveTolerance', async () => {
650
+ const base64Image = Buffer.from('base64-image').toString('base64');
651
+ const optionsWithTolerance = {
652
+ ...mockOptions,
653
+ folderOptions: {
654
+ ...mockOptions.folderOptions,
655
+ alwaysSaveActualImage: false,
656
+ },
657
+ compareOptions: {
658
+ ...mockOptions.compareOptions,
659
+ wic: {
660
+ ...mockOptions.compareOptions.wic,
661
+ saveAboveTolerance: 0.1,
662
+ },
663
+ },
664
+ };
665
+ vi.mocked(compareImages.default).mockResolvedValue({
666
+ rawMisMatchPercentage: 0.05,
667
+ misMatchPercentage: 0.05,
668
+ getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
669
+ diffBounds: { left: 0, top: 0, right: 0, bottom: 0 },
670
+ analysisTime: 10,
671
+ diffPixels: []
672
+ });
673
+ await executeImageCompare({
674
+ isViewPortScreenshot: true,
675
+ isNativeContext: false,
676
+ options: optionsWithTolerance,
677
+ testContext: mockTestContext,
678
+ actualBase64Image: base64Image,
679
+ });
680
+ expect(fsPromises.writeFile).not.toHaveBeenCalled();
681
+ });
682
+ it('should save base64 actual when diff exceeds saveAboveTolerance', async () => {
683
+ const base64Image = Buffer.from('base64-image').toString('base64');
684
+ const optionsWithTolerance = {
685
+ ...mockOptions,
686
+ folderOptions: {
687
+ ...mockOptions.folderOptions,
688
+ alwaysSaveActualImage: false,
689
+ },
690
+ compareOptions: {
691
+ ...mockOptions.compareOptions,
692
+ wic: {
693
+ ...mockOptions.compareOptions.wic,
694
+ saveAboveTolerance: 0.1,
695
+ },
696
+ },
697
+ };
698
+ vi.mocked(compareImages.default).mockResolvedValue({
699
+ rawMisMatchPercentage: 0.2,
700
+ misMatchPercentage: 0.2,
701
+ getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
702
+ diffBounds: { left: 0, top: 0, right: 0, bottom: 0 },
703
+ analysisTime: 10,
704
+ diffPixels: []
705
+ });
706
+ await executeImageCompare({
707
+ isViewPortScreenshot: true,
708
+ isNativeContext: false,
709
+ options: optionsWithTolerance,
710
+ testContext: mockTestContext,
711
+ actualBase64Image: base64Image,
712
+ });
713
+ expect(fsPromises.writeFile).toHaveBeenCalledWith('/mock/actual/test.png', Buffer.from(base64Image, 'base64'));
714
+ });
643
715
  it('should update baseline using base64 when visual baseline is updated', async () => {
644
716
  const base64Image = Buffer.from('base64-image').toString('base64');
645
717
  vi.mocked(utils.updateVisualBaseline).mockReturnValueOnce(true);
@@ -908,4 +980,74 @@ describe('executeImageCompare', () => {
908
980
  expect(images.saveBase64Image).not.toHaveBeenCalled();
909
981
  expect(log.warn).not.toHaveBeenCalled();
910
982
  });
983
+ it('should not save actual image when autoSaveBaseline is true, alwaysSaveActualImage is false, baseline exists, and comparison passes', async () => {
984
+ // This test covers issue #1085: autoSaveBaseline collides with alwaysSaveActualImage
985
+ // When baseline exists and comparison passes, actual image should NOT be saved
986
+ const base64Image = Buffer.from('base64-image').toString('base64');
987
+ const optionsWithAutoSave = {
988
+ ...mockOptions,
989
+ folderOptions: {
990
+ ...mockOptions.folderOptions,
991
+ alwaysSaveActualImage: false,
992
+ autoSaveBaseline: true,
993
+ }
994
+ };
995
+ vi.mocked(fsPromises.access).mockResolvedValue(undefined);
996
+ vi.mocked(images.checkBaselineImageExists).mockImplementation(async () => {
997
+ return Promise.resolve();
998
+ });
999
+ vi.mocked(compareImages.default).mockResolvedValue({
1000
+ rawMisMatchPercentage: 0,
1001
+ misMatchPercentage: 0,
1002
+ getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
1003
+ diffBounds: { left: 0, top: 0, right: 0, bottom: 0 },
1004
+ analysisTime: 10,
1005
+ diffPixels: []
1006
+ });
1007
+ await executeImageCompare({
1008
+ isViewPortScreenshot: true,
1009
+ isNativeContext: false,
1010
+ options: optionsWithAutoSave,
1011
+ testContext: mockTestContext,
1012
+ actualBase64Image: base64Image,
1013
+ });
1014
+ expect(images.saveBase64Image).not.toHaveBeenCalled();
1015
+ expect(fsPromises.writeFile).not.toHaveBeenCalledWith('/mock/actual/test.png', expect.anything());
1016
+ });
1017
+ it('should not save actual image when baseline does not exist, alwaysSaveActualImage is false, and autoSaveBaseline is false', async () => {
1018
+ // This test covers issue #1098: When both flags are false, we respect the user's choice
1019
+ // and provide a helpful error message suggesting to adjust the arguments if needed
1020
+ const base64Image = Buffer.from('base64-image').toString('base64');
1021
+ const optionsWithoutAutoSave = {
1022
+ ...mockOptions,
1023
+ folderOptions: {
1024
+ ...mockOptions.folderOptions,
1025
+ alwaysSaveActualImage: false,
1026
+ autoSaveBaseline: false,
1027
+ }
1028
+ };
1029
+ vi.mocked(fsPromises.access).mockImplementation(async (path) => {
1030
+ if (path === '/mock/baseline/test.png' || path === '/mock/actual/test.png') {
1031
+ throw new Error('File not found');
1032
+ }
1033
+ return undefined;
1034
+ });
1035
+ vi.mocked(images.checkBaselineImageExists).mockImplementation(checkBaselineImageExists);
1036
+ vi.mocked(compareImages.default).mockResolvedValue({
1037
+ rawMisMatchPercentage: 0,
1038
+ misMatchPercentage: 0,
1039
+ getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
1040
+ diffBounds: { left: 0, top: 0, right: 0, bottom: 0 },
1041
+ analysisTime: 10,
1042
+ diffPixels: []
1043
+ });
1044
+ await expect(executeImageCompare({
1045
+ isViewPortScreenshot: true,
1046
+ isNativeContext: false,
1047
+ options: optionsWithoutAutoSave,
1048
+ testContext: mockTestContext,
1049
+ actualBase64Image: base64Image,
1050
+ })).rejects.toThrow(/If you need the actual image to create a baseline, please set alwaysSaveActualImage to true/);
1051
+ expect(images.saveBase64Image).not.toHaveBeenCalled();
1052
+ });
911
1053
  });
@@ -77,11 +77,11 @@ export async function checkBaselineImageExists({ actualFilePath, baselineFilePat
77
77
  const actualFileExists = await checkIfImageExists(actualFilePath);
78
78
  const filePathMessage = actualFileExists
79
79
  ? `The image can be found here:\n${actualFilePath}`
80
- : 'The actual image was not saved to disk (alwaysSaveActualImage is false).';
80
+ : 'The actual image was not saved to disk (alwaysSaveActualImage is false).\nIf you need the actual image to create a baseline, please set alwaysSaveActualImage to true.';
81
81
  throw new Error(`
82
82
  #####################################################################################
83
- Baseline image not found, save the actual image manually to the baseline.
84
- ${filePathMessage}
83
+ Baseline image not found, save the actual image manually to the baseline.
84
+ ${filePathMessage}
85
85
  #####################################################################################`);
86
86
  }
87
87
  }
@@ -262,8 +262,8 @@ export async function executeImageCompare({ isViewPortScreenshot, isNativeContex
262
262
  if (useBase64Image) {
263
263
  // Convert base64 to buffer for comparison
264
264
  actualImageBuffer = Buffer.from(actualBase64Image, 'base64');
265
- // Only save if autoSaveBaseline is true (needed to copy to baseline)
266
- if (autoSaveBaseline) {
265
+ // Only save actual image if baseline doesn't exist and autoSaveBaseline is true
266
+ if (autoSaveBaseline && !(await checkIfImageExists(baselineFilePath))) {
267
267
  await saveBase64Image(actualBase64Image, actualFilePath);
268
268
  }
269
269
  }
@@ -316,7 +316,7 @@ export async function executeImageCompare({ isViewPortScreenshot, isNativeContex
316
316
  const { diffBoundingBoxes, storeDiffs } = await generateAndSaveDiff(data, imageCompareOptions, ignoredBoxes, diffFilePath, rawMisMatchPercentage);
317
317
  // 6a. Save actual image on failure if alwaysSaveActualImage is false
318
318
  const saveAboveTolerance = imageCompareOptions.saveAboveTolerance ?? 0;
319
- const hasFailure = rawMisMatchPercentage > 0 || rawMisMatchPercentage > saveAboveTolerance;
319
+ const hasFailure = rawMisMatchPercentage > saveAboveTolerance;
320
320
  if (useBase64Image && hasFailure && actualBase64Image) {
321
321
  // Save the actual image only when comparison fails
322
322
  await saveBase64Image(actualBase64Image, actualFilePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wdio/image-comparison-core",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "author": "Wim Selles - wswebcreation",
5
5
  "description": "Image comparison core module for @wdio/visual-service - WebdriverIO visual testing framework",
6
6
  "keywords": [
@@ -31,7 +31,7 @@
31
31
  "@wdio/types": "^9.20.0"
32
32
  },
33
33
  "devDependencies": {
34
- "webdriverio": "^9.20.1"
34
+ "webdriverio": "^9.23.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"