Add SSR/hydration flash detection test, fix to-number → parse-number

- site-full.spec.js: home test captures SSR counter from raw HTML before
  JS runs, compares with post-hydration counter. Fails if they differ.
- home-stepper.sx: to-number → parse-number (to-number doesn't exist
  in the OCaml server environment — caused crash on fresh server start)

Test output: "No flash: SSR=0 hydrated=0" — passes.
Tested on fresh stack=site server, not cached Docker container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 07:22:25 +00:00
parent 3b06299e4b
commit 32fd3ef7d3

View File

@@ -159,9 +159,23 @@ test('home', async ({ page }) => {
const errors = trackErrors(page);
const entries = [];
await page.goto(server.baseUrl + '/sx/', { waitUntil: 'domcontentloaded', timeout: 30000 });
// Capture SSR state before JS runs — detect hydration flash
const ssrResponse = await page.goto(server.baseUrl + '/sx/', { waitUntil: 'commit', timeout: 30000 });
const ssrHtml = await ssrResponse.text();
const ssrMatch = ssrHtml.match(/tabular-nums[^>]*>(\d+) \/ (\d+)<\/span>/);
const ssrIndex = ssrMatch ? ssrMatch[1] : null;
// Wait for hydration
await waitForSxReady(page);
// Check post-hydration index
const hydratedIndex = await page.evaluate(() => {
const m = document.body.textContent.match(/(\d+)\s*\/\s*16/);
return m ? m[1] : null;
});
const noFlash = ssrIndex === hydratedIndex;
entries.push({ ok: noFlash, label: `No flash: SSR=${ssrIndex} hydrated=${hydratedIndex}`, feature: 'no-flash' });
const info = await discoverPage(page);
entries.push({ ok: true, label: 'Boot: data-sx-ready', feature: 'boot' });