Files
HiveGrid-Wireframes/hivegrid-cloud/scripts/docs-link-check.test.ts
T

128 lines
4.2 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { afterEach, describe, it } from "vitest";
import { checkDocsLinks, formatDocsLinkCheckResult } from "./docs-link-check";
const fixtures: string[] = [];
function createFixture(files: Record<string, string>): string {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "docs-link-check-"));
fixtures.push(rootDir);
for (const [relativePath, content] of Object.entries(files)) {
const absolutePath = path.join(rootDir, relativePath);
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
fs.writeFileSync(absolutePath, content);
}
return rootDir;
}
afterEach(() => {
while (fixtures.length > 0) {
fs.rmSync(fixtures.pop() as string, { recursive: true, force: true });
}
});
describe("docs-link-check", () => {
it("passes for valid local Markdown links and anchors", () => {
const rootDir = createFixture({
"README.md": "# Root\nSee [plugins](docs/plugins.md#plugin-boundaries).",
"docs/plugins.md": "# Plugin Boundaries\nSee [feature inventory](feature-inventory.md).",
"docs/feature-inventory.md": "# Feature Inventory\n",
});
const result = checkDocsLinks(rootDir, ["README.md", "docs"]);
const output = formatDocsLinkCheckResult(result);
assert.deepEqual(result.failures, []);
assert.match(output, /^PASS docs link check/);
});
it("fails with file context for missing local paths", () => {
const rootDir = createFixture({
"README.md": "# Root\nSee [missing](docs/missing.md).",
});
const result = checkDocsLinks(rootDir, ["README.md", "docs"]);
assert.match(result.failures.join("\n"), /README\.md:\d+:\d+: broken local link "docs\/missing\.md"/);
});
it("fails with file context for missing local anchors", () => {
const rootDir = createFixture({
"README.md": "# Root\nSee [plugins](docs/plugins.md#missing-anchor).",
"docs/plugins.md": "# Plugin Boundaries\n",
});
const result = checkDocsLinks(rootDir, ["README.md", "docs"]);
assert.match(result.failures.join("\n"), /README\.md:\d+:\d+: broken local anchor "#missing-anchor"/);
});
it("ignores external URLs and special link schemes", () => {
const rootDir = createFixture({
"README.md": [
"# Root",
"See [external](https://example.com/missing#anchor).",
"Send [mail](mailto:help@example.com) or [call](tel:+15555550100).",
"Jump to [local](#root).",
].join("\n"),
});
const result = checkDocsLinks(rootDir, ["README.md"]);
assert.deepEqual(result.failures, []);
});
it("fails closed when a configured docs root is missing", () => {
const rootDir = createFixture({
"README.md": "# Root\n",
});
const result = checkDocsLinks(rootDir, ["README.md", "docs"]);
assert.match(result.failures.join("\n"), /docs-roots: missing configured docs root docs/);
});
it("fails closed when no Markdown files are checked", () => {
const rootDir = createFixture({
"src/index.ts": "export {};",
});
const result = checkDocsLinks(rootDir, ["docs"]);
assert.match(result.failures.join("\n"), /docs-roots: checked 0 Markdown files/);
});
it("exits non-zero from the CLI when local links are broken", () => {
const rootDir = createFixture({
"README.md": "# Root\nSee [missing](docs/missing.md).",
"AGENTS.md": "# Agents\n",
"SECURITY.md": "# Security\n",
"CHANGELOG.md": "# Changelog\n",
"docs/index.md": "# Docs\n",
"guides/index.md": "# Guides\n",
"llm-docs/index.md": "# LLM Docs\n",
});
let output = "";
assert.throws(() => {
try {
execFileSync("pnpm", ["exec", "tsx", path.resolve("scripts/docs-link-check.ts"), rootDir], { encoding: "utf8", stdio: "pipe" });
} catch (error) {
if (error && typeof error === "object") {
const failure = error as { stderr?: Buffer | string; stdout?: Buffer | string };
output = `${String(failure.stderr ?? "")}\n${String(failure.stdout ?? "")}`;
}
throw error;
}
});
assert.match(output, /broken local link "docs\/missing\.md"/);
});
});