diff --git a/README.md b/README.md
index a9a2791..8bd4419 100644
--- a/README.md
+++ b/README.md
@@ -204,6 +204,8 @@ red PR job against.
Then pins the live tree: every committed content page is already up to date, so the
tool and `page-csp.test.mjs` agree on what correct means, and a failure names the
regenerate command. Zero dependencies.
+- `node --test scripts/inline-scripts.test.mjs` — checks shared inline-script extraction,
+ unique hook matching, page line numbers and stable V8 source URLs.
- `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`, `inline-scripts.mjs`) and over every inline
diff --git a/scripts/carousels.test.mjs b/scripts/carousels.test.mjs
index 5b3c540..27cc68c 100644
--- a/scripts/carousels.test.mjs
+++ b/scripts/carousels.test.mjs
@@ -998,3 +998,22 @@ test("projects track inset: an empty computed padding-left counts as 0, not NaN"
t.next.dispatch("click");
assert.equal(t.lastScroll().left, SLIDE_PITCH, "would be NaN if the empty padding were not coerced");
});
+
+test("hero: keyboard focuses a slide without links and retries unsupported focus options", () => {
+ const t = bootHero();
+ t.slides[0].children[0].focus();
+ t.slides[1].children.forEach((el) => el.remove());
+ const attempts = [];
+ const target = t.slides[1];
+ target.focus = (options) => {
+ attempts.push(options);
+ if (options) throw new TypeError('options unsupported');
+ t.doc.activeElement = target;
+ };
+ t.root.dispatch('keydown', { key: 'ArrowRight' });
+ assert.equal(t.currentIndex(), 1);
+ assert.equal(target.tabIndex, -1);
+ assert.deepEqual(attempts, [{ preventScroll: true }, undefined]);
+ assert.equal(t.doc.activeElement, target);
+ assert.equal(t.rotating(), false);
+});
diff --git a/scripts/coverage-floor.test.mjs b/scripts/coverage-floor.test.mjs
index 73c5fa9..7c5c51f 100644
--- a/scripts/coverage-floor.test.mjs
+++ b/scripts/coverage-floor.test.mjs
@@ -339,3 +339,44 @@ test("every branch of every scripts/*.mjs helper and every inline page script is
...problemsOf(inline, inlineRuns.runs),
], []);
});
+
+test("fixture: real inline child coverage maps Function wrappers to original page lines", () => {
+ const dir = mkdtempSync(join(tmpdir(), "coverage-inline-fixture-"));
+ try {
+ mkdirSync(join(dir, "scripts"));
+ const page = '
Fixture
\n\n';
+ writeFileSync(join(dir, "page.html"), page);
+ const helper = pathToFileURL(join(HERE, "inline-scripts.mjs")).href;
+ writeFileSync(join(dir, "scripts", "page.test.mjs"), `
+import assert from "node:assert/strict";
+import { readFileSync } from "node:fs";
+import { inlineScript } from ${JSON.stringify(helper)};
+const { src } = inlineScript(readFileSync("page.html", "utf8"), "page.html", "pick");
+new Function("assert", "flag", "expected", src)(assert, true, "yes");
+new Function("assert", "flag", "expected", "unusedLongParameter", src)(assert, true, "yes");
+`);
+ const registry = { "page.html": [{ hook: "pick" }] };
+ const sources = inlineSources(dir, registry);
+ const first = collect(dir, suiteFiles(dir));
+ assert.equal(first.status, 0, first.stdout + first.stderr);
+ const { runs, problems } = inlineRunsFromReports(first.reports, sources);
+ assert.deepEqual(problems, []);
+ assert.deepEqual([...runs.keys()], ["page.html#pick"]);
+ assert.deepEqual(problemsOf(sources, runs), [
+ 'page.html#pick:5 `: "no"` never executed by the suite (add a test that asserts what this branch decides)',
+ ]);
+ assert.deepEqual(problemsOf(sources, new Map()), ['page.html#pick: never loaded by the suite']);
+ writeFileSync(join(dir, "scripts", "both.test.mjs"), `
+import assert from "node:assert/strict";
+import { readFileSync } from "node:fs";
+import { inlineScript } from ${JSON.stringify(helper)};
+new Function("assert", "flag", "expected", inlineScript(readFileSync("page.html", "utf8"), "page.html", "pick").src)(assert, false, "no");
+`);
+ const both = collect(dir, suiteFiles(dir));
+ assert.equal(both.status, 0, both.stdout + both.stderr);
+ assert.deepEqual(problemsOf(sources, inlineRunsFromReports(both.reports, sources).runs), []);
+ assert.deepEqual([...inlineRunsFromReports([{ result: [{ url: "inline:unknown", functions: [] }, { url: "node:fs" }, { url: pathToFileURL(join(dir, "scripts", "unknown.mjs")).href }] }, {}], sources).runs], []);
+ } finally {
+ rmSync(dir, { recursive: true, force: true });
+ }
+});
diff --git a/scripts/inline-scripts.test.mjs b/scripts/inline-scripts.test.mjs
new file mode 100644
index 0000000..4bb46aa
--- /dev/null
+++ b/scripts/inline-scripts.test.mjs
@@ -0,0 +1,12 @@
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import { inlineScript, inlineUrl } from "./inline-scripts.mjs";
+
+test("inline extraction pins body, page line and stable source URL", () => {
+ const html = 'Hi
\n';
+ assert.equal(inlineScript(html, 'p.html', 'widget').line, 2);
+ assert.equal(inlineUrl('p.html', 'widget'), 'inline:p.html#widget');
+ assert.equal(inlineScript(html, 'p.html', 'widget').src, '\nwidget();\n\n//# sourceURL=inline:p.html#widget\n');
+ assert.throws(() => inlineScript(html, 'p.html', 'missing'), /p.html: expected exactly one.*found 0/);
+ assert.throws(() => inlineScript(html + html, 'p.html', 'widget'), /found 2/);
+});