Skip to content

Commit 1e9d9d5

Browse files
committed
fix: fix 1115
1 parent 77a6621 commit 1e9d9d5

9 files changed

Lines changed: 88 additions & 11 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"@wdio/image-comparison-core": patch
3+
"@wdio/visual-service": patch
4+
---
5+
6+
## #1115 Respect `alwaysSaveActualImage: false` for `checkScreen` methods
7+
8+
When using visual matchers like `toMatchScreenSnapshot('tag', 0.9)` with `alwaysSaveActualImage: false`, the actual image was still being saved even when the comparison passed within the threshold.
9+
10+
The root cause was that the matcher's expected threshold was not being passed to the core comparison logic. The core used `saveAboveTolerance` (defaulting to 0) to decide whether to save images, while the matcher used the user-provided threshold to determine pass/fail - these were disconnected.
11+
12+
This fix ensures:
13+
- When `alwaysSaveActualImage: false` and `saveAboveTolerance` is not explicitly set, actual images are never saved (respecting the literal meaning of the option)
14+
- When `saveAboveTolerance` is explicitly set (like matchers do internally), actual images are saved only when the mismatch exceeds that threshold
15+
16+
# Committers: 1
17+
18+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"test:unit": "vitest --coverage --run",
2020
"test:unit:ui": "vitest --coverage --ui",
2121
"test:unit:watch": "vitest --coverage --watch",
22-
"test.local.init": "rimraf localBaseline && wdio ./tests/configs/wdio.local.init.conf.ts",
22+
"test.local.init": "rimraf localBaseline && SAVE_ACTUAL=true wdio ./tests/configs/wdio.local.init.conf.ts",
2323
"test.local.desktop": "wdio tests/configs/wdio.local.desktop.conf.ts",
2424
"test.local.emus.app": "wdio tests/configs/wdio.local.android.emus.app.conf.ts",
2525
"test.local.emus.web": "wdio tests/configs/wdio.local.android.emus.web.conf.ts",

packages/image-comparison-core/src/__snapshots__/base.test.ts.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ exports[`BaseClass > initializes default options correctly 1`] = `
2121
"ignoreNothing": false,
2222
"rawMisMatchPercentage": false,
2323
"returnAllCompareData": false,
24-
"saveAboveTolerance": 0,
2524
"scaleImagesToSameSize": false,
2625
},
2726
"disableBlinkingCursor": false,

packages/image-comparison-core/src/helpers/__snapshots__/options.test.ts.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ exports[`options > defaultOptions > should return the default options when no op
9797
"ignoreNothing": false,
9898
"rawMisMatchPercentage": false,
9999
"returnAllCompareData": false,
100-
"saveAboveTolerance": 0,
101100
"scaleImagesToSameSize": false,
102101
},
103102
"disableBlinkingCursor": false,

packages/image-comparison-core/src/helpers/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const DEFAULT_COMPARE_OPTIONS = {
1616
ignoreNothing: false,
1717
rawMisMatchPercentage: false,
1818
returnAllCompareData: false,
19-
saveAboveTolerance: 0,
2019
scaleImagesToSameSize: false,
2120
}
2221
export const DEFAULT_FORMAT_STRING = '{tag}-{browserName}-{width}x{height}-dpr-{dpr}'

packages/image-comparison-core/src/methods/images.executeImageCompare.test.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -693,14 +693,60 @@ describe('executeImageCompare', () => {
693693
expect(fsPromises.writeFile).toHaveBeenCalledWith('/mock/actual/test.png', Buffer.from(base64Image, 'base64'))
694694
})
695695

