import { readFile } from 'node:fs/promises'; import assert from 'node:assert/strict'; import test from 'node:test'; const html = await readFile(new URL('../index.html', import.meta.url), 'utf8'); const manifest = JSON.parse( await readFile(new URL('../data/manifest.json', import.meta.url), 'utf8') ); const dashboardJs = await readFile(new URL('../js/dashboard.js', import.meta.url), 'utf8'); function idsInMarkup() { return [...html.matchAll(/\sid=["']([^"']+)["']/g)].map(match => match[1]); } test('dashboard keeps the required shell element ids unique', () => { const ids = idsInMarkup(); const requiredIds = [ 'clockDisplay', 'dateDisplay', 'lastUpdated', 'globalStatus', 'health', 'healthBadge', 'serverDot', 'webDot', 'webStatus', 'emacsDot', 'emacsStatus', 'uptimeStat', 'uptimeDays', 'uptimeHours', 'uptimeMins', 'uptimeStart', 'uptimePct', 'uptimeFill', 'pingHistory', 'quickLinks', 'widgetGrid', 'buildGrid', 'dailyMessage', 'countdownLabel', 'countdownDisplay', 'cd-days', 'cd-hours', 'cd-mins', 'cd-secs', 'modalOverlay', 'modalTitle', 'modalBody', 'modalCancel', 'modalConfirm' ]; for (const id of requiredIds) { assert.ok(ids.includes(id), `missing #${id}`); } const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index); assert.deepEqual(duplicates, []); }); test('manifest v2 defines integrations and widgets', () => { assert.equal(manifest.version, 2); assert.ok(manifest.integrations?.authoring); assert.ok(Array.isArray(manifest.widgets) && manifest.widgets.length >= 3); assert.ok(manifest.widgets.some(w => w.id === 'scripts-health')); }); test('manifest defines unique link and build ids', () => { const linkIds = manifest.links.map(l => l.id); const buildIds = manifest.builds.map(b => b.id); assert.equal(new Set(linkIds).size, linkIds.length); assert.equal(new Set(buildIds).size, buildIds.length); assert.ok(manifest.links.some(l => l.id === 'timesheet' && l.href === '/time-tracking/')); assert.ok(manifest.builds.some(b => b.id === 'org_web')); assert.ok(manifest.builds.some(b => b.id === 'emacs')); assert.ok(manifest.builds.some(b => b.id === 'adventure_resources')); assert.ok(manifest.builds.some(b => b.id === 'guacamole_start')); assert.ok(manifest.builds.some(b => b.id === 'guacamole_stop')); assert.ok(manifest.builds.some(b => b.id === 'nostalgia_prod')); }); test('manifest build cards expose stable dom ids', () => { for (const build of manifest.builds) { assert.ok(build.id, `build needs id`); if (build.id === 'org_web') { assert.equal(build.start.path, '/api/build-web'); assert.equal(build.logs.path, '/api/build-web/logs'); assert.equal(build.lastRunKey, 'org_web'); } if (build.id === 'emacs') { assert.equal(build.start.path, '/api/rerun-emacs'); assert.equal(build.lastRunKey, 'emacs'); } if (build.id === 'adventure_resources') { assert.equal(build.start.path, '/api/adventure/resources/free'); assert.equal(build.lastRunKey, 'adventure_resources'); } if (build.id === 'guacamole_start') { assert.equal(build.start.path, '/api/guacamole/start'); assert.equal(build.lastRunKey, 'guacamole_start'); } if (build.id === 'guacamole_stop') { assert.equal(build.start.path, '/api/guacamole/stop'); assert.equal(build.lastRunKey, 'guacamole_stop'); } if (build.id === 'nostalgia_prod') { assert.equal(build.start.path, '/api/nostalgia/production'); assert.equal(build.lastRunKey, 'nostalgia_prod'); } } }); test('dashboard module references manifest-driven API endpoints', () => { const expectedEndpoints = [ '/api/health', '/api/uptime', '/api/last-runs', '/api/build-web', '/api/build-web/logs', '/api/build-web/status', '/api/rerun-emacs', '/api/rerun-emacs/logs', '/api/rerun-emacs/status', '/api/adventure/resources/free', '/api/adventure/resources/free/logs', '/api/adventure/resources/free/status', '/api/guacamole/start', '/api/guacamole/start/logs', '/api/guacamole/start/status', '/api/guacamole/stop', '/api/guacamole/stop/logs', '/api/guacamole/stop/status', '/api/nostalgia/production', '/api/nostalgia/production/logs', '/api/nostalgia/production/status' ]; for (const endpoint of expectedEndpoints) { assert.ok( manifest.builds.some(b => b.start?.path === endpoint || b.status?.path === endpoint || b.logs?.path === endpoint ) || Object.values(manifest.status).includes(endpoint) || dashboardJs.includes(endpoint), `missing endpoint ${endpoint}` ); } assert.match(dashboardJs, /new EventSource\(url\)/); assert.match(dashboardJs, /addEventListener\(['"]log['"]/); assert.match(dashboardJs, /\/api\/zone\/manifest/); assert.match(dashboardJs, /data\/manifest\.json/); assert.match(dashboardJs, /\/api\/zone\/status/); assert.match(dashboardJs, /renderWidgets/); assert.match(dashboardJs, /buildDomIds/); assert.doesNotMatch(dashboardJs, /build-roam/); assert.doesNotMatch(dashboardJs, /run-combined/); }); test('layout classes and accessible structure are present', () => { for (const className of ['topbar', 'dashboard-grid', 'rail', 'panel', 'run-grid']) { assert.match(html, new RegExp(`class=["'][^"']*\\b${className}\\b`), `missing .${className}`); } assert.match(dashboardJs, /log-tools/); assert.match(html, /
{ assert.match(html, /href=["']css\/shared\.css["']/); assert.match(html, /href=["']css\/dashboard\.css["']/); assert.match(html, /type=["']module["'][^>]*src=["']js\/dashboard\.js(\?[^"']*)?["']/); }); test('gated manifest schema has required keys', () => { for (const key of ['version', 'dashboard', 'links', 'builds', 'status']) { assert.ok(key in manifest, `manifest missing ${key}`); } assert.equal(typeof manifest.dashboard.title, 'string'); assert.ok(Array.isArray(manifest.links) && manifest.links.length > 0); assert.ok(Array.isArray(manifest.builds) && manifest.builds.length > 0); });