Cross-check el() ids against index.html in the selftest
A knob wired in app.js but missing from the markup throws during wiring, aborts the module and leaves a blank page - a symptom pointing nowhere near its cause. It has now happened twice, so it gets a check rather than vigilance: the selftest fetches both files and compares the id sets in each direction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9a11eeabc1
commit
a146d271b6
3 changed files with 38 additions and 2 deletions
|
|
@ -148,7 +148,11 @@ chromium --headless --virtual-time-budget=8000 --dump-dom \
|
|||
http://127.0.0.1:8777/selftest.html | grep -oE '(PASS|FAIL) [0-9/]+'
|
||||
```
|
||||
|
||||
Or open `selftest.html`. 29 assertions over the stages below detection.
|
||||
Or open `selftest.html`. 41 assertions over the stages below detection, plus a
|
||||
wiring cross-check: every `el('id')` in `app.js` must exist in `index.html`. A
|
||||
knob wired in one but not the other throws during wiring, which aborts the rest
|
||||
of the module and leaves a blank page — a symptom that points nowhere near its
|
||||
cause, and which has happened twice.
|
||||
|
||||
The ring-simplicity check is the load-bearing one. Because `hold` parts *cut*
|
||||
between poses instead of interpolating, a ring whose vertex order is wrong
|
||||
|
|
|
|||
|
|
@ -45,6 +45,37 @@ const spreadX = (frames, slot) => {
|
|||
|
||||
/* ---- the tests ---- */
|
||||
|
||||
// Cross-check every el('id') in app.js against the ids in index.html.
|
||||
//
|
||||
// This bug class has bitten twice: a knob wired in app.js but absent from the
|
||||
// markup throws during wiring, which aborts the rest of the module and leaves a
|
||||
// blank page. The symptom ("nothing happens") points nowhere near the cause, so
|
||||
// it is worth an automated check rather than vigilance.
|
||||
export async function runWiring() {
|
||||
const out = [];
|
||||
try {
|
||||
const [app, html] = await Promise.all([
|
||||
fetch('./js/app.js').then((r) => r.text()),
|
||||
fetch('./index.html').then((r) => r.text()),
|
||||
]);
|
||||
const ids = new Set([...app.matchAll(/\bel\(\s*['"]([\w-]+)['"]\s*\)/g)].map((m) => m[1]));
|
||||
const list = app.match(/for \(const id of \[([\s\S]*?)\]\)/);
|
||||
if (list) {
|
||||
for (const m of list[1].matchAll(/'([\w]+)'/g)) { ids.add(m[1]); ids.add(m[1] + 'v'); }
|
||||
}
|
||||
const have = new Set([...html.matchAll(/id="([\w-]+)"/g)].map((m) => m[1]));
|
||||
const missing = [...ids].filter((i) => !have.has(i));
|
||||
out.push({ name: `every el() id exists in index.html (${ids.size} checked)`,
|
||||
pass: missing.length === 0, detail: missing.join(', ') });
|
||||
const unused = [...have].filter((i) => !ids.has(i));
|
||||
out.push({ name: 'no orphaned ids in index.html', pass: unused.length === 0,
|
||||
detail: unused.join(', ') });
|
||||
} catch (e) {
|
||||
out.push({ name: 'wiring check ran', pass: false, detail: e.message });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function run() {
|
||||
results.length = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@
|
|||
</style></head><body>
|
||||
<h1 id="head">running…</h1><ul id="out"></ul>
|
||||
<script type="module">
|
||||
import { run } from './js/selftest.js';
|
||||
import { run, runWiring } from './js/selftest.js';
|
||||
let res;
|
||||
try { res = run(); }
|
||||
catch (e) { res = [{ name: 'harness threw: ' + e.message, pass: false, detail: String(e.stack).split('\n')[1] || '' }]; }
|
||||
res = res.concat(await runWiring());
|
||||
const pass = res.filter(r => r.pass).length;
|
||||
const fail = res.length - pass;
|
||||
document.getElementById('head').textContent = `${fail === 0 ? 'PASS' : 'FAIL'} — ${pass}/${res.length} assertions`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue