Files
rose-ash/tests/playwright/site-server.js
giles fecfc71e5f Add full site test suite: stack=site sandbox, per-page feature reports
New test infrastructure:
- site-server.js: shared OCaml HTTP server lifecycle (beforeAll/afterAll)
- site-full.spec.js: full site test suite, no Docker

Tests:
  home (7 features): boot, header island, stepper island, stepper click,
    SPA navigation, universal smoke, no console errors
  hyperscript (8 features): boot, HS element discovery, activation (8/8),
    toggle color on/off, count clicks, bounce add/wait/remove, smoke, errors
  geography: 12/12 pages render
  applications: 9/9 pages render
  tools: 5/5 pages render
  etc: 5/5 pages render
  SPA navigation: SKIPPED (link boosting not working yet)
  language: FAILS — /sx/(language.(spec.(explore.evaluator))) hangs (real bug)

Run: npx playwright test tests/playwright/site-full.spec.js
Run one: npx playwright test tests/playwright/site-full.spec.js -g "hyperscript"

Each test prints a feature report showing exactly what was verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 22:20:13 +00:00

53 lines
1.7 KiB
JavaScript

// Shared OCaml HTTP server lifecycle for Playwright tests.
// Starts sx_server.exe --http on a random port, waits for ready.
// One instance serves all tests in a spec file.
const { spawn } = require('child_process');
const path = require('path');
const PROJECT_ROOT = path.resolve(__dirname, '../..');
class SiteServer {
constructor() {
this.port = 49152 + Math.floor(Math.random() * 16000);
this.proc = null;
this._stderr = '';
}
async start() {
const serverBin = path.join(PROJECT_ROOT, 'hosts/ocaml/_build/default/bin/sx_server.exe');
this.proc = spawn(serverBin, ['--http', String(this.port)], {
cwd: PROJECT_ROOT,
env: { ...process.env, SX_PROJECT_DIR: PROJECT_ROOT, OCAMLRUNPARAM: 'b' },
stdio: ['ignore', 'pipe', 'pipe'],
});
this.proc.stderr.on('data', chunk => { this._stderr += chunk.toString(); });
this.proc.stdout.on('data', () => {});
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Server did not start within 30s\n' + this._stderr.slice(-500))), 30000);
this.proc.stderr.on('data', () => {
if (this._stderr.includes('Listening on port')) {
clearTimeout(timeout);
resolve();
}
});
this.proc.on('error', err => { clearTimeout(timeout); reject(err); });
this.proc.on('exit', code => { clearTimeout(timeout); reject(new Error('Server exited: ' + code)); });
});
}
get baseUrl() { return `http://localhost:${this.port}`; }
stop() {
if (this.proc && !this.proc.killed) {
this.proc.kill('SIGTERM');
setTimeout(() => { if (this.proc && !this.proc.killed) this.proc.kill('SIGKILL'); }, 2000);
}
}
}
module.exports = { SiteServer, PROJECT_ROOT };