# test.build/core/validators/site.validator.test.ts

> 84 lines of code and 0 definitions.

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

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

## Contained in

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

## Source

```typescript
import { ADDRESS, EMPTY, PAGE } from "../converters/site.fixture.ts";
import {
    checkAlternates,
    checkIndexable,
    checkLinks,
    checkPage,
    checkSearchKey,
    checkSitemap,
    checkText,
} from "@banes-lab/build-scripts/core/validators/site.validator.ts";
import { describe, expect, it } from "vitest";
import type { Seen } from "@banes-lab/build-scripts/types/validation.types.ts";
import { malformedSearchKey } from "@banes-lab/build-scripts/configuration/strings/site.strings.ts";
import { readArtifact } from "@banes-lab/build-scripts/core/converters/build.converter.ts";

const TERMS_FILE = "terms.html";
const EMPTY_FILE = "empty.html";
const SITEMAP_FILE = "sitemap.xml";
const EMPTY_FINDINGS = 4;

const fresh = function fresh(): Seen {
    return { descriptions: new Set(), titles: new Set() };
};

describe("checkSearchKey", () => {
    it("accepts a key of letters, digits and hyphens within its length, and reports a short, long or malformed one", () => {
        expect(checkSearchKey("k.txt", "abcd-1234")).toStrictEqual([]);
        expect(checkSearchKey("k.txt", "abc")).toStrictEqual([{ file: "k.txt", message: malformedSearchKey(3, 8, 128) }]);
        expect(checkSearchKey("k.txt", "a".repeat(129))).toHaveLength(1);
        expect(checkSearchKey("k.txt", "abcd 1234\n")).toHaveLength(1);
    });
});

describe("checkAlternates", () => {
    it("wants the Markdown and JSON alternate links to name the route's own alternates", () => {
        const twins = { json: "https://example.test/json/terms", markdown: `${ADDRESS}.md` };
        const linked = `<!doctype html><html><head><link rel="alternate" type="text/markdown" href="${twins.markdown}"><link rel="alternate" type="application/json" href="${twins.json}"></head><body></body></html>`;
        expect(checkAlternates(TERMS_FILE, readArtifact(linked), twins)).toStrictEqual([]);
        expect(checkAlternates(TERMS_FILE, readArtifact(PAGE), twins)).toHaveLength(2);
    });
});

describe("checkIndexable", () => {
    it("wants a served route indexable and an error page excluded", () => {
        const served = readArtifact(PAGE);
        const excluded = { ...served, robots: "noindex, nofollow" };
        expect(checkIndexable(TERMS_FILE, served, true)).toStrictEqual([]);
        expect(checkIndexable(TERMS_FILE, excluded, true)).toHaveLength(1);
        expect(checkIndexable("404.html", excluded, false)).toStrictEqual([]);
        expect(checkIndexable("404.html", served, false)).toHaveLength(1);
    });
});

describe("checkPage", () => {
    it("accepts a page with text, a unique title and description and the right canonical", () => {
        expect(checkPage(TERMS_FILE, readArtifact(PAGE), ADDRESS, fresh())).toStrictEqual([]);
    });

    it("reports an empty shell, a blank title, a missing description and a wrong canonical", () => {
        const findings = checkPage(EMPTY_FILE, readArtifact(EMPTY), ADDRESS, fresh());
        expect(findings).toHaveLength(EMPTY_FINDINGS);
        expect(findings.every((finding) => finding.file === EMPTY_FILE)).toBe(true);
    });

    it("reports a title or description another page already used", () => {
        const seen = fresh();
        checkPage(TERMS_FILE, readArtifact(PAGE), ADDRESS, seen);
        expect(checkPage("copy.html", readArtifact(PAGE), ADDRESS, seen)).toHaveLength(2);
    });
});

describe("checkSitemap", () => {
    it("wants every page listed and nothing else", () => {
        const stray = `<urlset><url><loc>${ADDRESS}</loc></url><url><loc>https://example.test/stray</loc></url></urlset>`;
        expect(checkSitemap(SITEMAP_FILE, stray, [ADDRESS, "https://example.test/"])).toHaveLength(2);
        expect(
            checkSitemap(SITEMAP_FILE, `<urlset><url><loc>${ADDRESS}</loc></url></urlset>`, [ADDRESS]),
        ).toStrictEqual([]);
    });
});

describe("checkText", () => {
    it("reports each required fragment the text lacks", () => {
        expect(checkText("llms.txt", "one two", ["one", "three"])).toHaveLength(1);
    });
});

describe("checkLinks", () => {
    it("reports internal links to routes nothing serves, ignoring assets, payloads, fragments and external links", () => {
        const html = `<a href="/terms">ok</a><a href="/terms/guide#setup">ok</a><a href="/gone">bad</a><a href="#top">frag</a><a href="/assets/x.png">asset</a><a href="/json/terms">payload</a><a href="https://example.test/">ext</a>`;
        expect(checkLinks("terms.html", readArtifact(html), new Set(["/", "/terms", "/terms/guide"]))).toStrictEqual([
            { file: "terms.html", message: "Links to /gone, which no pre-rendered route serves." },
        ]);
    });
});
```
