# test.content/core/validators/panel.validator.test.ts

> 67 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-content-core-validators-panel-validator-test-ts
Source text: https://banes-lab.com/source/testing/test.content/core/validators/panel.validator.test.ts.txt

Listed in [test.content/core/validators](https://banes-lab.com/api/source/testing/test.content/core/validators.md), after [test.content/core/validators/link.validator.test.ts](https://banes-lab.com/source/testing/test.content/core/validators/link.validator.test.ts.md) and before [test.content/core/validators/writing.validator.test.ts](https://banes-lab.com/source/testing/test.content/core/validators/writing.validator.test.ts.md).

## Contained in

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

## Source

```typescript
import {
    PANEL_CITE_UNKNOWN,
    PANEL_TAG_SEPARATED,
    PANEL_TAG_TOO_LONG,
    PANEL_TAG_TWICE,
    PANEL_UNCITED,
    PANEL_UNTAGGED,
} from "@banes-lab/content/configuration/strings/panel.strings.ts";
import { describe, expect, it } from "vitest";
import { panelCount, validatePanels } from "@banes-lab/content/core/validators/panel.validator.ts";
import type { PanelSection } from "@banes-lab/content/types/panel.types.ts";
import { readPanelSections } from "@banes-lab/content/core/loaders/panel.loader.ts";

const PAGE = "page";

const CLEAN: PanelSection = {
    id: "clean",
    panels: [
        { kind: "code", tag: "node shape" },
        { kind: "mermaid", tag: "the loop" },
    ],
    prose: "The [node shape] carries its gate, and [the loop] walks it.",
};

const FAULTY: PanelSection = {
    id: "faulty",
    panels: [
        { kind: "code", tag: null },
        { kind: "code", tag: "a title that runs on and on" },
        { kind: "mermaid", tag: "the loop · cited" },
        { kind: "figure", tag: "orphan" },
        { kind: "code", tag: "repeated" },
        { kind: "code", tag: "repeated" },
    ],
    prose: "Only [repeated] and [the loop · cited] and [a title that runs on and on] are cited here.",
};

describe("validatePanels", () => {
    it("passes a section whose every panel carries a short tag the prose cites", () => {
        expect(validatePanels(PAGE, [CLEAN])).toStrictEqual([]);
    });

    it("names the untagged, the long, the separated, the uncited and the duplicated tag", () => {
        const messages = validatePanels(PAGE, [FAULTY]).map((finding) => finding.message);
        expect(messages).toStrictEqual([
            PANEL_UNTAGGED,
            `${PANEL_TAG_TOO_LONG}a title that runs on and on`,
            `${PANEL_TAG_SEPARATED}the loop · cited`,
            `${PANEL_UNCITED}orphan`,
            `${PANEL_TAG_TWICE}repeated`,
        ]);
        expect(validatePanels(PAGE, [FAULTY]).every((finding) => finding.section === FAULTY.id)).toBe(true);
    });

    it("names a cite that matches no panel in its section, once, and reads a bracket followed by a link target as a link", () => {
        const section: PanelSection = {
            id: "cites",
            panels: [{ kind: "code", tag: "node shape" }],
            prose: "The [node shape] and [a missing figure] and [a missing figure] and [a page](/pag) read here.",
        };
        expect(validatePanels(PAGE, [section])).toStrictEqual([
            { message: `${PANEL_CITE_UNKNOWN}a missing figure`, page: PAGE, section: "cites" },
        ]);
    });

    it("counts the panels across sections", () => {
        expect(panelCount([CLEAN, FAULTY])).toBe(8);
    });
});

describe("readPanelSections", () => {
    it("returns null for a page that was never built", () => {
        expect(readPanelSections("no-such-page")).toBeNull();
    });
});
```
