sc-theme-builder/test/renderTree.test.js
2026-07-01 20:07:28 -05:00

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>&"'`), "&lt;b&gt;&amp;&quot;&#39;");
const html = renderTreeToHtml(getPreset("topnav"), { ...CTX, body: "<script>x</script>" });
assert.match(html, /<script>x<\/script>/, "trusted server body not escaped");
});