veza/apps/web/scripts/visual-diff.js

64 lines
2 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* Generate a pixel diff image between two PNGs using pixelmatch.
* Usage: node scripts/visual-diff.js <expected.png> <actual.png> [diff-output.png] [threshold]
*
* When Playwright fails a visual test it writes:
* - expected: test-results/.../expected-*.png
* - actual: test-results/.../actual-*.png
* You can run this script to get a single diff image (pixels that differ in red).
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import pixelmatch from 'pixelmatch';
import { PNG } from 'pngjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const expectedPath = process.argv[2];
const actualPath = process.argv[3];
const diffOutputPath = process.argv[4] || path.join(path.dirname(actualPath || ''), 'diff.png');
const threshold = parseFloat(process.argv[5] || '0.1', 10);
if (!expectedPath || !actualPath) {
console.error('Usage: node scripts/visual-diff.js <expected.png> <actual.png> [diff-output.png] [threshold]');
process.exit(1);
}
function loadPng(filePath) {
const data = fs.readFileSync(filePath);
return PNG.sync.read(data);
}
try {
const imgExpected = loadPng(expectedPath);
const imgActual = loadPng(actualPath);
if (imgExpected.width !== imgActual.width || imgExpected.height !== imgActual.height) {
console.error('Image dimensions differ:', imgExpected.width, 'x', imgExpected.height, 'vs', imgActual.width, 'x', imgActual.height);
process.exit(1);
}
const { width, height } = imgExpected;
const diff = new PNG({ width, height });
const numDiffPixels = pixelmatch(
imgExpected.data,
imgActual.data,
diff.data,
width,
height,
{ threshold }
);
fs.mkdirSync(path.dirname(diffOutputPath), { recursive: true });
fs.writeFileSync(diffOutputPath, PNG.sync.write(diff));
console.log(`Diff pixels: ${numDiffPixels}`);
console.log(`Diff image written: ${diffOutputPath}`);
process.exit(numDiffPixels > 0 ? 1 : 0);
} catch (err) {
console.error(err);
process.exit(1);
}