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 [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}` );