test.web/presentation/renderers/tab.renderer.test.ts
test.web/presentation/renderers/tab.renderer.test.ts is a file in Codebase Testing. 133 lines of code and 0 definitions.
import { GRAMMAR_META, METHODOLOGY_META } from "@banes-lab/web/configuration/strings/tab.strings.ts";
import { afterEach, describe, expect, it } from "vitest";
import {
hasSectionPages,
layoutOf,
renderContent,
renderFooterFor,
renderOwnSection,
renderTabContent,
renderTabFooter,
renderTabHeader,
renderTabs,
} from "@banes-lab/web/presentation/renderers/tab.renderer.ts";
import { ACTIVE_CLASS } from "@banes-lab/web/configuration/constants/element.constants.ts";
import { ARCHITECTURE_TABS } from "@banes-lab/web/core/generated/architecture.page.generated.ts";
import { GRAMMAR_PAGE } from "@banes-lab/web/core/ids/page.ids.ts";
import { GRAMMAR_TABS } from "@banes-lab/web/core/generated/grammar.page.generated.ts";
import { METHODOLOGY_TABS } from "@banes-lab/web/core/generated/methodology.page.generated.ts";
import { ONTOLOGY_TABS } from "@banes-lab/web/configuration/strings/ontology.fragment.strings.ts";
import { STATIC_RENDER_ATTRIBUTE } from "@banes-lab/web/configuration/constants/document.constants.ts";
import { TREE_TAB } from "@banes-lab/web/core/ids/anatomy.ids.ts";
import { loadTreeTab } from "@banes-lab/web/core/loaders/tree.loader.ts";
import { nodePath } from "@banes-lab/web/core/assets/link.assets.ts";
const EMBLEM_CLASS = "tab-page-emblem";
const BUTTON_CLASS = "tab-button";
const SECTION_CLASS = "tab-section";
const [firstTab] = GRAMMAR_TABS;
const subsectionCount = function subsectionCount(): number {
return (firstTab?.sections ?? []).reduce((total, section) => total + section.subsections.length, 0);
};
afterEach(() => {
document.body.replaceChildren();
});
describe("renderTabHeader", () => {
it("shows the title, subtitle and an emblem when the meta carries one", () => {
const header = renderTabHeader(GRAMMAR_META);
expect(header.textContent.includes(GRAMMAR_META.title)).toBe(true);
expect(header.textContent.includes(GRAMMAR_META.subtitle)).toBe(true);
expect(header.querySelector(`.${EMBLEM_CLASS}`)).not.toBeNull();
expect(renderTabHeader(METHODOLOGY_META).querySelector(`.${EMBLEM_CLASS}`)).toBeNull();
});
});
describe("renderTabs", () => {
it("renders one button per tab and marks the active one", () => {
const bar = renderTabs(GRAMMAR_PAGE, GRAMMAR_TABS, firstTab?.id ?? "");
const buttons = bar.querySelectorAll(`.${BUTTON_CLASS}`);
expect(buttons).toHaveLength(GRAMMAR_TABS.length);
expect(buttons[0]?.classList.contains(ACTIVE_CLASS)).toBe(true);
});
});
describe("renderTabContent", () => {
it("renders every section of the tab", () => {
const content = renderTabContent(firstTab?.sections ?? []);
expect(content.querySelectorAll(`.${SECTION_CLASS}`)).toHaveLength(firstTab?.sections.length ?? 0);
});
});
describe("renderContent", () => {
it("renders a chapter for the chapter layout and sections for the grid layout", () => {
const [methodologyTab] = METHODOLOGY_TABS;
if (firstTab === undefined || methodologyTab === undefined) {
throw new Error(GRAMMAR_PAGE);
}
expect(renderContent("chapter", methodologyTab, GRAMMAR_PAGE).classList.contains("chapter-content")).toBe(true);
expect(renderContent("grid", firstTab, GRAMMAR_PAGE).querySelectorAll(`.${SECTION_CLASS}`)).toHaveLength(
firstTab.sections.length,
);
});
it("lets a tab override the page layout, and drops the section footer for a glossary", () => {
const glossary = ARCHITECTURE_TABS.find((tab) => tab.layout === "glossary");
if (glossary === undefined || firstTab === undefined) {
throw new Error(GRAMMAR_PAGE);
}
expect(layoutOf("chapter", glossary)).toBe("glossary");
expect(renderContent("chapter", glossary, GRAMMAR_PAGE).classList.contains("glossary-content")).toBe(true);
expect(renderFooterFor("chapter", glossary)?.querySelector(".tab-symbol")?.textContent).toBe("A");
expect(renderFooterFor("grid", firstTab)?.querySelector(".tab-symbol")?.textContent).toBe("A1");
});
it("renders the explorer for an ontology tab, with category jumps only in the footer", () => {
const explorer = ONTOLOGY_TABS.find((tab) => tab.layout === "ontology");
if (explorer === undefined) {
throw new Error(GRAMMAR_PAGE);
}
expect(renderContent("grid", explorer, GRAMMAR_PAGE).classList.contains("explorer-content")).toBe(true);
expect(renderFooterFor("grid", explorer)?.querySelectorAll(`.${BUTTON_CLASS}`)).toHaveLength(
explorer.sections.length,
);
});
it("renders the filesystem for a tree tab and drops the footer", async () => {
const tree = await loadTreeTab(TREE_TAB);
if (tree === undefined) {
throw new Error(GRAMMAR_PAGE);
}
expect(renderContent("chapter", tree, GRAMMAR_PAGE).classList.contains("filesystem-content")).toBe(true);
expect(renderFooterFor("chapter", tree)).toBeNull();
});
it("renders a sectioned tab whole for a visitor, and as linked section intros with section pages in the static render", () => {
const [methodologyTab] = METHODOLOGY_TABS;
if (methodologyTab === undefined) {
throw new Error(GRAMMAR_PAGE);
}
const sectioned = { ...methodologyTab, layout: "sectioned" as const };
const [first] = sectioned.sections;
const live = renderContent("chapter", sectioned, GRAMMAR_PAGE);
expect(live.querySelectorAll(".chapter-subsection-title").length).toBeGreaterThan(0);
expect(hasSectionPages("chapter", sectioned)).toBe(true);
expect(hasSectionPages("chapter", methodologyTab)).toBe(false);
document.documentElement.setAttribute(STATIC_RENDER_ATTRIBUTE, "");
try {
const index = renderContent("chapter", sectioned, GRAMMAR_PAGE);
expect(index.querySelectorAll(".chapter-subsection-title")).toHaveLength(0);
expect(index.querySelector(".chapter-section-link a")?.getAttribute("href")).toBe(
nodePath(GRAMMAR_PAGE, sectioned.id, first?.id ?? ""),
);
expect(renderFooterFor("chapter", sectioned)?.querySelectorAll(`.${BUTTON_CLASS}`)).toHaveLength(
sectioned.sections.length,
);
const page = renderOwnSection("chapter", sectioned, first?.id ?? "");
expect(page?.querySelectorAll(".chapter-subsection-title")).toHaveLength(first?.subsections.length ?? 0);
expect(renderOwnSection("chapter", sectioned, first?.id ?? "", [0, 1])?.querySelectorAll(".chapter-subsection-title")).toHaveLength(1);
expect(renderOwnSection("chapter", sectioned, "no-such-section")).toBeNull();
} finally {
document.documentElement.removeAttribute(STATIC_RENDER_ATTRIBUTE);
}
});
});
describe("renderTabFooter", () => {
it("renders one jump button per section and one per subsection", () => {
const sections = firstTab?.sections ?? [];
const footer = renderTabFooter(sections);
expect(footer.querySelectorAll(`.${BUTTON_CLASS}`)).toHaveLength(sections.length + subsectionCount());
expect(footer.querySelector(".tab-symbol")?.textContent).toBe("A1");
});
});