test.build/core/converters/section.converter.test.ts
test.build/core/converters/section.converter.test.ts is a file in Codebase Testing. 156 lines of code and 0 definitions.
import type { DiscoveredPage, Discovery } from "@banes-lab/build-scripts/types/site.types.ts";
import { describe, expect, it } from "vitest";
import {
documentPages,
sectionEdges,
sectionLeaves,
sectionPlans,
tabsOf,
withoutHeading,
} from "@banes-lab/build-scripts/core/converters/section.converter.ts";
import { createLinker } from "@banes-lab/build-scripts/core/resolvers/link.resolver.ts";
import { placementsOf } from "@banes-lab/build-scripts/core/converters/location.converter.ts";
import { tabIndexPlans } from "@banes-lab/build-scripts/core/converters/index.page.converter.ts";
const SITE = "https://example.test";
const LOOP = "chapter:/method#loop";
const GATE = "chapter:/method/build#gate";
const bodies = function bodies(path: string, ids: readonly string[]): readonly string[] {
return ids.map((id) => `## ${id}\n\nSee [the loop](/method#loop) on ${path}.`);
};
const METHOD: DiscoveredPage = {
content: {
kind: "tabbed",
tabs: [
{ id: "start", label: "Start", sections: [{ id: "loop", intro: "The loop. More.", title: "The loop" }] },
{
id: "build",
label: "Build",
sections: [{ id: "gate", intro: "See [the loop](/method#loop).", title: "The gate" }],
},
],
},
description: "The method.",
id: "method",
label: "Method",
markdown: "",
page: "method",
path: "/method",
tab: null,
title: "Method — Site",
};
const FAQ: DiscoveredPage = {
content: { kind: "document", sections: [{ id: "origin", title: "Origin" }] },
description: "Questions.",
id: "faq",
label: "FAQ",
markdown: "",
page: "faq",
path: "/faq",
tab: null,
title: "FAQ — Site",
};
const BUILD_ROUTE = { ...METHOD, label: "Build", path: "/method/build", tab: "build", title: "Build — Method — Site" };
const DISCOVERY: Discovery = {
author: "Ada",
consent: "Yes.",
name: "Site",
pages: [METHOD, FAQ],
routes: [METHOD, BUILD_ROUTE, FAQ],
site: SITE,
summary: "A site.",
};
const summarize = (markdown: string): string | null => markdown.slice(0, markdown.indexOf(".") + 1) || null;
describe("tabsOf and documentPages", () => {
it("reads a tabbed page's tabs at their route paths and a document page as one tab without an id", () => {
expect(tabsOf(DISCOVERY, METHOD).map((tab) => [tab.id, tab.path])).toStrictEqual([
["start", "/method"],
["build", "/method/build"],
]);
expect(tabsOf(DISCOVERY, FAQ).map((tab) => [tab.id, tab.path])).toStrictEqual([[null, "/faq"]]);
expect(documentPages(DISCOVERY).map((page) => page.id)).toStrictEqual(["faq"]);
});
});
describe("sectionPlans", () => {
it("gives every section a chapter ref, its page href with a fragment and a leaf address with the tab explicit", () => {
const plans = sectionPlans(DISCOVERY, summarize);
expect(
plans.map((plan) => [plan.identity.ref, plan.identity.address.json, plan.identity.summary]),
).toStrictEqual([
[LOOP, "/json/method/start/loop", "The loop."],
[GATE, "/json/method/build/gate", "See [the loop](/method#loop)."],
["chapter:/faq#origin", "/json/faq/origin", null],
]);
});
});
describe("withoutHeading", () => {
it("drops the section's own heading line and keeps the body", () => {
expect(withoutHeading("\n## The loop\n\nBody.\n")).toBe("Body.");
expect(withoutHeading("Body only.")).toBe("Body only.");
});
});
describe("sectionEdges", () => {
it("records each section's outgoing links and gives each target the sections that link to it", () => {
const plans = sectionPlans(DISCOVERY, summarize);
const linker = createLinker(
plans.map((plan) => plan.identity),
SITE,
(href) => href,
);
const { incoming, outgoing } = sectionEdges(plans, linker);
expect(outgoing.get(GATE)?.map((link) => link.ref)).toStrictEqual([LOOP]);
expect(incoming.get(LOOP)?.map((link) => link.ref)).toStrictEqual([GATE]);
expect(incoming.has(GATE)).toBe(false);
});
});
describe("sectionLeaves", () => {
it("links sections both ways, opens on the section title and relinks the body to leaves", () => {
const plans = sectionPlans(DISCOVERY, summarize);
const linker = createLinker(
plans.map((plan) => plan.identity),
SITE,
(href) => href,
);
const numbers = new Map([[GATE, "2"]]);
const placements = placementsOf(
tabIndexPlans(DISCOVERY, plans),
plans.map((plan) => plan.identity),
SITE,
);
const leaves = sectionLeaves(plans, {
bodies,
evidence: () => [],
folder: (ref) => (ref === LOOP ? { containedIn: null, contains: [linker.link("The gate", GATE)] } : null),
graphs: [],
grounds: (ref) => (ref === LOOP ? [linker.link("The gate", GATE)] : []),
linkedBy: (ref) => (ref === LOOP ? [linker.link("The gate", GATE)] : []),
linker,
links: (ref) => (ref === GATE ? [linker.link("The loop", LOOP)] : []),
navigation: () => null,
numbers,
placement: (ref) => placements.get(ref) ?? null,
});
const gate = leaves.find((leaf) => leaf.identity.ref === GATE);
const loop = leaves.find((leaf) => leaf.identity.ref === LOOP);
expect(gate?.data).toMatchObject({
number: "2",
page: "method",
relations: [{ links: [{ ref: LOOP }], relation: "links-to" }],
siblings: { next: null, previous: null },
tab: "build",
up: { json: `${SITE}/json/api/pages/method/build`, ref: "api:/method/build" },
});
expect(loop?.data).toMatchObject({
number: null,
relations: [
{ links: [{ ref: GATE }], relation: "contains" },
{ links: [{ ref: GATE }], relation: "linked-from" },
{ links: [{ ref: GATE }], relation: "evidence-for" },
],
});
expect(loop?.markdown).toContain("## Contains");
expect(gate?.data).not.toHaveProperty("contains");
expect(gate?.data).not.toHaveProperty("linkedBy");
expect(gate?.markdown).toContain(`[the loop](${SITE}/method/start/loop.md)`);
expect(gate?.markdown?.startsWith("# The gate")).toBe(true);
});
});