- playwright.config.visual.ts: dedicated config, viewport 1280x720, Chromium only, snapshots in e2e/tests/visual/__snapshots__ - e2e/tests/visual/visual-regression.spec.ts: login, register, dashboard (full/header/sidebar), player bar, playlists, 404, mobile/tablet viewports; dark theme + reduceMotion - scripts/visual-diff.js: optional pixelmatch script to generate diff image from two PNGs - docs/VISUAL_TESTING_STRATEGY.md: strategy, commands, CI, workflow - npm scripts: test:visual, test:visual:update, test:visual:report - deps: pixelmatch, pngjs; @playwright/test aligned to 1.58.1 - baseline snapshots added for login, dashboard, playlists, 404, viewports Co-authored-by: Cursor <cursoragent@cursor.com>
63 lines
2 KiB
JavaScript
63 lines
2 KiB
JavaScript
#!/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);
|
|
}
|