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); // } // }); });