test.content/core/converters/section.converter.test.ts
test.content/core/converters/section.converter.test.ts is a file in Codebase Testing. 131 lines of code and 0 definitions.
import {
conceptTeachers,
linkTargetsOf,
markdownSectionsOf,
moduleSectionOf,
nodeOf,
nodePartsOf,
sectionsOf,
stringsOf,
teachingNodes,
} from "@banes-lab/content/core/converters/section.converter.ts";
import { describe, expect, it } from "vitest";
import type { ContentGraph } from "@banes-lab/content/types/coverage.types.ts";
const GRAPH: ContentGraph = {
page: "methodology",
sections: {
loop: { requires: [], teaches: ["loop"], traces: [] },
stance: { narrative: true, requires: [], teaches: [], traces: [] },
},
};
describe("nodeOf and nodePartsOf", () => {
it("join a page and a section into one node and split it back", () => {
const node = nodeOf("methodology", "loop");
expect(nodePartsOf(node)).toStrictEqual({ page: "methodology", section: "loop" });
});
});
describe("teachingNodes", () => {
it("lists only the sections that teach a concept", () => {
expect(teachingNodes([GRAPH])).toStrictEqual([nodeOf("methodology", "loop")]);
});
});
describe("conceptTeachers", () => {
it("maps each concept to every node that teaches it, in page and section order", () => {
const other: ContentGraph = {
page: "architecture",
sections: { model: { requires: ["loop"], teaches: ["loop", "model"], traces: [] } },
};
const teachers = conceptTeachers([GRAPH, other]);
expect(teachers.get("loop")).toStrictEqual([nodeOf("methodology", "loop"), nodeOf("architecture", "model")]);
expect(teachers.get("model")).toStrictEqual([nodeOf("architecture", "model")]);
expect(teachers.has("stance")).toBe(false);
});
});
describe("stringsOf", () => {
it("collects every string in a nested value, and skips the records the caller names", () => {
const value = { a: "one", b: [{ c: "two" }, 3], panel: { kind: "code", text: "three" } };
expect(stringsOf(value)).toStrictEqual(["one", "two", "code", "three"]);
expect(stringsOf(value, (record) => record["kind"] === "code")).toStrictEqual(["one", "two"]);
});
});
const TABBED = {
tabs: [
{
id: "start",
intro: "Read [the loop](/disciplined-methodology#loop) first.",
sections: [
{
id: "loop",
intro: "See [a check](/disciplined-methodology/build#check) and [a check](/disciplined-methodology/build#check).",
},
{ id: "stance", intro: "No links here." },
],
},
],
};
describe("linkTargetsOf", () => {
it("reads every Markdown link target in order", () => {
expect(linkTargetsOf("[a](/x) and [b](/y#z)")).toStrictEqual(["/x", "/y#z"]);
});
it("stops at a link that never closes", () => {
expect(linkTargetsOf("[a](/x) then [b](/y")).toStrictEqual(["/x"]);
});
});
describe("sectionsOf", () => {
it("splits every identified section out of the page body, keyed by page and section id", () => {
const sections = sectionsOf("disciplined-methodology", TABBED);
expect(sections.map((section) => section.key)).toStrictEqual([
"disciplined-methodology",
"disciplined-methodology#loop",
"disciplined-methodology#stance",
]);
expect(sections[0]?.links).toStrictEqual(["/disciplined-methodology#loop"]);
expect(sections[1]?.links).toStrictEqual(["/disciplined-methodology/build#check"]);
expect(sections[2]?.links).toStrictEqual([]);
});
it("changes a section's fingerprint when its text changes, and only that section's", () => {
const before = sectionsOf("page", TABBED);
const edited = structuredClone(TABBED);
const stance = edited.tabs[0]?.sections[1];
if (stance !== undefined) {
stance.intro = "Still no links here.";
}
const after = sectionsOf("page", edited);
expect(after[0]?.fingerprint).toBe(before[0]?.fingerprint);
expect(after[1]?.fingerprint).toBe(before[1]?.fingerprint);
expect(after[2]?.fingerprint).not.toBe(before[2]?.fingerprint);
});
it("leaves numbers out of the fingerprint, so a build count never unsigns a page", () => {
const one = sectionsOf("home", { stats: [{ label: "rules", value: 64 }] });
const two = sectionsOf("home", { stats: [{ label: "rules", value: 65 }] });
expect(one[0]?.fingerprint).toBe(two[0]?.fingerprint);
});
it("keys a page without sections as one section named by the page", () => {
expect(sectionsOf("home", { lead: "Hello." }).map((section) => section.key)).toStrictEqual(["home"]);
});
});
const README = "# package\n\nLead.\n\n## Purpose\n\nOne.\n\n## Install\n\nTwo.";
describe("markdownSectionsOf", () => {
it("keys the lead and each second-level section by file and heading", () => {
expect(markdownSectionsOf("pkg", "README.md", README).map((section) => section.key)).toStrictEqual([
"pkg#README.md",
"pkg#README.md#Purpose",
"pkg#README.md#Install",
]);
});
it("changes only the fingerprint of the section whose text changed", () => {
const before = markdownSectionsOf("pkg", "README.md", README);
const after = markdownSectionsOf("pkg", "README.md", README.replace("Two.", "Three."));
expect(after[0]?.fingerprint).toBe(before[0]?.fingerprint);
expect(after[1]?.fingerprint).toBe(before[1]?.fingerprint);
expect(after[2]?.fingerprint).not.toBe(before[2]?.fingerprint);
});
});
describe("moduleSectionOf", () => {
it("keys a module by its path and fingerprints its literals", () => {
const one = moduleSectionOf("pkg", "tools/a.strings.ts", ["Posted.", "Refused."]);
expect(one.key).toBe("pkg#tools/a.strings.ts");
expect(one.page).toBe("pkg");
expect(moduleSectionOf("pkg", "tools/a.strings.ts", ["Posted."]).fingerprint).not.toBe(one.fingerprint);
});
});