Admin: tool registry, grouped nav, and an /admin/ hub
This commit is contained in:
1 parent
7fc2b02e6a
commit
7040d4f078
9 files changed
+412
-7
No files matched your search
@@ -0,0 +1,58 @@
|
||||
/* check-admin-nav.mjs — every admin page must be reachable from the nav.
|
||||
*
|
||||
* node tools/check-admin-nav.mjs
|
||||
*
|
||||
* The A3 test rig shipped unreachable: the nav was a hand-maintained array of
|
||||
* filenames and adding testsheet.html to the folder did not add it to the bar.
|
||||
* Now the bar and the hub are both generated from TOOLS in admin.js, and this
|
||||
* checks the other direction — that the folder and the registry agree.
|
||||
*/
|
||||
import { readdirSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { TOOLS, GROUPS, nav, href } from '../public/admin/admin.js';
|
||||
|
||||
const adminDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'public', 'admin');
|
||||
|
||||
/* Pages that are intentionally not tools: the hub itself and the login screen. */
|
||||
const NOT_TOOLS = new Set(['index.html', 'login.html']);
|
||||
|
||||
const pages = readdirSync(adminDir).filter(f => f.endsWith('.html') && !NOT_TOOLS.has(f));
|
||||
const ids = new Set(TOOLS.map(t => t.id));
|
||||
|
||||
let bad = 0;
|
||||
const fail = m => { console.log(' FAIL ' + m); bad++; };
|
||||
|
||||
console.log('admin nav / page registry\n');
|
||||
|
||||
for (const f of pages) {
|
||||
const id = f.replace(/\.html$/, '');
|
||||
if (!ids.has(id)) fail(`${f} exists but is not in TOOLS — unreachable from the nav and the hub`);
|
||||
}
|
||||
for (const t of TOOLS) {
|
||||
if (!pages.includes(`${t.id}.html`)) fail(`TOOLS lists "${t.id}" but ${t.id}.html does not exist`);
|
||||
if (!t.name || !t.blurb) fail(`"${t.id}" is missing a name or blurb`);
|
||||
if (!GROUPS.includes(t.group)) fail(`"${t.id}" is in group "${t.group}", which is not in GROUPS`);
|
||||
}
|
||||
/* No check that a label differs from its filename: "Layout" and "Playlist" are
|
||||
the right names and happen to match. The labels that were actually wrong
|
||||
("landing", "plan", "print") were wrong because they did not say what the
|
||||
page does, which no rule here can tell. */
|
||||
|
||||
/* The bar must actually contain every tool, and mark exactly one active. */
|
||||
const bar = nav('playlist');
|
||||
for (const t of TOOLS) {
|
||||
if (!bar.includes(href(t))) fail(`nav() output is missing a link to ${href(t)}`);
|
||||
if (!bar.includes(`>${t.name}<`)) fail(`nav() output is missing the label "${t.name}"`);
|
||||
}
|
||||
const actives = (bar.match(/class="active"/g) || []).length;
|
||||
if (actives !== 1) fail(`nav('playlist') marked ${actives} links active, expected 1`);
|
||||
if ((nav('').match(/class="active"/g) || []).length !== 0) fail(`nav('') marked a link active`);
|
||||
if (!bar.startsWith('<header')) fail('nav() root is not <header> — print.html hides "header" on print');
|
||||
|
||||
console.log(` ${pages.length} pages, ${TOOLS.length} registered, ${GROUPS.length} groups`);
|
||||
for (const g of GROUPS) {
|
||||
console.log(` ${g.padEnd(12)} ${TOOLS.filter(t => t.group === g).map(t => t.name).join(', ')}`);
|
||||
}
|
||||
console.log(bad ? `\n${bad} problem(s)` : '\nall pages reachable');
|
||||
process.exit(bad ? 1 : 0);
|
||||
Reference in new issue
Block a user