// tests/playground/voyage-playground.test.mjs // Filesystem + content tests for v4.2 voyage playground. // Pure existence + grep checks — no browser launch. import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { existsSync, statSync, readFileSync, readdirSync } from 'node:fs'; import { dirname, resolve, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = resolve(__dirname, '..', '..'); const PLAYGROUND = join(ROOT, 'playground'); const HTML = join(PLAYGROUND, 'voyage-playground.html'); const VENDOR = join(PLAYGROUND, 'vendor', 'playground-design-system'); const MANIFEST = join(VENDOR, 'MANIFEST.json'); test('voyage-playground.html exists and has nonzero size', () => { assert.ok(existsSync(HTML), 'voyage-playground.html must exist'); assert.ok(statSync(HTML).size > 0, 'must have content'); }); test('voyage-playground.html has DOCTYPE + html closing tag', () => { const text = readFileSync(HTML, 'utf-8'); assert.match(text, /^/i); assert.match(text, /<\/html>\s*$/); }); test('voyage-playground.html does NOT contain external (http/https) URLs', () => { // SC1 zero-network constraint: all assets must be relative to ./vendor/ const text = readFileSync(HTML, 'utf-8'); assert.ok(!/https?:\/\//.test(text), 'no external URLs allowed in playground HTML'); }); test('voyage-playground.html does NOT contain literal `marked` (renderer ban per risk-assessor H1)', () => { const text = readFileSync(HTML, 'utf-8'); // marked is disqualified by issue #3515; markdown-it locked instead // Allow comments mentioning "marked" as an explanatory artifact, but no actual import paths assert.ok(!/from ['"].*marked/.test(text), 'no import from marked'); assert.ok(!/]*marked\.min\.js/.test(text), 'no marked script tag'); }); test('voyage-playground.html includes skip-to-main link (A11Y baseline)', () => { const text = readFileSync(HTML, 'utf-8'); assert.match(text, /Skip to main content/); }); test('voyage-playground.html declares aria-live region', () => { const text = readFileSync(HTML, 'utf-8'); assert.match(text, /aria-live="polite"/); }); test('playground/vendor/playground-design-system/MANIFEST.json exists and parses as JSON with expected keys', () => { assert.ok(existsSync(MANIFEST), 'MANIFEST.json must be present from sync-design-system.mjs'); const obj = JSON.parse(readFileSync(MANIFEST, 'utf-8')); assert.ok(obj.source_commit, 'source_commit field required'); assert.ok(obj.sync_date, 'sync_date field required'); assert.ok(obj.files && typeof obj.files === 'object', 'files map required'); }); test('playground/vendor/playground-design-system/ contains expected DS files', () => { const files = readdirSync(VENDOR); for (const expected of ['tokens.css', 'base.css', 'components.css', 'fonts.css', 'print.css']) { assert.ok(files.includes(expected), `${expected} expected in vendor/`); } assert.ok(files.includes('fonts'), 'fonts/ subdirectory expected'); });