The sandbox was a claim the code could not make. `panels.js` said "no DOM node, no document... a module cannot style anything because it is never handed anything that could" -- while handing over `composer`, whose factories return handles wrapping a real element. A module could take one, style it, and reach the whole document through node.ownerDocument. No lint to evade, just the intended API. No module ever used it. Panels are rendered from the DECLARATION, and a module that wants to draw something should be declaring it. So it is gone, and the guarantee is now true as stated: the kit hands a module nothing that touches the DOM. What is NOT claimed any more: that a module CANNOT reach the DOM. A ui.js is an ES module in the page realm and `document` is a global. kit_lint is a discipline check, not a boundary -- globalThis["docu"+"ment"] defeats every regex and always will. It now catches the bracket-access spellings, which were the first thing tried and went straight through, and says plainly what it is and is not. The test that was supposed to protect this asserted "no value IS a node" while a value that MAKES nodes sat in the same object. It checks what it names now. And the overlay compaction test computed a before-snapshot and discarded it with `void before`, checking only that survivors do not collide -- true even if nothing ever moves. Two attempts to fix it failed against CORRECT code (earlier tests leave live claims, so the rung has a legitimate gap; and some demos close themselves, so closing them again is a no-op). It now opens two overlays of its own so the invariant runs every time, and removing notify() from zlayer fails three tests instead of none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
144 lines
5.5 KiB
JavaScript
144 lines
5.5 KiB
JavaScript
// Rule 3: an element the kit does not have makes its module hide.
|
|
//
|
|
// The alternative is a panel that renders something approximate, which is worse
|
|
// than nothing: it looks like it works.
|
|
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { setupDom, teardownDom } from "./_dom.js";
|
|
|
|
let composer, UnknownElement, renderPanel, mountAll, mounts;
|
|
test.before(async () => {
|
|
setupDom();
|
|
({ composer, UnknownElement } = await import("../composer.js"));
|
|
({ renderPanel } = await import("../panel.js"));
|
|
({ mountAll } = await import("../../shell/panels.js"));
|
|
mounts = await import("../../shell/mounts.js");
|
|
});
|
|
test.after(() => teardownDom());
|
|
|
|
test.beforeEach(() => {
|
|
mounts._reset();
|
|
document.body.replaceChildren();
|
|
});
|
|
|
|
const spec = (id, settings) => ({ id, title: id, mount: "test.region", settings });
|
|
|
|
test("asking the kit for something it does not have throws", () => {
|
|
assert.throws(() => composer.hologram, UnknownElement);
|
|
assert.throws(() => composer.button.hologram, UnknownElement);
|
|
});
|
|
|
|
test("a panel naming an unknown renderer appends nothing", () => {
|
|
const host = document.createElement("div");
|
|
document.body.appendChild(host);
|
|
assert.throws(() => {
|
|
const panel = renderPanel(spec("bad", {
|
|
good: { type: "bool", default: true, label: "Fine" },
|
|
bad: { type: "bool", default: true, label: "Bad", ui: "hologram" },
|
|
}));
|
|
host.appendChild(panel.node);
|
|
});
|
|
assert.equal(host.childElementCount, 0,
|
|
"not even the rows that built successfully reached the document");
|
|
});
|
|
|
|
test("a broken module is absent, and its neighbours still mount", async () => {
|
|
const host = document.createElement("div");
|
|
document.body.appendChild(host);
|
|
mounts.offer("test.region", host);
|
|
|
|
const { mounted, hidden } = await mountAll({ modules: [
|
|
spec("first", { on: { type: "bool", default: true, label: "First" } }),
|
|
spec("broken", { on: { type: "bool", default: true, label: "Broken", ui: "hologram" } }),
|
|
spec("third", { on: { type: "bool", default: true, label: "Third" } }),
|
|
] });
|
|
|
|
assert.deepEqual(mounted.map((m) => m.id), ["first", "third"]);
|
|
assert.deepEqual(hidden.map((h) => h.id), ["broken"]);
|
|
assert.match(host.textContent, /First/);
|
|
assert.match(host.textContent, /Third/);
|
|
assert.doesNotMatch(host.textContent, /Broken/);
|
|
});
|
|
|
|
test("nothing about a hidden module reaches the screen", async () => {
|
|
// No placeholder, no greyed row, no warning chip: absent is absent.
|
|
const host = document.createElement("div");
|
|
document.body.appendChild(host);
|
|
mounts.offer("test.region", host);
|
|
|
|
await mountAll({ modules: [spec("broken", {
|
|
on: { type: "bool", default: true, label: "Broken", ui: "hologram" },
|
|
})] });
|
|
|
|
assert.equal(host.childElementCount, 0);
|
|
assert.equal(host.textContent, "");
|
|
});
|
|
|
|
test("a module whose ui.js fails to load is hidden, not half-mounted", async () => {
|
|
const host = document.createElement("div");
|
|
document.body.appendChild(host);
|
|
mounts.offer("test.region", host);
|
|
|
|
const { mounted, hidden } = await mountAll(
|
|
{ modules: [{ ...spec("has_ui", { on: { type: "bool", default: true, label: "On" } }), ui: "/missing.js" }] },
|
|
{ load: () => { throw new Error("404"); } },
|
|
);
|
|
|
|
assert.deepEqual(mounted, []);
|
|
assert.equal(hidden[0].id, "has_ui");
|
|
assert.equal(host.childElementCount, 0, "its panel did not stay behind");
|
|
});
|
|
|
|
test("a module naming a mount point nobody offers is absent", async () => {
|
|
// Hide, don't warn, one level up: a typo'd mount point makes a module vanish
|
|
// rather than landing somewhere arbitrary.
|
|
const { mounted, hidden } = await mountAll({ modules: [
|
|
{ ...spec("lost", { on: { type: "bool", default: true, label: "On" } }), mount: "nowhere" },
|
|
] });
|
|
assert.deepEqual(mounted, []);
|
|
assert.match(hidden[0].why, /no region offers/);
|
|
});
|
|
|
|
test("a module is handed no way to touch the document", async () => {
|
|
const host = document.createElement("div");
|
|
document.body.appendChild(host);
|
|
mounts.offer("test.region", host);
|
|
|
|
let context = null;
|
|
await mountAll(
|
|
{ modules: [{ ...spec("m", { on: { type: "bool", default: true, label: "On" } }), ui: "/ui.js" }] },
|
|
{ load: async () => ({ setup: (ctx) => { context = ctx; } }) },
|
|
);
|
|
|
|
assert.deepEqual(Object.keys(context).sort(), ["on", "shell", "values"]);
|
|
|
|
// Not "no value IS a node" -- that was the old assertion, and it passed while
|
|
// `composer` sat in this object handing out handles whose .node is a real
|
|
// element, reachable to the whole document through node.ownerDocument. The
|
|
// property that matters is that nothing here PRODUCES one either.
|
|
for (const [name, value] of Object.entries(context)) {
|
|
assert.equal(value instanceof globalThis.Node, false, `${name} is a DOM node`);
|
|
}
|
|
assert.equal("composer" in context, false,
|
|
"an element factory is a way to obtain a DOM node");
|
|
|
|
// `on` is callable, and must hand back an unsubscribe rather than anything
|
|
// that leads to the page.
|
|
const off = context.on(() => {});
|
|
assert.equal(typeof off, "function");
|
|
assert.equal(off instanceof globalThis.Node, false);
|
|
|
|
// Nothing one level down is a node either -- the services are the other place
|
|
// a handle could have been tucked away.
|
|
for (const [name, value] of Object.entries(context.shell)) {
|
|
for (const [inner, member] of Object.entries(value)) {
|
|
assert.equal(member instanceof globalThis.Node, false, `shell.${name}.${inner}`);
|
|
}
|
|
}
|
|
|
|
// The service surface is named one by one, so it can be reviewed.
|
|
assert.deepEqual(Object.keys(context.shell).sort(), ["density", "theme"]);
|
|
});
|