Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,13 @@ red PR job against.
- `node --test scripts/page-scripts.test.mjs` — static gate over every committed HTML
page: each inline `<script>` parses, each JSON-LD block is valid schema.org JSON, no
external `<script src>` is introduced, every inline behaviour script (other than the
one-line `no-js` toggle) is registered in `HARNESSED_SCRIPTS` with the harness test
that extracts and runs it — so a new inline script cannot ship with only a syntax
check — and `sitemap.xml` lists exactly the canonical top-level pages (redirect
shortcuts excluded, new pages required). Zero dependencies.
one-line `no-js` toggle) is registered in `HARNESSED_SCRIPTS`
(`scripts/inline-scripts.mjs`) with the harness test that extracts and runs it — so a
new inline script cannot ship with only a syntax check — and `sitemap.xml` lists
exactly the canonical top-level pages (redirect shortcuts excluded, new pages
required). The harnesses extract their block through `inlineScript()` from the same
module, which tags the body with a `//# sourceURL=inline:<page>#<hook>` trailer so
`coverage-floor.test.mjs` can attribute its coverage. Zero dependencies.
- `node --test scripts/page-markup.test.mjs` — static markup gate over every committed
non-redirect HTML page: `id`s are unique, every ARIA idref (`aria-controls`,
`aria-labelledby`, `aria-describedby`, …) and `label for` resolves to an `id` on the
Expand Down Expand Up @@ -203,15 +206,19 @@ red PR job against.
regenerate command. Zero dependencies.
- `node --test scripts/coverage-floor.test.mjs` — branch-coverage floor over the
`scripts/*.mjs` helpers the other gates import (`page-csp.mjs`, `html-pages.mjs`,
`actions-if.mjs`, `update-csp-hashes.mjs`): runs the rest of the suite once more as a
child with `NODE_V8_COVERAGE`, reads the raw V8 block-coverage ranges for every helper
(`*.test.mjs` excluded) and fails naming each range no process executed — file, line
and the quoted source slice — so a helper branch that runs under the suite without a
test asserting what it decides cannot merge green. A range counts as executed if any
child process ran it; a tool's main-module guard (`if (process.argv[1] …
import.meta.url)`) is excluded because its self-test runs it via `spawnSync` against a
copy. Each rule has a fixture self-test, including a real child run over a tmp helper
with one dead branch. Zero dependencies.
`actions-if.mjs`, `update-csp-hashes.mjs`, `inline-scripts.mjs`) and over every inline
page script registered in `HARNESSED_SCRIPTS` (hero carousel, ACMM levels, projects
carousel, story dialog): runs the rest of the suite once more as a child with
`NODE_V8_COVERAGE`, reads the raw V8 block-coverage ranges for every helper
(`*.test.mjs` excluded) and for every `inline:<page>#<hook>` script the harnesses
compile via `new Function` (offsets shifted past the function wrapper), and fails
naming each range no process executed — file or `page#hook`, page line and the quoted
source slice — so a branch that runs under the suite without a test asserting what it
decides cannot merge green. A range counts as executed if any child process ran it; a
tool's main-module guard (`if (process.argv[1] … import.meta.url)`) is excluded because
its self-test runs it via `spawnSync` against a copy. Each rule has a fixture
self-test, including real child runs over a tmp helper and a tmp page with one dead
branch each. Zero dependencies.
- `node --test scripts/llms-txt.test.mjs` — static gate keeping `llms.txt` in step with
`index.html`: the agent CLIs it names must equal the Agent CLIs chips, the inference
engines/gateways it names must equal the engine and gateway chips (classifier chips
Expand Down
46 changes: 36 additions & 10 deletions scripts/acmm-levels.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@ import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { inlineScript } from "./inline-scripts.mjs";

const HERE = dirname(fileURLToPath(import.meta.url));
const PAGE = join(HERE, "..", "index.html");
const HOLD_MS = 5000;
const LEVELS = 6;

function extractAcmmScript(html) {
const blocks = [...html.matchAll(/<script>([\s\S]*?)<\/script>/g)].map((m) => m[1]);
const scripts = blocks.filter((s) => s.includes("[data-acmm-levels]"));
assert.equal(scripts.length, 1, "expected exactly one ACMM levels script block");
return scripts[0];
return inlineScript(html, "index.html", "[data-acmm-levels]").src;
}

class ClassList {
Expand Down Expand Up @@ -73,7 +71,9 @@ class Element {
querySelector(selector) { return this.querySelectorAll(selector)[0] || null; }
}

function makeDocument({ activeIndex = 0 } = {}) {
// `withRoot: false` is a page without the ladder, `panelCount` a details list
// out of step with the levels, `tabless` the index of a level with no button.
function makeDocument({ activeIndex = 0, withRoot = true, panelCount = LEVELS, tabless = -1 } = {}) {
const doc = { activeElement: null, hidden: false, listeners: {} };
doc.addEventListener = (type, fn) => { (doc.listeners[type] ||= []).push(fn); };
doc.dispatch = (type) => { for (const fn of doc.listeners[type] || []) fn({ type }); };
Expand All @@ -91,19 +91,19 @@ function makeDocument({ activeIndex = 0 } = {}) {
classes: active ? ["is-active"] : [],
parent: list,
});
const tab = new Element(doc, "tab-" + (i + 1), {
const tab = i === tabless ? null : new Element(doc, "tab-" + (i + 1), {
attrs: { "data-acmm-tab": "", "aria-selected": active ? "true" : "false" },
parent: level,
});
const panel = new Element(doc, "panel-" + (i + 1), {
const panel = i >= panelCount ? null : new Element(doc, "panel-" + (i + 1), {
attrs: { "data-acmm-panel": "" },
classes: active ? ["is-active"] : [],
parent: details,
});
levels.push(level); tabs.push(tab); panels.push(panel);
}
doc.querySelector = (selector) => {
if (selector === "[data-acmm-levels]") return root;
if (selector === "[data-acmm-levels]") return withRoot ? root : null;
throw new Error("unsupported document selector in stub DOM: " + selector);
};
return { doc, root, levels, tabs, panels };
Expand Down Expand Up @@ -133,10 +133,10 @@ function makeTimers() {
};
}

function boot({ reduceMotion = false, intersectionObserver = true, activeIndex = 0 } = {}) {
function boot({ reduceMotion = false, intersectionObserver = true, activeIndex = 0, withRoot = true, panelCount = LEVELS, tabless = -1 } = {}) {
const html = readFileSync(PAGE, "utf8");
const src = extractAcmmScript(html);
const dom = makeDocument({ activeIndex });
const dom = makeDocument({ activeIndex, withRoot, panelCount, tabless });
const timers = makeTimers();
const mq = { matches: reduceMotion, listeners: [], addEventListener(type, fn) { if (type === "change") this.listeners.push(fn); } };
mq.set = (matches) => { mq.matches = matches; mq.listeners.forEach((fn) => fn({ matches })); };
Expand Down Expand Up @@ -376,3 +376,29 @@ test("start() is idempotent: repeated in-view signals never stack timers", () =>
t.advance(HOLD_MS);
assertActive(t, 1, "exactly one advance per HOLD_MS");
});

test("no-op guards: a page without the ladder, or panels out of step with the levels, wires nothing", () => {
const none = boot({ withRoot: false });
assert.deepEqual(none.observers, [], "nothing observed");
assert.deepEqual(Object.keys(none.doc.listeners), [], "nothing listened to");
assert.deepEqual(Object.keys(none.root.listeners), []);
assert.equal(none.timers.pendingCount(), 0);
const skewed = boot({ panelCount: LEVELS - 1 });
assert.deepEqual(skewed.observers, [], "a panel list that does not match the levels is left alone rather than mis-paired");
assert.deepEqual(Object.keys(skewed.root.listeners), []);
assert.equal(skewed.tabs[1].tabIndex, 0, "roving tabindex never applied");
assert.equal(skewed.panels[1].getAttribute("aria-hidden"), null);
});

test("a level without a tab button is skipped by the wiring but still toggles with the ladder", () => {
const t = boot({ tabless: 2 });
t.show();
assert.equal(t.tabs[2], null);
assert.deepEqual(Object.keys(t.levels[2].listeners), [], "no click/focus/keydown wiring for the buttonless level");
t.tabs[1].dispatch("click");
t.tabs[1].dispatch("keydown", { key: "ArrowRight" });
assert.equal(t.activeIndexNow(), 2, "keyboard selection still reaches it");
assert.equal(t.levels[2].classList.contains("is-active"), true);
assert.equal(t.panels[2].getAttribute("aria-hidden"), "false");
assert.equal(t.doc.activeElement, t.tabs[1], "there is no button to move focus to");
});
Loading
Loading