// Run: node scripts/shader-pixel-regression.mjs // Optional: SHADER_BROWSER=/path/to/firefox SHADER_TIMEOUT_MS=180000 // Chromium: SHADER_BROWSER=/path/to/chrome-headless-shell SHADER_BROWSER_KIND=chromium // Hosts without user namespaces: SHADER_NO_SANDBOX=1 (local test content only). // No automation dependency: the browser POSTs WebGL readback results to a loopback Vite server. // Also checks that prepared packs create no new programs through reveal and restart. import { spawn } from 'node:child_process' import { once } from 'node:events' import { readFile, mkdir, writeFile, rm } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { createServer } from 'vite' import { originalFragment, baselineFragment, referenceCommit, referenceHash } from './shader-wear-reference.mjs' const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') const profile = resolve(root, 'scripts', `.shader-browser-${process.pid}`) const timeoutMs = Number(process.env.SHADER_TIMEOUT_MS ?? 180000) if (!Number.isFinite(timeoutMs) || timeoutMs < 1000 || timeoutMs > 600000) { throw new Error('SHADER_TIMEOUT_MS must be between 1000 and 600000') } const fragmentShader = originalFragment(await readFile(resolve(root, 'src/cardMaterial.ts'), 'utf8')) const baselineShader = baselineFragment(root) let resolveResult let rejectResult const result = new Promise((resolve, reject) => { resolveResult = resolve rejectResult = reject }) // Attach a handler immediately; startup errors can precede awaiting the result. result.catch(() => {}) let server let browser let timer try { await mkdir(profile) await writeFile(resolve(profile, 'user.js'), [ 'user_pref("webgl.force-enabled", true);', 'user_pref("webgl.disabled", false);', 'user_pref("browser.shell.checkDefaultBrowser", false);', 'user_pref("browser.startup.homepage_override.mstone", "ignore");', 'user_pref("datareporting.policy.dataSubmissionEnabled", false);', ].join('\n')) server = await createServer({ root, configFile: false, cacheDir: resolve(profile, 'vite'), logLevel: 'error', server: { host: '127.0.0.1', port: 0, watch: null }, plugins: [{ name: 'shader-pixel-regression', configureServer(vite) { vite.middlewares.use((req, res, next) => { if (req.url === '/__shader_reference') { res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ fragmentShader, baselineShader })) } else if (req.url === '/__shader_pixels' && req.method === 'POST') { let body = '' req.on('data', (chunk) => { body += chunk if (body.length > 2_000_000) req.destroy() }) req.on('end', () => { try { resolveResult(JSON.parse(body)) res.end('ok') } catch (error) { rejectResult(error) res.statusCode = 400 res.end('Invalid JSON') } }) } else if (req.url === '/__shader_test') { res.setHeader('Content-Type', 'text/html') res.end('