66 lines
2.4 KiB
JavaScript
66 lines
2.4 KiB
JavaScript
const { test } = require("node:test");
|
|
const assert = require("node:assert");
|
|
const { renderTreeToHtml, renderMenu, esc } = require("../lib/renderTree");
|
|
const { getPreset } = require("../lib/layoutTree");
|
|
|
|
const CTX = {
|
|
title: "T",
|
|
body: "<p id='the-body'>BODY</p>",
|
|
brand: { name: "Acme", logo: null },
|
|
menu: [{ items: [{ label: "Home", link: "/" }, { label: "Views", link: "/viewedit", subitems: [{ label: "New", link: "/viewedit/new" }] }] }],
|
|
alerts: [{ type: "success", msg: "saved" }],
|
|
currentUrl: "/",
|
|
};
|
|
|
|
|
|
function balanced(html) {
|
|
// crude tag-name-agnostic sanity: angle brackets balance
|
|
return (html.match(/</g) || []).length === (html.match(/>/g) || []).length;
|
|
}
|
|
|
|
|
|
test("topnav preset renders navbar + body slot + footer", () => {
|
|
const html = renderTreeToHtml(getPreset("topnav"), CTX);
|
|
assert.match(html, /<nav class="navbar/);
|
|
assert.match(html, /id='the-body'/, "body injected into Content slot");
|
|
assert.match(html, /navbar-brand[^>]*>.*Acme/s, "brand rendered");
|
|
assert.match(html, /<footer/);
|
|
assert.ok(balanced(html));
|
|
});
|
|
|
|
|
|
test("sidebar preset renders an aside + body", () => {
|
|
const html = renderTreeToHtml(getPreset("sidebar"), CTX);
|
|
assert.match(html, /<aside class="tb-sidebar/);
|
|
assert.match(html, /id='the-body'/);
|
|
assert.ok(balanced(html));
|
|
});
|
|
|
|
|
|
test("body + alerts always emitted even if tree omits the slots", () => {
|
|
const bare = { type: "Root", children: [{ type: "Content" }] }; // no AlertsSlot
|
|
const html = renderTreeToHtml(bare, CTX);
|
|
assert.match(html, /id='the-body'/);
|
|
assert.match(html, /alert-success/, "alerts appended when no AlertsSlot");
|
|
});
|
|
|
|
|
|
test("junk tree falls back to default (still renders body)", () => {
|
|
const html = renderTreeToHtml({ type: "Nope" }, CTX);
|
|
assert.match(html, /id='the-body'/);
|
|
});
|
|
|
|
|
|
test("menu renders links + a dropdown for subitems; marks active", () => {
|
|
const html = renderMenu(CTX.menu, "navbar", CTX);
|
|
assert.match(html, /href="\/"[^>]*>.*Home/s);
|
|
assert.match(html, /dropdown/);
|
|
assert.match(html, /nav-link active/, "current url marked active");
|
|
});
|
|
|
|
|
|
test("esc escapes text but body HTML is passed through", () => {
|
|
assert.equal(esc(`<b>&"'`), "<b>&"'");
|
|
const html = renderTreeToHtml(getPreset("topnav"), { ...CTX, body: "<script>x</script>" });
|
|
assert.match(html, /<script>x<\/script>/, "trusted server body not escaped");
|
|
});
|