diff --git a/test.js b/test.js new file mode 100644 index 0000000..9e4999a --- /dev/null +++ b/test.js @@ -0,0 +1,150 @@ +#!/usr/bin/env node +/** + * Nexus Test Harness + * Validates the scene loads without errors using only Node.js built-ins. + * Run: node test.js + */ + +import { execSync } from 'child_process'; +import { readFileSync, statSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +let passed = 0; +let failed = 0; + +function pass(name) { + console.log(` ✓ ${name}`); + passed++; +} + +function fail(name, reason) { + console.log(` ✗ ${name}`); + if (reason) console.log(` → ${reason}`); + failed++; +} + +function section(name) { + console.log(`\n${name}`); +} + +// ── Syntax checks ────────────────────────────────────────────────────────── +section('JS Syntax'); + +for (const file of ['app.js', 'ws-client.js']) { + try { + execSync(`node --check ${resolve(__dirname, file)}`, { stdio: 'pipe' }); + pass(`${file} parses without syntax errors`); + } catch (e) { + fail(`${file} syntax check`, e.stderr?.toString().trim() || e.message); + } +} + +// ── File size budget ──────────────────────────────────────────────────────── +section('File Size Budget (< 500 KB)'); + +for (const file of ['app.js', 'ws-client.js']) { + try { + const bytes = statSync(resolve(__dirname, file)).size; + const kb = (bytes / 1024).toFixed(1); + if (bytes < 500 * 1024) { + pass(`${file} is ${kb} KB`); + } else { + fail(`${file} exceeds 500 KB budget`, `${kb} KB`); + } + } catch (e) { + fail(`${file} size check`, e.message); + } +} + +// ── JSON validation ───────────────────────────────────────────────────────── +section('JSON Files'); + +for (const file of ['manifest.json', 'portals.json', 'vision.json']) { + try { + const raw = readFileSync(resolve(__dirname, file), 'utf8'); + JSON.parse(raw); + pass(`${file} is valid JSON`); + } catch (e) { + fail(`${file}`, e.message); + } +} + +// ── HTML structure ────────────────────────────────────────────────────────── +section('HTML Structure (index.html)'); + +const html = (() => { + try { return readFileSync(resolve(__dirname, 'index.html'), 'utf8'); } + catch (e) { fail('index.html readable', e.message); return ''; } +})(); + +if (html) { + const checks = [ + ['DOCTYPE declaration', //i], + [' attribute', /]+lang=/i], + ['charset meta tag', /]+charset/i], + ['viewport meta tag', /]+viewport/i], + ['