#!/usr/bin/env node
/* test-static-parity.mjs: "with JavaScript off, a compiled page renders its static styles correctly on first paint."
*
* The strongest fixture available is this site. Its pages are server-rendered by Next, so the HTML is complete before
* any script runs. For each page this compares two copies of that same markup, element by element (computed style
* for about 100 properties, plus the position and size of the box, plus ::before and ::after):
*
* A the engine as it ships: the .dssl sheets, applied by the runtime
* B the output of `dssl build --reset`, linked as one stylesheet, with JavaScript DISABLED
*
* They must match. The only things taken out first are the demo elements that exist to show features a build cannot
* keep (tilt, bend and stretch, overlap, gradient-flow), listed below. Everything else, thousands of elements, has to
* come out identical. It needs the site built (npm run build) and spawns `next start` itself.
*
* node scripts/test-static-parity.mjs [--route /docs] */
import { chromium } from 'playwright-core';
import { createServer } from 'node:http';
import { spawn, spawnSync } from 'node:child_process';
import { existsSync, readFileSync, mkdtempSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { findChrome, ROOT } from './lib/browser-harness.mjs';
let pass = 0, fail = 0;
const ok = (n, c, x) => { if (c) { pass++; console.log(` ok ${n}`); } else { fail++; console.log(`FAIL ${n}${x !== undefined ? '; ' + x : ''}`); } };
if (!existsSync(path.join(ROOT, '.next', 'BUILD_ID'))) { console.log(' skipped: the site is not built (run npm run build first)'); console.log('\n0 passed, 0 failed'); process.exit(0); }
if (!findChrome()) { console.log(' skipped: no Chrome found'); process.exit(0); }
// 1. build the stylesheet with the real CLI, from the same files the site loads
const tmp = mkdtempSync(path.join(tmpdir(), 'dssl-parity-'));
const SHEETS = ['tokens', 'site', 'home', 'docs', 'playground', 'changelog', 'bench'];
const built = spawnSync(process.execPath, ['--no-warnings', path.join(ROOT, 'cli/dssl.mjs'), 'build', ...SHEETS.map((s) => path.join(ROOT, 'public/styles', s + '.dssl')), '-o', path.join(tmp, 'site.css'), '--reset', '--sourcemap', '--strict'], { encoding: 'utf8' });
ok('the site\'s sheets build with --strict (no warnings)', built.status === 0, built.stdout + built.stderr);
const css = readFileSync(path.join(tmp, 'site.css'), 'utf8');
// 2. serve the site
const PORT = 3600 + Math.floor(Math.random() * 300);
const next = spawn(process.execPath, [path.join(ROOT, 'node_modules/next/dist/bin/next'), 'start', '-p', String(PORT)], { cwd: ROOT, stdio: 'ignore' });
const stop = () => { try { next.kill(); } catch { /* gone */ } };
process.on('exit', stop);
for (let i = 0; i < 60; i++) { try { if ((await fetch(`http://127.0.0.1:${PORT}/`)).ok) break; } catch { /* not yet */ } await new Promise((r) => setTimeout(r, 500)); }
// 3. the pages, as two variants
const strip = (h) => h.replace(/');
pages[`/b-${key}`] = base.replace(/