@lewiswright/vitest-plugin-vis
Version:
Vitest visual testing plugin
81 lines (75 loc) • 4.21 kB
JavaScript
import dedent from 'dedent';
import { resolve } from 'pathe';
import { isBase64String } from "../shared/base64.js";
import { compareImage } from "../shared/compare_image.js";
import { alignImagesToSameSize } from "./align_images.js";
import { toDataURL, toImageData } from "./image_data.js";
import { prettifyOptions } from "./image_snapshot_matcher.logic.js";
import { convertElementToCssSelector } from "./selector.js";
import { toTaskId } from "./task_id.js";
import { server } from "./vitest_browser_context_proxy.js";
export function imageSnapshotMatcher(commands) {
return async function matchImageSnapshot(test, subject, options) {
const isAutoSnapshot = !!test.meta.vis?.isAutoSnapshot;
const taskId = toTaskId(test);
const info = await commands.prepareImageSnapshotComparison(taskId, parseImageSnapshotSubject(subject), isAutoSnapshot, options?.customizeSnapshotId
? await parseImageSnapshotOptions(commands, taskId, isAutoSnapshot, options)
: options);
if (!info)
return;
options = { ...info, ...options };
const baselineImage = await toImageData(info.baseline);
const resultImage = await toImageData(info.result);
const [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage);
const { width, height } = baselineAlignedImage;
const diffImage = new ImageData(width, height);
const { pass, diffAmount } = compareImage(baselineAlignedImage.data, resultAlignedImage.data, diffImage.data, width, height, options);
if (pass) {
if (options?.expectToFail) {
throw new Error(dedent `Snapshot \`${taskId}\` matched but expected to fail.
Options: ${prettifyOptions(options)}
Diff: ${options.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}
Expected: ${resolve(info.projectRoot, info.baselinePath)}
Actual: ${resolve(info.projectRoot, info.resultPath)}`);
}
return;
}
if (server.config.snapshotOptions.updateSnapshot === 'all' && !options?.expectToFail) {
await writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage);
return;
}
await writeSnapshot(commands, resolve(info.projectRoot, info.diffPath), diffImage);
throw new Error(dedent `Snapshot \`${taskId}\` mismatched
${options?.failureThreshold
? options?.failureThresholdType === 'percent'
? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`
: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`
: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`}
Options: ${prettifyOptions(options)}
Expected: ${resolve(info.projectRoot, info.baselinePath)}
Actual: ${resolve(info.projectRoot, info.resultPath)}
Difference: ${resolve(info.projectRoot, info.diffPath)}`);
};
}
/**
* @deprecated internalized. Use `matchImageSnapshot` directly instead.
*/
export function parseImageSnapshotSubject(subject) {
if (subject instanceof Element)
return convertElementToCssSelector(subject);
if (subject?.['selector'])
return subject['selector'];
if (isBase64String(subject))
return subject;
throw new Error(`'toMatchImageSnapshot()' expects the subject to be an element, locator, or image encoded in base64 string, but got: ${subject}`);
}
async function writeSnapshot(commands, path, image) {
const content = (await toDataURL(image)).split(',')[1];
return commands.writeFile(path, content, { encoding: 'base64' });
}
async function parseImageSnapshotOptions(commands, taskId, isAutoSnapshot, options) {
const index = await commands.imageSnapshotNextIndex(taskId);
const { customizeSnapshotId, ...rest } = options;
const snapshotFileId = customizeSnapshotId({ id: taskId, index, isAutoSnapshot });
return { ...rest, snapshotFileId };
}