# test.build/core/converters/section.converter.test.ts

> 156 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-build-core-converters-section-converter-test-ts
Source text: https://banes-lab.com/source/testing/test.build/core/converters/section.converter.test.ts.txt

Listed in [test.build/core/converters](https://banes-lab.com/api/source/testing/test.build/core/converters.md), after [test.build/core/converters/search.index.converter.test.ts](https://banes-lab.com/source/testing/test.build/core/converters/search.index.converter.test.ts.md) and before [test.build/core/converters/section.page.converter.test.ts](https://banes-lab.com/source/testing/test.build/core/converters/section.page.converter.test.ts.md).

## Contained in

- [test.build/core/converters](https://banes-lab.com/anatomy/testing/folder-testing-test-build-core-converters.md)

## Source

```typescript
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);
    });
});
```
