test-card/tests/test-card.test.ts
Tomáš Mládek c30bffac73
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
test: rudimentary visual testing for test card
2025-09-27 14:14:27 +02:00

86 lines
2.6 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import puppeteer, { type Browser, type Page } from 'puppeteer';
import fs from 'fs';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';
import { startDevServer, stopDevServer } from './utils';
let browser: Browser;
let page: Page;
const baseTestPath = 'tests/output';
const screenshotPath = `${baseTestPath}/testcard-current.png`;
const baselinePath = `${baseTestPath}/testcard-baseline.png`;
const diffPath = `${baseTestPath}/testcard-diff.png`;
describe('Test Card', () => {
beforeAll(async () => {
await startDevServer(); // boot SvelteKit dev server
browser = await puppeteer.launch();
page = await browser.newPage();
await page.goto('http://localhost:5888/card');
await page.waitForNetworkIdle();
await page.addStyleTag({
content: '.clock { opacity: 0; } * { transition: none !important; }'
});
await page.setViewport({ width: 1920, height: 1080 });
await page.evaluate(() => {
return new Promise((resolve) => {
requestAnimationFrame(() => {
requestAnimationFrame(resolve);
});
});
});
await fs.promises.mkdir(baseTestPath, { recursive: true });
await page.screenshot({ path: screenshotPath });
}, 60000);
afterAll(async () => {
await browser.close();
await stopDevServer();
});
it('matches baseline (visual regression)', () => {
if (!fs.existsSync(baselinePath)) {
fs.copyFileSync(screenshotPath, baselinePath);
console.log('Baseline image created. Re-run tests.');
return;
}
const img1 = PNG.sync.read(fs.readFileSync(baselinePath));
const img2 = PNG.sync.read(fs.readFileSync(screenshotPath));
const { width, height } = img1;
expect(img2.width).toBe(width);
expect(img2.height).toBe(height);
const diff = new PNG({ width, height });
const mismatches = pixelmatch(img1.data, img2.data, diff.data, width, height, {
threshold: 0.1
});
if (mismatches > 0) {
fs.writeFileSync(diffPath, PNG.sync.write(diff));
}
expect(mismatches).toBe(0);
});
// it('has a black vertical line exactly in the center', () => {
// const img = PNG.sync.read(fs.readFileSync(screenshotPath));
// const { width, height, data } = img;
// const midX = Math.floor(width / 2);
// function getPixel(x: number, y: number) {
// const idx = (width * y + x) << 2;
// return { r: data[idx], g: data[idx + 1], b: data[idx + 2] };
// }
// for (let y = 0; y < height; y++) {
// const { r, g, b } = getPixel(midX, y);
// expect(r).toBeLessThan(20);
// expect(g).toBeLessThan(20);
// expect(b).toBeLessThan(20);
// }
// });
});