696-
it('should save base64 actual on diff when not always saving', async () => {
696+
it('should NOT save base64 actual on diff when alwaysSaveActualImage is false and saveAboveTolerance is not set (#1115)', async () => {
697+
// When alwaysSaveActualImage: false and saveAboveTolerance is not explicitly set,
698+
// actual images should never be saved - respecting the literal meaning of the option
697699
const base64Image = Buffer.from('base64-image').toString('base64')
698700
const optionsWithDiff = {
699701
...mockOptions,
700702
folderOptions: {
701703
...mockOptions.folderOptions,
702704
alwaysSaveActualImage: false,
703-
}
705+
},
706+
compareOptions: {
707+
...mockOptions.compareOptions,
708+
wic: {
709+
...mockOptions.compareOptions.wic,
710+
saveAboveTolerance: undefined,
711+
},
712+
},
713+
}
714+
vi.mocked(compareImages.default).mockResolvedValue({
715+
rawMisMatchPercentage: 0.5,
716+
misMatchPercentage: 0.5,
717+
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
718+
diffBounds: { left: 0, top: 0, right: 0, bottom: 0 },
719+
analysisTime: 10,
720+
diffPixels: []
721+
})
722+
723+
await executeImageCompare({
724+
isViewPortScreenshot: true,
725+
isNativeContext: false,
726+
options: optionsWithDiff,
727+
testContext: mockTestContext,
728+
actualBase64Image: base64Image,
729+
})
730+
731+
expect(fsPromises.writeFile).not.toHaveBeenCalled()
732+
})
733+
734+
it('should save base64 actual on diff when saveAboveTolerance is explicitly set to 0', async () => {
735+
// When saveAboveTolerance is explicitly set (even to 0), save actual images when diff exceeds it
736+
const base64Image = Buffer.from('base64-image').toString('base64')
737+
const optionsWithDiff = {
738+
...mockOptions,
739+
folderOptions: {
740+
...mockOptions.folderOptions,
741+
alwaysSaveActualImage: false,
742+
},
743+
compareOptions: {
744+
...mockOptions.compareOptions,
745+
wic: {
746+
...mockOptions.compareOptions.wic,
747+
saveAboveTolerance: 0,
748+
},
749+
},
704750
}
705751
vi.mocked(compareImages.default).mockResolvedValue({
706752
rawMisMatchPercentage: 0.5,

packages/image-comparison-core/src/methods/images.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,15 @@ export async function executeImageCompare(
450450
)
451451

452452
// 6a. Save actual image on failure if alwaysSaveActualImage is false
453-
const saveAboveTolerance = imageCompareOptions.saveAboveTolerance ?? 0
454-
const hasFailure = rawMisMatchPercentage > saveAboveTolerance
455-
if (useBase64Image && hasFailure && actualBase64Image) {
456-
// Save the actual image only when comparison fails
453+
// Only save when saveAboveTolerance is explicitly set (e.g., by matchers).
454+
// When using checkScreen directly without saveAboveTolerance, respect
455+
// alwaysSaveActualImage: false literally and don't save actual images.
456+
// @see https://github.com/webdriverio/visual-testing/issues/1115
457+
const saveAboveTolerance = imageCompareOptions.saveAboveTolerance
458+
const shouldSaveOnFailure = saveAboveTolerance !== undefined
459+
const hasFailure = rawMisMatchPercentage > (saveAboveTolerance ?? 0)
460+
if (useBase64Image && shouldSaveOnFailure && hasFailure && actualBase64Image) {
461+
// Save the actual image only when comparison fails and threshold was explicitly set
457462
await saveBase64Image(actualBase64Image, actualFilePath)
458463
}
459464

tests/configs/wdio.local.desktop.conf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export const config: WebdriverIO.Config = {
7272
createJsonReportFiles: true,
7373
clearRuntimeFolder: true,
7474
enableLayoutTesting: true,
75-
alwaysSaveActualImage: false,
75+
alwaysSaveActualImage: process.env.SAVE_ACTUAL === 'true',
7676
},
7777
]
7878
],

tests/specs/desktop.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import type { ImageCompareResult } from '@wdio/image-comparison-core'
12
import { browser, expect } from '@wdio/globals'
3+
import { fileExists } from '../helpers/fileExists.ts'
24

35
describe('@wdio/visual-service desktop', () => {
46
// @TODO
@@ -39,4 +41,13 @@ describe('@wdio/visual-service desktop', () => {
3941
],
4042
})
4143
})
44+
45+
it(`should not store an actual image for '${browserName}' when the diff is below the threshold`, async function() {
46+
const result = await browser.checkScreen('examplePageFail', {
47+
returnAllCompareData: true,
48+
}) as ImageCompareResult
49+
50+
expect(result.misMatchPercentage).toBeLessThanOrEqual(50)
51+
expect(fileExists(result.folders.actual)).toBe(false)
52+
})
4253
})

0 commit comments

Comments
 (0)