test.web/presentation/components/reference.component.test.ts
test.web/presentation/components/reference.component.test.ts is a file in Codebase Testing. 164 lines of code and 0 definitions.
import {
REFERENCE_ABOVE_CLASS,
REFERENCE_GAP_PX,
REFERENCE_PENDING_CLASS,
} from "@banes-lab/web/configuration/constants/reference.constants.ts";
import {
armLink,
clampPanel,
createChapterPanel,
createRecordPanel,
placePanel,
} from "@banes-lab/web/presentation/components/reference.component.ts";
import { describe, expect, it, vi } from "vitest";
import { createElement } from "@banes-lab/web/core/factories/element.factory.ts";
import { declaredStyle } from "@banes-lab/web/core/registries/style.registry.ts";
import { hrefOf } from "@banes-lab/web/domain/converters/ontology.converter.ts";
const rectOf = function rectOf(top: number, left: number): DOMRect {
return {
bottom: top + 10,
height: 10,
left,
right: left + 10,
toJSON: () => ({}),
top,
width: 10,
x: left,
y: top,
};
};
describe("placePanel", () => {
it("sits below the anchor when there is room, clamped to the viewport, else above it", () => {
const panel = createElement("div");
const anchor = createElement("a");
vi.spyOn(panel, "offsetWidth", "get").mockReturnValue(200);
vi.spyOn(panel, "offsetHeight", "get").mockReturnValue(100);
vi.spyOn(anchor, "getBoundingClientRect").mockReturnValue(rectOf(50, window.innerWidth - 20));
placePanel(panel, anchor);
expect(declaredStyle(panel, "top")).toBe(`${String(60 + REFERENCE_GAP_PX)}px`);
expect(declaredStyle(panel, "left")).toBe(`${String(window.innerWidth - 200 - REFERENCE_GAP_PX)}px`);
expect(panel.classList.contains(REFERENCE_ABOVE_CLASS)).toBe(false);
vi.spyOn(anchor, "getBoundingClientRect").mockReturnValue(rectOf(window.innerHeight - 20, 0));
placePanel(panel, anchor);
expect(panel.classList.contains(REFERENCE_ABOVE_CLASS)).toBe(true);
expect(declaredStyle(panel, "top")).toBe(`${String(window.innerHeight - 20 - REFERENCE_GAP_PX - 100)}px`);
expect(declaredStyle(panel, "left")).toBe(`${String(REFERENCE_GAP_PX)}px`);
});
it("clamps a panel that grew past the viewport back inside it", () => {
const panel = createElement("div");
vi.spyOn(panel, "offsetWidth", "get").mockReturnValue(200);
vi.spyOn(panel, "offsetHeight", "get").mockReturnValue(window.innerHeight);
vi.spyOn(panel, "offsetTop", "get").mockReturnValue(window.innerHeight - 50);
vi.spyOn(panel, "offsetLeft", "get").mockReturnValue(window.innerWidth);
clampPanel(panel);
expect(declaredStyle(panel, "top")).toBe(`${String(REFERENCE_GAP_PX)}px`);
expect(declaredStyle(panel, "left")).toBe(`${String(window.innerWidth - 200 - REFERENCE_GAP_PX)}px`);
});
});
describe("armLink", () => {
it("marks the link pending with a dial and an arrow, and the disposer restores it", () => {
const link = createElement("a", { attributes: { href: "/ontology#architecture-x" }, text: "X" });
const disarm = armLink(link);
expect(link.classList.contains(REFERENCE_PENDING_CLASS)).toBe(true);
expect(link.querySelectorAll("span")).toHaveLength(2);
expect(link.querySelector("svg circle")).not.toBeNull();
disarm();
expect(link.classList.contains(REFERENCE_PENDING_CLASS)).toBe(false);
expect(link.querySelectorAll("span")).toHaveLength(0);
expect(link.textContent).toBe("X");
});
});
describe("createRecordPanel", () => {
it("renders the title with its code, the kind with its layer link, the summary and one labeled row per relation", () => {
const nodes = createRecordPanel({
code: "SRP",
kind: "principle",
layer: { label: "Structural Core", ref: "layer:structural-core" },
name: "Single Responsibility Principle",
relations: [
{
edges: [
{ label: "High Cohesion", ref: "architecture:high-cohesion" },
{ label: "Unlinked", ref: null },
],
relation: "requires",
},
{ edges: [{ label: "Fail Fast", ref: "architecture:fail-fast" }], relation: "referenced-by" },
],
summary: "One reason to change.",
});
const text = nodes.map((node) => node.textContent).join("|");
expect(text).toContain("Single Responsibility Principle · SRP");
expect(text).toContain("principle · Structural Core");
expect(text).toContain("One reason to change.");
expect(text).toContain("Requires: High Cohesion, Unlinked");
expect(text).toContain("Referenced by: Fail Fast");
const links = nodes.flatMap((node) => [...node.querySelectorAll("a")]).map((link) => link.getAttribute("href"));
expect(links).toStrictEqual([
hrefOf("layer:structural-core"),
hrefOf("architecture:high-cohesion"),
hrefOf("architecture:fail-fast"),
]);
});
it("renders one labeled row per check question and reads a declared absence as its reason", () => {
const nodes = createRecordPanel({
check: {
answers: {
authority: "the declared boundary, which each module conforms to",
evidence: "fires: a planted cross-boundary import",
freshness: "a verdict stands until the module changes",
observation: "none: a cluster is a grouping of principles",
population: "every module in the codebase",
refusal: "the rule fails the build",
},
by: [{ label: "Fail Fast", ref: "architecture:fail-fast" }],
dependsOn: [],
shape: [],
},
code: null,
kind: "principle",
layer: null,
name: "Modularity",
relations: [],
summary: null,
});
const text = nodes.map((node) => node.textContent).join("|");
expect(text).toContain("Checked by: Fail Fast");
expect(text).toContain("Depends on: Not answered");
expect(text).toContain("Population: Every module in the codebase");
expect(text).toContain("Freshness: A verdict stands until the module changes");
expect(text).toContain("Refusal: The rule fails the build");
expect(text).toContain("Observation: None, because a cluster is a grouping of principles");
expect(text).toContain("Evidence: Watched to fire: a planted cross-boundary import");
expect(text).toContain("Authoritative side: The declared boundary, which each module conforms to");
});
it("omits the summary and the relations when the record has none", () => {
expect(
createRecordPanel({
code: null,
kind: "layer",
layer: null,
name: "Structural Core",
relations: [],
summary: null,
}),
).toHaveLength(2);
});
});
describe("createChapterPanel", () => {
it("renders the chapter title, the chapter kind, the intro and the principle", () => {
const nodes = createChapterPanel({
intro: "What a board holds.",
principle: "State is derived.",
relations: [{ edges: [{ label: "createElement", ref: null }], relation: "in this codebase" }],
title: "The board",
});
const text = nodes.map((node) => node.textContent).join("|");
expect(text).toContain("The board");
expect(text).toContain("Chapter");
expect(text).toContain("What a board holds.");
expect(text).toContain("Principle: State is derived.");
expect(text).toContain("In this codebase: createElement");
expect(createChapterPanel({ intro: null, principle: null, relations: [], title: "Bare" })).toHaveLength(2);
});
});