import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; import path from 'node:path'; import test from 'node:test'; import vm from 'node:vm'; const root = path.resolve(import.meta.dirname, '..'); async function read(relativePath) { return fs.readFile(path.join(root, relativePath), 'utf8'); } async function clientSources() { const directory = path.join(root, 'public/js'); const names = await fs.readdir(directory); return Promise.all(names.filter(name => name.endsWith('.js')).map(name => read(`public/js/${name}`))); } test('every static HTML id is referenced by authored client JavaScript', async () => { const html = await read('public/index.html'); const scripts = (await clientSources()).join('\n'); const ids = [...html.matchAll(/\bid="([^"]+)"/g)].map(match => match[1]); assert.ok(ids.length > 0); for (const id of ids) { assert.match(scripts, new RegExp(id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')), `unused id: ${id}`); } }); test('every static HTML class is used by CSS or authored JavaScript', async () => { const html = await read('public/index.html'); const css = `${await read('public/css/bootstrap.css')}\n${await read('public/css/app.css')}`; const scripts = (await clientSources()).join('\n'); const classes = [...html.matchAll(/\bclass="([^"]+)"/g)] .flatMap(match => match[1].split(/\s+/)) .filter(Boolean); for (const className of new Set(classes)) { assert.ok(css.includes(className) || scripts.includes(className), `unused class: ${className}`); } }); test('application actions use pointer events instead of click handlers', async () => { const scripts = (await Promise.all([ read('public/js/app.js'), read('public/js/slideshow.js'), read('public/js/bootstrap-loader.js') ])).join('\n'); assert.match(scripts, /pointerup/); assert.match(scripts, /keydown/); assert.doesNotMatch(scripts, /(?:\.on|addEventListener)\(\s*['"]click['"]/); }); test('mobile navigation footer contains a rotating SVG arrow and exact metre output', async () => { const html = await read('public/index.html'); const app = await read('public/js/app.js'); const css = await read('public/css/app.css'); assert.match(html, /