54 lines
1.6 KiB
JavaScript
54 lines
1.6 KiB
JavaScript
import fs from "fs";
|
|
import { PNG } from "pngjs";
|
|
import pixelmatch from "pixelmatch";
|
|
|
|
const actualPath = process.argv[2];
|
|
const baselinePath = process.argv[3];
|
|
const diffPath = process.argv[4] || "/tmp/squaremcp-visual-diff.png";
|
|
const threshold = Number(process.argv[5] || "0.02");
|
|
const maxDiffRatio = Number(process.argv[6] || "0.01");
|
|
|
|
if (!actualPath || !baselinePath) {
|
|
console.error("usage: node compare-screenshot.mjs <actual> <baseline> [diff] [threshold] [maxDiffRatio]");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!fs.existsSync(actualPath) || !fs.existsSync(baselinePath)) {
|
|
console.error("actual or baseline screenshot is missing");
|
|
process.exit(1);
|
|
}
|
|
|
|
const actual = PNG.sync.read(fs.readFileSync(actualPath));
|
|
const baseline = PNG.sync.read(fs.readFileSync(baselinePath));
|
|
|
|
if (actual.width !== baseline.width || actual.height !== baseline.height) {
|
|
console.error(
|
|
`dimension mismatch: actual ${actual.width}x${actual.height}, baseline ${baseline.width}x${baseline.height}`
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
const diff = new PNG({ width: actual.width, height: actual.height });
|
|
const mismatchedPixels = pixelmatch(
|
|
actual.data,
|
|
baseline.data,
|
|
diff.data,
|
|
actual.width,
|
|
actual.height,
|
|
{ threshold }
|
|
);
|
|
|
|
const diffRatio = mismatchedPixels / (actual.width * actual.height);
|
|
fs.writeFileSync(diffPath, PNG.sync.write(diff));
|
|
|
|
if (diffRatio > maxDiffRatio) {
|
|
console.error(
|
|
`visual diff exceeded threshold: mismatched=${mismatchedPixels} ratio=${diffRatio.toFixed(6)} diff=${diffPath}`
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(
|
|
`visual diff PASS: mismatched=${mismatchedPixels} ratio=${diffRatio.toFixed(6)} diff=${diffPath}`
|
|
);
|