test.web/presentation/renderers/ontology.renderer.test.ts
test.web/presentation/renderers/ontology.renderer.test.ts is a file in Codebase Testing. 157 lines of code and 0 definitions.
import { describe, expect, it } from "vitest";
import { hasOwnSections, hasSectionPages } from "@banes-lab/web/presentation/renderers/tab.renderer.ts";
import { recordLinkText, sectionPageLabel } from "@banes-lab/web/configuration/strings/ontology.strings.ts";
import {
renderExplorer,
renderExplorerSection,
rowCount,
rowWeights,
} from "@banes-lab/web/presentation/renderers/ontology.renderer.ts";
import { ONTOLOGY_PRINCIPLES_TAB } from "@banes-lab/web/core/ids/ontology.ids.ts";
import { ONTOLOGY_TABS } from "@banes-lab/web/configuration/strings/ontology.fragment.strings.ts";
import { SEARCH_ATTRIBUTE } from "@banes-lab/web/configuration/constants/ontology.constants.ts";
import { STATIC_RENDER_ATTRIBUTE } from "@banes-lab/web/configuration/constants/document.constants.ts";
import type { Tab } from "@banes-lab/web/types/document.types.ts";
import { registerRecords } from "@banes-lab/web/domain/registries/record.registry.ts";
const principlesTab = function principlesTab(): Tab {
const principles = ONTOLOGY_TABS.find((tab) => tab.id === ONTOLOGY_PRINCIPLES_TAB);
if (principles === undefined) {
throw new Error("no principles tab");
}
return principles;
};
const DIAGRAM = principlesTab().sections[0]?.blocks?.find((block) => block.kind === "mermaid");
const ROW_BODY = [{ entries: [{ description: "Kept.", term: "Rule" }], kind: "glossary" as const }];
const LONG_VALUE = "A rule that runs on well past the width one card can hold without stacking. ".repeat(3);
const FIXTURE: Tab = {
icon: "",
id: "fixture",
label: "Fixture",
sections: [
{
blocks: DIAGRAM === undefined ? [] : [DIAGRAM],
icon: "",
id: "group-one",
intro: "One.",
subsections: [
{
blocks: [
{ items: ["Kind: principle", "Severity: mandatory"], kind: "list" },
{
entries: [
{ description: "High Cohesion", term: "Requires" },
{ description: LONG_VALUE, term: "Rule" },
],
kind: "glossary",
},
],
id: "architecture-alpha",
title: "Alpha",
},
{
blocks: [
{
entries: [
{ description: "First.", term: "one" },
{ description: "Second.", term: "two" },
],
kind: "glossary",
},
],
title: "Entries",
},
],
title: "Group one",
},
],
};
describe("renderExplorer", () => {
const article = renderExplorer(FIXTURE);
it("renders a search box, a category navigator and one record per subsection or catalog entry", () => {
expect(article.querySelector(".filter-box")).not.toBeNull();
expect(article.querySelectorAll(".explorer-nav-list a")).toHaveLength(1);
expect(article.querySelectorAll(".record")).toHaveLength(3);
expect(article.querySelector("#architecture-alpha .record-chip")?.textContent).toBe("Kind: principle");
expect(article.querySelector("#architecture-alpha")?.getAttribute(SEARCH_ATTRIBUTE)).toContain("high cohesion");
expect(article.querySelector(".explorer-count")?.textContent).toBe("3 of 3 shown");
});
it("renders a record's body only once its details open, as field cards, widening a card whose value runs long", () => {
const details = article.querySelector("#architecture-alpha details");
expect(article.querySelectorAll("#architecture-alpha .record-field")).toHaveLength(0);
details?.dispatchEvent(new Event("toggle"));
const cards = article.querySelectorAll("#architecture-alpha .record-field");
expect(cards).toHaveLength(2);
expect(cards[0]?.classList.contains("record-field-wide")).toBe(false);
expect(cards[1]?.classList.contains("record-field-wide")).toBe(true);
details?.dispatchEvent(new Event("toggle"));
expect(article.querySelectorAll("#architecture-alpha .record-field")).toHaveLength(2);
});
it("keeps the diagram in a panel behind a collapsed summary, rendered on first open", () => {
const details = article.querySelector(".explorer-group > .record-details");
expect(details?.hasAttribute("open")).toBe(false);
expect(details?.querySelector(".chapter-panel")).toBeNull();
details?.dispatchEvent(new Event("toggle"));
expect(details?.querySelector(".chapter-panel .diagram-figure")).not.toBeNull();
});
it("renders a published record row linked to its record page for a static render, and the other bodies eagerly", () => {
registerRecords(["architecture:alpha", "architecture:information"]);
const information = { blocks: [], id: "architecture-information", title: "Information" };
const unpublished = { blocks: [{ items: ["Kind: shape"], kind: "list" as const }, ...ROW_BODY], id: "architecture-beta", title: "Beta" };
const [group] = FIXTURE.sections;
const sections = group === undefined ? [] : [{ ...group, subsections: [...group.subsections, information, unpublished] }];
document.documentElement.setAttribute(STATIC_RENDER_ATTRIBUTE, "");
const tab = renderExplorer({ ...FIXTURE, sections });
const rendered = renderExplorerSection({ ...FIXTURE, sections }, "group-one");
document.documentElement.removeAttribute(STATIC_RENDER_ATTRIBUTE);
if (rendered === null) {
throw new Error("no section");
}
expect(renderExplorerSection(FIXTURE, "group-none")).toBeNull();
expect(hasSectionPages("ontology", FIXTURE)).toBe(true);
expect(hasSectionPages("tree", FIXTURE)).toBe(false);
expect(hasOwnSections("tree", FIXTURE)).toBe(true);
expect(hasOwnSections("chapter", FIXTURE)).toBe(false);
expect(tab.querySelector(".record")).toBeNull();
expect(tab.querySelector(".filter-box")).toBeNull();
expect(tab.querySelector(".explorer-section-link a")?.getAttribute("href")).toBe("/ontology/fixture/group-one");
expect(tab.querySelector(".explorer-section-link a")?.textContent).toBe(sectionPageLabel(5, "Group one"));
expect(rowCount(sections[0] ?? { icon: "", id: "", subsections: [], title: "" })).toBe(5);
const weights = rowWeights(sections[0] ?? { icon: "", id: "", subsections: [], title: "" });
expect(weights).toHaveLength(5);
expect(weights.every((weight) => weight > 0)).toBe(true);
expect(rendered.querySelector("#architecture-alpha .record-title a")?.getAttribute("href")).toBe(
"/records/architecture/alpha",
);
expect(rendered.querySelector("#architecture-information .record-title a")?.textContent).toBe(
recordLinkText("Information", "Group one"),
);
expect(recordLinkText("START", "Keywords")).toBe("START (Keywords)");
expect(rendered.querySelector("#architecture-beta .record-title a")).toBeNull();
expect(rendered.querySelectorAll("#architecture-beta .record-field")).toHaveLength(1);
expect(rendered.querySelectorAll("#architecture-alpha .record-field")).toHaveLength(0);
expect(rendered.querySelector("#architecture-alpha")?.hasAttribute("data-search")).toBe(false);
expect(rendered.querySelector(".record-details .chapter-panel")).not.toBeNull();
});
it("filters records and groups by the typed query", () => {
const search = article.querySelector<HTMLInputElement>(".filter-box");
if (search === null) {
throw new Error("no search box");
}
search.value = "second";
search.dispatchEvent(new Event("input"));
const records = [...article.querySelectorAll<HTMLElement>(".record")];
expect(records.filter((record) => record.hidden === false).map((record) => record.id)).toStrictEqual([
"group-one-sub-1-sub-1",
]);
expect(article.querySelector(".explorer-count")?.textContent).toBe("1 of 3 shown");
search.value = "";
search.dispatchEvent(new Event("input"));
expect(records.every((record) => record.hidden === false)).toBe(true);
});
it("renders the real principles tab with one record per principle", () => {
const principles = principlesTab();
const rendered = renderExplorer(principles);
const expected = principles.sections.reduce((total, section) => total + section.subsections.length, 0);
expect(rendered.querySelectorAll(".record")).toHaveLength(expected);
});
});