@wdio/image-comparison-core
Version:
Image comparison core module for @wdio/visual-service - WebdriverIO visual testing framework
771 lines (770 loc) • 30 kB
JavaScript
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { join } from 'node:path';
import logger from '@wdio/logger';
import { promises as fsPromises } from 'node:fs';
import { readFileSync, writeFileSync } from 'node:fs';
import * as utils from '../helpers/utils.js';
import * as rectangles from './rectangles.js';
import * as processDiffPixels from './processDiffPixels.js';
import * as createCompareReport from './createCompareReport.js';
import * as compareImages from '../resemble/compareImages.js';
const log = logger('test');
vi.mock('@wdio/logger', () => import(join(process.cwd(), '__mocks__', '@wdio/logger')));
vi.mock('jimp', () => {
const mockImage = {
composite: vi.fn().mockReturnThis(),
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,mock-image-data'),
opacity: vi.fn().mockReturnThis(),
width: 100,
height: 200,
bitmap: { width: 100, height: 200 },
background: 0,
formats: [],
inspect: vi.fn().mockReturnValue('MockImage'),
toString: vi.fn().mockReturnValue('MockImage'),
scanIterator: vi.fn(),
scan: vi.fn(),
scanQuiet: vi.fn(),
scanIteratorQuiet: vi.fn(),
scanQuietIterator: vi.fn(),
scanQuietIteratorQuiet: vi.fn(),
};
const JimpMock = vi.fn().mockImplementation(() => mockImage);
JimpMock.read = vi.fn().mockResolvedValue(mockImage);
JimpMock.MIME_PNG = 'image/png';
return {
Jimp: JimpMock,
JimpMime: {
png: 'image/png',
},
};
});
vi.mock('node:fs', async () => {
const actual = await vi.importActual('node:fs');
return {
...actual,
promises: {
access: vi.fn(),
unlink: vi.fn(),
mkdir: vi.fn(),
writeFile: vi.fn()
},
readFileSync: vi.fn(),
writeFileSync: vi.fn(),
constants: {
R_OK: 4,
},
};
});
vi.mock('../helpers/utils.js', () => ({
getAndCreatePath: vi.fn(),
getBase64ScreenshotSize: vi.fn(),
updateVisualBaseline: vi.fn(),
calculateDprData: vi.fn(),
prepareComparisonFilePaths: vi.fn()
}));
vi.mock('./rectangles.js', () => ({
determineStatusAddressToolBarRectangles: vi.fn(),
isWdioElement: vi.fn(),
prepareIgnoreRectangles: vi.fn()
}));
vi.mock('./processDiffPixels.js', () => ({
processDiffPixels: vi.fn(),
generateAndSaveDiff: vi.fn()
}));
vi.mock('./createCompareReport.js', () => ({
createCompareReport: vi.fn(),
createJsonReportIfNeeded: vi.fn()
}));
vi.mock('../resemble/compareImages.js', () => ({
default: vi.fn()
}));
vi.mock('../helpers/constants.js', () => ({
DEFAULT_RESIZE_DIMENSIONS: { top: 0, right: 0, bottom: 0, left: 0 }
}));
vi.mock('process', () => ({
argv: ['node', 'test.js']
}));
vi.mock('./images.js', async () => {
const actual = await vi.importActual('./images.js');
return {
...actual,
checkBaselineImageExists: vi.fn(),
removeDiffImageIfExists: vi.fn(),
saveBase64Image: vi.fn(),
addBlockOuts: vi.fn(),
};
});
import { executeImageCompare } from './images.js';
import * as images from './images.js';
describe('executeImageCompare', () => {
const mockDeviceRectangles = {
bottomBar: { x: 0, y: 0, width: 0, height: 0 },
homeBar: { x: 0, y: 0, width: 0, height: 0 },
leftSidePadding: { x: 0, y: 0, width: 0, height: 0 },
rightSidePadding: { x: 0, y: 0, width: 0, height: 0 },
screenSize: { width: 1920, height: 1080 },
statusBarAndAddressBar: { x: 0, y: 0, width: 0, height: 0 },
statusBar: { x: 0, y: 0, width: 0, height: 0 },
viewport: { x: 0, y: 0, width: 1920, height: 1080 }
};
const mockOptions = {
devicePixelRatio: 2,
deviceRectangles: mockDeviceRectangles,
ignoreRegions: [],
isAndroidNativeWebScreenshot: false,
isAndroid: false,
fileName: 'test.png',
folderOptions: {
actualFolder: '/actual',
autoSaveBaseline: false,
baselineFolder: '/baseline',
browserName: 'chrome',
deviceName: 'desktop',
diffFolder: '/diff',
isMobile: false,
savePerInstance: false
},
compareOptions: {
wic: {
scaleImagesToSameSize: true,
rawMisMatchPercentage: false,
saveAboveTolerance: 0,
createJsonReportFiles: false,
diffPixelBoundingBoxProximity: 10,
returnAllCompareData: false
},
method: {}
}
};
const mockTestContext = {
commandName: 'test',
framework: 'mocha',
parent: 'Test Suite',
tag: 'test',
title: 'Test Title',
instanceData: {
browser: { name: 'chrome', version: '100' },
deviceName: 'desktop',
platform: { name: 'windows', version: '10' },
app: 'test-app',
isMobile: false,
isAndroid: false,
isIOS: false
}
};
let logWarnSpy;
beforeEach(async () => {
vi.clearAllMocks();
const jimp = await import('jimp');
const jimpReadMock = vi.mocked(jimp.Jimp.read);
const mockImage = {
composite: vi.fn().mockReturnThis(),
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,mock-image-data'),
opacity: vi.fn().mockReturnThis(),
width: 100,
height: 200,
bitmap: { width: 100, height: 200 },
background: 0,
formats: [],
inspect: vi.fn().mockReturnValue('MockImage'),
toString: vi.fn().mockReturnValue('MockImage'),
scanIterator: vi.fn(),
scan: vi.fn(),
scanQuiet: vi.fn(),
scanIteratorQuiet: vi.fn(),
scanQuietIterator: vi.fn(),
scanQuietIteratorQuiet: vi.fn(),
};
jimpReadMock.mockResolvedValue(mockImage);
vi.mocked(fsPromises.access).mockResolvedValue(undefined);
vi.mocked(fsPromises.unlink).mockResolvedValue(undefined);
vi.mocked(fsPromises.mkdir).mockResolvedValue(undefined);
vi.mocked(fsPromises.writeFile).mockResolvedValue(undefined);
vi.mocked(readFileSync).mockReturnValue(Buffer.from('mock-image-data'));
vi.mocked(writeFileSync).mockReturnValue(undefined);
vi.mocked(utils.getAndCreatePath).mockReturnValue('/mock/path');
vi.mocked(utils.getBase64ScreenshotSize).mockReturnValue({ width: 100, height: 200 });
vi.mocked(utils.updateVisualBaseline).mockReturnValue(false);
vi.mocked(utils.calculateDprData).mockImplementation((rectangles) => rectangles);
vi.mocked(utils.prepareComparisonFilePaths).mockReturnValue({
actualFolderPath: '/mock/actual',
baselineFolderPath: '/mock/baseline',
diffFolderPath: '/mock/diff',
actualFilePath: '/mock/actual/test.png',
baselineFilePath: '/mock/baseline/test.png',
diffFilePath: '/mock/diff/test.png'
});
vi.mocked(rectangles.determineStatusAddressToolBarRectangles).mockReturnValue(null);
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [],
hasIgnoreRectangles: false
});
vi.mocked(processDiffPixels.processDiffPixels).mockReturnValue([]);
vi.mocked(processDiffPixels.generateAndSaveDiff).mockResolvedValue({
diffBoundingBoxes: [],
storeDiffs: false
});
vi.mocked(createCompareReport.createCompareReport).mockReturnValue(undefined);
vi.mocked(createCompareReport.createJsonReportIfNeeded).mockResolvedValue(undefined);
vi.mocked(compareImages.default).mockResolvedValue({
rawMisMatchPercentage: 0.5,
misMatchPercentage: 0.5,
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
diffBounds: { left: 0, top: 0, right: 100, bottom: 200 },
analysisTime: 100,
diffPixels: []
});
vi.mocked(images.checkBaselineImageExists).mockResolvedValue(undefined);
vi.mocked(images.removeDiffImageIfExists).mockResolvedValue(undefined);
vi.mocked(images.saveBase64Image).mockResolvedValue(undefined);
vi.mocked(images.addBlockOuts).mockResolvedValue('mock-blockout-image');
logWarnSpy = vi.spyOn(log, 'warn');
});
afterEach(() => {
vi.clearAllMocks();
logWarnSpy.mockRestore();
});
it('should execute image comparison successfully with default options', async () => {
const result = await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: mockOptions,
testContext: mockTestContext
});
expect(result).toMatchSnapshot();
expect(utils.prepareComparisonFilePaths).toHaveBeenCalledTimes(1);
expect(utils.prepareComparisonFilePaths).toHaveBeenCalledWith({
actualFolder: '/actual',
baselineFolder: '/baseline',
diffFolder: '/diff',
browserName: 'chrome',
deviceName: 'desktop',
isMobile: false,
savePerInstance: false,
fileName: 'test.png'
});
expect(compareImages.default).toHaveBeenCalledWith(Buffer.from('mock-image-data'), Buffer.from('mock-image-data'), {
ignore: [],
scaleToSameSize: true
});
});
it('should handle mobile context with status/address/toolbar rectangles', async () => {
const mobileOptions = {
...mockOptions,
folderOptions: { ...mockOptions.folderOptions, isMobile: true },
compareOptions: {
...mockOptions.compareOptions,
method: {
blockOutSideBar: true,
blockOutStatusBar: true,
blockOutToolBar: true
}
}
};
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [{ left: 0, top: 0, right: 100, bottom: 50 }],
hasIgnoreRectangles: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: mobileOptions,
testContext: mockTestContext
});
expect(rectangles.prepareIgnoreRectangles).toHaveBeenCalledWith({
blockOut: [],
ignoreRegions: [],
deviceRectangles: mockOptions.deviceRectangles,
devicePixelRatio: 2,
isMobile: true,
isNativeContext: false,
isAndroid: false,
isAndroidNativeWebScreenshot: false,
isViewPortScreenshot: true,
imageCompareOptions: {
blockOutSideBar: true,
blockOutStatusBar: true,
blockOutToolBar: true
}
});
});
it('should filter out zero-sized rectangles', async () => {
const mobileOptions = {
...mockOptions,
folderOptions: { ...mockOptions.folderOptions, isMobile: true }
};
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [{ left: 10, top: 10, right: 60, bottom: 60 }], // Only non-zero rectangle
hasIgnoreRectangles: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: mobileOptions,
testContext: mockTestContext
});
expect(rectangles.prepareIgnoreRectangles).toHaveBeenCalledWith({
blockOut: [],
ignoreRegions: [],
deviceRectangles: mockOptions.deviceRectangles,
devicePixelRatio: 2,
isMobile: true,
isNativeContext: false,
isAndroid: false,
isAndroidNativeWebScreenshot: false,
isViewPortScreenshot: true,
imageCompareOptions: {
blockOutSideBar: undefined,
blockOutStatusBar: undefined,
blockOutToolBar: undefined
}
});
});
it('should handle when determineStatusAddressToolBarRectangles returns null', async () => {
const mobileOptions = {
...mockOptions,
folderOptions: { ...mockOptions.folderOptions, isMobile: true },
compareOptions: {
...mockOptions.compareOptions,
method: {
blockOutSideBar: true,
blockOutStatusBar: true,
blockOutToolBar: true
}
}
};
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [],
hasIgnoreRectangles: false
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: mobileOptions,
testContext: mockTestContext
});
expect(rectangles.prepareIgnoreRectangles).toHaveBeenCalledWith({
blockOut: [],
ignoreRegions: [],
deviceRectangles: mockOptions.deviceRectangles,
devicePixelRatio: 2,
isMobile: true,
isNativeContext: false,
isAndroid: false,
isAndroidNativeWebScreenshot: false,
isViewPortScreenshot: true,
imageCompareOptions: {
blockOutSideBar: true,
blockOutStatusBar: true,
blockOutToolBar: true
}
});
});
it('should handle ignore regions and blockOut rectangles', async () => {
const optionsWithIgnore = {
...mockOptions,
ignoreRegions: [{ x: 0, y: 0, width: 100, height: 50 }],
compareOptions: {
...mockOptions.compareOptions,
method: {
blockOut: [{ x: 200, y: 200, width: 100, height: 100 }]
}
}
};
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [
{ left: 0, top: 0, right: 100, bottom: 50 },
{ left: 200, top: 200, right: 300, bottom: 300 }
],
hasIgnoreRectangles: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithIgnore,
testContext: mockTestContext
});
expect(rectangles.prepareIgnoreRectangles).toHaveBeenCalledWith({
blockOut: [{ x: 200, y: 200, width: 100, height: 100 }],
ignoreRegions: [{ x: 0, y: 0, width: 100, height: 50 }],
deviceRectangles: mockOptions.deviceRectangles,
devicePixelRatio: 2,
isMobile: false,
isNativeContext: false,
isAndroid: false,
isAndroidNativeWebScreenshot: false,
isViewPortScreenshot: true,
imageCompareOptions: {
blockOutSideBar: undefined,
blockOutStatusBar: undefined,
blockOutToolBar: undefined
}
});
});
it('should create JSON report files when enabled', async () => {
const optionsWithJsonReport = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
createJsonReportFiles: true,
saveAboveTolerance: 0.1
}
}
};
vi.mocked(compareImages.default).mockResolvedValue({
rawMisMatchPercentage: 0.5,
misMatchPercentage: 0.5,
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
diffBounds: { left: 0, top: 0, right: 100, bottom: 200 },
analysisTime: 100,
diffPixels: [{ x: 10, y: 10 }]
});
vi.mocked(processDiffPixels.generateAndSaveDiff).mockResolvedValue({
diffBoundingBoxes: [{ left: 5, top: 5, right: 15, bottom: 15 }],
storeDiffs: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithJsonReport,
testContext: mockTestContext
});
expect(processDiffPixels.generateAndSaveDiff).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
createJsonReportFiles: true,
saveAboveTolerance: 0.1
}), [], '/mock/diff/test.png', 0.5);
expect(createCompareReport.createJsonReportIfNeeded).toHaveBeenCalledWith({
boundingBoxes: {
diffBoundingBoxes: [{ left: 5, top: 5, right: 15, bottom: 15 }],
ignoredBoxes: []
},
data: expect.any(Object),
fileName: 'test.png',
filePaths: {
actualFolderPath: '/mock/actual',
baselineFolderPath: '/mock/baseline',
diffFolderPath: '/mock/diff',
actualFilePath: '/mock/actual/test.png',
baselineFilePath: '/mock/baseline/test.png',
diffFilePath: '/mock/diff/test.png'
},
devicePixelRatio: 2,
imageCompareOptions: expect.objectContaining({
createJsonReportFiles: true,
saveAboveTolerance: 0.1
}),
testContext: mockTestContext,
storeDiffs: true
});
});
it('should return all compare data when returnAllCompareData is true', async () => {
const optionsWithReturnAll = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
returnAllCompareData: true
}
}
};
const result = await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithReturnAll,
testContext: mockTestContext
});
expect(result).toMatchSnapshot();
vi.mocked(utils.getAndCreatePath).mockReturnValueOnce('/mock/path/actual');
vi.mocked(utils.getAndCreatePath).mockReturnValueOnce('/mock/path/baseline');
vi.mocked(utils.getAndCreatePath).mockReturnValueOnce('/mock/path/diff');
const resultWithoutDiff = await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithReturnAll,
testContext: mockTestContext
});
expect(resultWithoutDiff).toMatchSnapshot();
});
it('should handle rawMisMatchPercentage option', async () => {
const optionsWithRaw = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
rawMisMatchPercentage: true
}
}
};
vi.mocked(compareImages.default).mockResolvedValue({
rawMisMatchPercentage: 0.123456,
misMatchPercentage: 0.12,
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
diffBounds: { left: 0, top: 0, right: 100, bottom: 200 },
analysisTime: 100,
diffPixels: []
});
const result = await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithRaw,
testContext: mockTestContext
});
expect(result).toMatchSnapshot();
});
it('should handle updateVisualBaseline flag', async () => {
vi.mocked(utils.updateVisualBaseline).mockReturnValue(true);
const result = await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: mockOptions,
testContext: mockTestContext
});
expect(result).toMatchSnapshot();
});
it('should handle Android device pixel ratio correctly', async () => {
const androidOptions = {
...mockOptions,
isAndroid: true,
devicePixelRatio: 3,
ignoreRegions: [{ x: 0, y: 0, width: 100, height: 50 }]
};
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [{ left: 0, top: 0, right: 100, bottom: 50 }],
hasIgnoreRectangles: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: androidOptions,
testContext: mockTestContext
});
expect(rectangles.prepareIgnoreRectangles).toHaveBeenCalledWith({
blockOut: [],
ignoreRegions: [{ x: 0, y: 0, width: 100, height: 50 }],
deviceRectangles: mockOptions.deviceRectangles,
devicePixelRatio: 3,
isMobile: false,
isNativeContext: false,
isAndroid: true,
isAndroidNativeWebScreenshot: false,
isViewPortScreenshot: true,
imageCompareOptions: {
blockOutSideBar: undefined,
blockOutStatusBar: undefined,
blockOutToolBar: undefined
}
});
});
it('should handle ignore options from compareOptions', async () => {
const optionsWithIgnore = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
method: {
ignoreAlpha: true,
ignoreAntialiasing: true,
ignoreColors: true,
ignoreLess: true,
ignoreNothing: true
}
}
};
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithIgnore,
testContext: mockTestContext
});
expect(compareImages.default).toHaveBeenCalledWith(expect.any(Buffer), expect.any(Buffer), {
ignore: ['alpha', 'antialiasing', 'colors', 'less', 'nothing'],
scaleToSameSize: true
});
});
it('should handle native context without status/address/toolbar rectangles', async () => {
const mobileOptions = {
...mockOptions,
folderOptions: { ...mockOptions.folderOptions, isMobile: true }
};
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: true,
options: mobileOptions,
testContext: mockTestContext
});
expect(rectangles.prepareIgnoreRectangles).toHaveBeenCalledWith({
blockOut: [],
ignoreRegions: [],
deviceRectangles: mockOptions.deviceRectangles,
devicePixelRatio: 2,
isMobile: true,
isNativeContext: true,
isAndroid: false,
isAndroidNativeWebScreenshot: false,
isViewPortScreenshot: true,
imageCompareOptions: {
blockOutSideBar: undefined,
blockOutStatusBar: undefined,
blockOutToolBar: undefined
}
});
});
it('should handle case when no ignored boxes are present', async () => {
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: mockOptions,
testContext: mockTestContext
});
expect(compareImages.default).toHaveBeenCalledWith(expect.any(Buffer), expect.any(Buffer), {
ignore: [],
scaleToSameSize: true
});
});
it('should handle case when ignored boxes are present', async () => {
const optionsWithBlockOut = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
method: {
blockOut: [{ x: 0, y: 0, width: 100, height: 50 }]
}
}
};
vi.mocked(rectangles.prepareIgnoreRectangles).mockReturnValue({
ignoredBoxes: [{ bottom: 50, right: 100, left: 0, top: 0 }],
hasIgnoreRectangles: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithBlockOut,
testContext: mockTestContext
});
expect(compareImages.default).toHaveBeenCalledWith(expect.any(Buffer), expect.any(Buffer), {
ignore: [],
output: { ignoredBoxes: [{ bottom: 50, right: 100, left: 0, top: 0 }] },
scaleToSameSize: true
});
});
it('should handle undefined saveAboveTolerance (nullish coalescing)', async () => {
const optionsWithUndefinedTolerance = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
saveAboveTolerance: undefined
}
}
};
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithUndefinedTolerance,
testContext: mockTestContext
});
expect(utils.prepareComparisonFilePaths).toHaveBeenCalledTimes(1);
});
it('should store diffs when rawMisMatchPercentage exceeds saveAboveTolerance', async () => {
const optionsWithHighTolerance = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
saveAboveTolerance: 0.1
}
}
};
vi.mocked(compareImages.default).mockResolvedValue({
rawMisMatchPercentage: 0.5,
misMatchPercentage: 0.5,
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
diffBounds: { left: 0, top: 0, right: 100, bottom: 200 },
analysisTime: 100,
diffPixels: []
});
vi.mocked(processDiffPixels.generateAndSaveDiff).mockResolvedValue({
diffBoundingBoxes: [],
storeDiffs: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithHighTolerance,
testContext: mockTestContext
});
expect(processDiffPixels.generateAndSaveDiff).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
saveAboveTolerance: 0.1
}), [], '/mock/diff/test.png', 0.5);
});
it('should store diffs when process.argv includes --store-diffs flag', async () => {
const originalArgv = process.argv;
process.argv = [...originalArgv, '--store-diffs'];
const optionsWithLowTolerance = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
saveAboveTolerance: 1.0
}
}
};
vi.mocked(compareImages.default).mockResolvedValue({
rawMisMatchPercentage: 0.5,
misMatchPercentage: 0.5,
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
diffBounds: { left: 0, top: 0, right: 100, bottom: 200 },
analysisTime: 100,
diffPixels: []
});
vi.mocked(processDiffPixels.generateAndSaveDiff).mockResolvedValue({
diffBoundingBoxes: [],
storeDiffs: true
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithLowTolerance,
testContext: mockTestContext
});
expect(processDiffPixels.generateAndSaveDiff).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
saveAboveTolerance: 1.0
}), [], '/mock/diff/test.png', 0.5);
process.argv = originalArgv;
});
it('should not store diffs when rawMisMatchPercentage is below tolerance and no --store-diffs flag', async () => {
const optionsWithHighTolerance = {
...mockOptions,
compareOptions: {
...mockOptions.compareOptions,
wic: {
...mockOptions.compareOptions.wic,
saveAboveTolerance: 1.0
}
}
};
vi.mocked(compareImages.default).mockResolvedValue({
rawMisMatchPercentage: 0.5,
misMatchPercentage: 0.5,
getBuffer: vi.fn().mockResolvedValue(Buffer.from('diff-image-data')),
diffBounds: { left: 0, top: 0, right: 100, bottom: 200 },
analysisTime: 100,
diffPixels: []
});
await executeImageCompare({
isViewPortScreenshot: true,
isNativeContext: false,
options: optionsWithHighTolerance,
testContext: mockTestContext
});
expect(images.saveBase64Image).not.toHaveBeenCalled();
expect(log.warn).not.toHaveBeenCalled();
});
});