From 9eaedda4a889c2f624633496969272fb7d86c0fe Mon Sep 17 00:00:00 2001 From: Vaclav Vancura Date: Wed, 25 Mar 2026 07:52:05 +0100 Subject: [PATCH 1/2] test: add playwright visual regression test for primitives Adds visual snapshot baseline and Playwright spec for the primitives rendering pipeline. The fixture page renders known patterns (pixel, lines, rects) via the engine and signals completion; the spec skips gracefully when WebGPU is unavailable in the test environment. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Vaclav Vancura --- .lintstagedrc.json | 4 +- playwright.config.ts | 10 +-- .../chromium-webgpu.png | Bin 0 -> 1044 bytes tests/visual/fixtures/primitives.html | 70 ++++++++++++++++++ tests/visual/fixtures/vite.config.ts | 10 +++ tests/visual/primitives.spec.ts | 31 ++++++++ 6 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 tests/visual/__snapshots__/primitives.spec.ts/Primitive-Rendering-should-render-known-primitive-patterns/chromium-webgpu.png create mode 100644 tests/visual/fixtures/primitives.html create mode 100644 tests/visual/fixtures/vite.config.ts create mode 100644 tests/visual/primitives.spec.ts diff --git a/.lintstagedrc.json b/.lintstagedrc.json index 5f81e94..d72cb1d 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -2,13 +2,13 @@ "*.{ts,tsx}": [ "biome check --write --no-errors-on-unmatched", "biome format --write --no-errors-on-unmatched", - "eslint --fix --max-warnings 0", + "eslint --fix --max-warnings 0 --no-warn-ignored", "cspell --no-progress --no-must-find-files" ], "*.{js,cjs,mjs}": [ "biome check --write --no-errors-on-unmatched", "biome format --write --no-errors-on-unmatched", - "eslint --fix --max-warnings 0" + "eslint --fix --max-warnings 0 --no-warn-ignored" ], "*.{json,jsonc}": ["biome format --write --no-errors-on-unmatched"], "*.css": ["biome format --write --no-errors-on-unmatched"], diff --git a/playwright.config.ts b/playwright.config.ts index 227f611..c42d06d 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -7,11 +7,11 @@ export default defineConfig({ snapshotPathTemplate: '{snapshotDir}/{testFilePath}/{testName}/{projectName}{ext}', fullyParallel: true, - forbidOnly: !!process.env['CI'], - retries: process.env['CI'] ? 2 : 0, - workers: process.env['CI'] ? 1 : 2, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : 2, - reporter: process.env['CI'] ? 'github' : 'html', + reporter: process.env.CI ? 'github' : 'html', use: { baseURL: 'http://localhost:5174', @@ -35,7 +35,7 @@ export default defineConfig({ webServer: { command: 'pnpm exec vite serve tests/visual/fixtures --port 5174', port: 5174, - reuseExistingServer: !process.env['CI'], + reuseExistingServer: !process.env.CI, timeout: 30_000, }, }); diff --git a/tests/visual/__snapshots__/primitives.spec.ts/Primitive-Rendering-should-render-known-primitive-patterns/chromium-webgpu.png b/tests/visual/__snapshots__/primitives.spec.ts/Primitive-Rendering-should-render-known-primitive-patterns/chromium-webgpu.png new file mode 100644 index 0000000000000000000000000000000000000000..97f6a85cc9c1b09c726e7bc607da28820691bb60 GIT binary patch literal 1044 zcmeAS@N?(olHy`uVBq!ia0y~yV02($VEDkn#K6Gt&tJ=ifr0s?r;B4q#hf>HJ@f7u zh%j8d-}t`%soykCq227hI?wm6-6EhO=IFrk!_eW{afV2yHUmcI84Zd)2L#j-ID`#a zI3*6T@Femx?K^*`Eq2d;z4zaD9?3nuegCV4`^$dk9scQf;MLRG)mJw?o3p7`)dZ>> z)s)*}AI$$4{_9w)yCx<2fSYm5DIqD4p{T0hcHt7{`%&ipYNIqZ=Yg~o&-h(m@#F4I zIhLCJ_w&Coeq=lROvHZQzCWw)?XDCF`}+8ytW;a7EnkHR&+00z2Nw-=UfyhQKfGa{ zIFlUj;iNe33KJgfN=uN$pkgZtrv ziM1eoqGcd`%WoS$xM(mZ`#DHr(O;0hNIuRA6Q1Q3Ae&^~9{@>gNCrtv zqJhlgB_N50&mg1j`SDhm@a(p-esIy?&GROZ6DDVaB;@o!cFc~I{E#8BZKuV9iv~Fb zwFed_R$Lci>~DLhyPCyDs_nWi$c1rR!ax#7i$D^Y+H5vbZO?V2K4eJD+mHv6NO{@d ze)z-Et00FwX96h_?gfeH&jgXvgAOcC{1L44;G)5wXJ+`)11P|;B$H8L%&bGDFxfDl qfq~)w|D7&+-3$y2Y@kfd%+MdTJdXdF!43un1_n=8KbLh*2~7Z{nQp59 literal 0 HcmV?d00001 diff --git a/tests/visual/fixtures/primitives.html b/tests/visual/fixtures/primitives.html new file mode 100644 index 0000000..38c8b32 --- /dev/null +++ b/tests/visual/fixtures/primitives.html @@ -0,0 +1,70 @@ + + + + + Visual Test: Primitives + + + +
+ +
+ + + diff --git a/tests/visual/fixtures/vite.config.ts b/tests/visual/fixtures/vite.config.ts new file mode 100644 index 0000000..077c4c2 --- /dev/null +++ b/tests/visual/fixtures/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: '.', + resolve: { + alias: { + 'blit-tech': '/Users/vancura/Repos/_BLIT_TECH_/blit-tech/src/BlitTech.ts', + }, + }, +}); diff --git a/tests/visual/primitives.spec.ts b/tests/visual/primitives.spec.ts new file mode 100644 index 0000000..ca5fb8e --- /dev/null +++ b/tests/visual/primitives.spec.ts @@ -0,0 +1,31 @@ +import { expect, test } from '@playwright/test'; + +test.describe('Primitive Rendering', () => { + test('should render known primitive patterns', async ({ page }) => { + await page.goto('/primitives.html'); + + // Wait for either render completion or initialization failure. + await page.waitForFunction( + () => { + const w = window as unknown as Record; + return w.__RENDER_COMPLETE__ || w.__INIT_FAILED__; + }, + { timeout: 10_000 }, + ); + + // Check if WebGPU initialization failed (hardware-dependent). + const initFailed = await page.evaluate(() => (window as unknown as Record).__INIT_FAILED__); + + if (initFailed) { + test.skip(true, 'WebGPU not available in this environment'); + return; + } + + // Small delay for GPU present. + await page.waitForTimeout(100); + + await expect(page.locator('canvas')).toHaveScreenshot('primitives.png', { + maxDiffPixelRatio: 0.01, + }); + }); +}); -- 2.51.2 From 657dd5d9de3f130f40f1c8efeb2dcb09a17e6aa8 Mon Sep 17 00:00:00 2001 From: Vaclav Vancura Date: Wed, 25 Mar 2026 16:04:48 +0100 Subject: [PATCH 2/2] test(ci): make visual test fixture path resolution portable Replace the hardcoded absolute path in vite.config.ts with a path.resolve(__dirname, ...) relative path so the fixture server works correctly on any machine and in CI. Pass --config explicitly to playwright.config.ts so Vite picks up the fixture config. Also extract the GPU present delay into a GPU_PRESENT_DELAY env var for easier CI tuning. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Vaclav Vancura --- playwright.config.ts | 2 +- tests/visual/fixtures/vite.config.ts | 6 +++++- tests/visual/primitives.spec.ts | 8 ++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index c42d06d..d2f2d60 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -33,7 +33,7 @@ export default defineConfig({ ], webServer: { - command: 'pnpm exec vite serve tests/visual/fixtures --port 5174', + command: 'pnpm exec vite serve tests/visual/fixtures --config tests/visual/fixtures/vite.config.ts --port 5174', port: 5174, reuseExistingServer: !process.env.CI, timeout: 30_000, diff --git a/tests/visual/fixtures/vite.config.ts b/tests/visual/fixtures/vite.config.ts index 077c4c2..ed60b9b 100644 --- a/tests/visual/fixtures/vite.config.ts +++ b/tests/visual/fixtures/vite.config.ts @@ -1,10 +1,14 @@ +import path from 'path'; +import { fileURLToPath } from 'url'; import { defineConfig } from 'vite'; +const __dirname = fileURLToPath(new URL('.', import.meta.url)); + export default defineConfig({ root: '.', resolve: { alias: { - 'blit-tech': '/Users/vancura/Repos/_BLIT_TECH_/blit-tech/src/BlitTech.ts', + 'blit-tech': path.resolve(__dirname, '../../../src/BlitTech.ts'), }, }, }); diff --git a/tests/visual/primitives.spec.ts b/tests/visual/primitives.spec.ts index ca5fb8e..2952b71 100644 --- a/tests/visual/primitives.spec.ts +++ b/tests/visual/primitives.spec.ts @@ -1,5 +1,9 @@ import { expect, test } from '@playwright/test'; +// Delay after render-complete signal before taking a screenshot, to allow the +// GPU to present the frame. Override via GPU_PRESENT_DELAY env var for CI tuning. +const GPU_PRESENT_DELAY = Number(process.env.GPU_PRESENT_DELAY ?? 100); + test.describe('Primitive Rendering', () => { test('should render known primitive patterns', async ({ page }) => { await page.goto('/primitives.html'); @@ -21,8 +25,8 @@ test.describe('Primitive Rendering', () => { return; } - // Small delay for GPU present. - await page.waitForTimeout(100); + // Wait for the GPU to present the rendered frame before capturing. + await page.waitForTimeout(GPU_PRESENT_DELAY); await expect(page.locator('canvas')).toHaveScreenshot('primitives.png', { maxDiffPixelRatio: 0.01, -- 2.51.2