Files
2026-07-13 12:38:34 +08:00

155 lines
5.0 KiB
TypeScript

/**
* Navigation completeness tests.
*
* Validates that every entry in meta.json files maps to a real .mdx file or
* directory, and that every content file is referenced in its parent meta.json.
*/
import { describe, test, expect } from "bun:test";
import { readdir, readFile, stat } from "fs/promises";
import { join, basename, dirname, relative } from "path";
const CONTENT_DIR = join(import.meta.dir, "../../content/docs");
/** Separator entries in meta.json start with --- */
function isSeparator(entry: string): boolean {
return entry.startsWith("---");
}
/** Recursively find all meta.json files under a directory */
async function findMetaFiles(dir: string): Promise<string[]> {
const results: string[] = [];
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...(await findMetaFiles(fullPath)));
} else if (entry.name === "meta.json") {
results.push(fullPath);
}
}
return results;
}
/** Check if a path exists as a file or directory */
async function exists(path: string): Promise<boolean> {
try {
await stat(path);
return true;
} catch {
return false;
}
}
describe("Navigation - meta.json validity", () => {
test("root meta.json entries all resolve to files or directories", async () => {
const metaPath = join(CONTENT_DIR, "meta.json");
const meta = JSON.parse(await readFile(metaPath, "utf-8"));
const missing: string[] = [];
for (const entry of meta.pages as string[]) {
if (isSeparator(entry)) continue;
if (entry === "...") continue;
const asFile = join(CONTENT_DIR, `${entry}.mdx`);
const asDir = join(CONTENT_DIR, entry);
const fileExists = await exists(asFile);
const dirExists = await exists(asDir);
if (!fileExists && !dirExists) {
missing.push(entry);
}
}
expect(missing).toEqual([]);
});
test("all nested meta.json entries resolve to files or directories", async () => {
const metaFiles = await findMetaFiles(CONTENT_DIR);
const errors: string[] = [];
for (const metaPath of metaFiles) {
const dir = dirname(metaPath);
const meta = JSON.parse(await readFile(metaPath, "utf-8"));
const relDir = relative(CONTENT_DIR, dir);
for (const entry of (meta.pages || []) as string[]) {
if (isSeparator(entry)) continue;
// Handle "..." (rest) entries which are valid fumadocs syntax
if (entry === "...") continue;
const asFile = join(dir, `${entry}.mdx`);
const asDir = join(dir, entry);
const fileExists = await exists(asFile);
const dirExists = await exists(asDir);
if (!fileExists && !dirExists) {
errors.push(`${relDir}/meta.json → "${entry}" (no .mdx file or directory found)`);
}
}
}
expect(errors).toEqual([]);
});
test("no orphan .mdx files missing from meta.json", async () => {
const metaFiles = await findMetaFiles(CONTENT_DIR);
const orphans: string[] = [];
// Check root level
const rootMetaPath = join(CONTENT_DIR, "meta.json");
const rootMeta = JSON.parse(await readFile(rootMetaPath, "utf-8"));
const rootEntries = new Set(
(rootMeta.pages as string[]).filter((e: string) => !isSeparator(e) && e !== "...")
);
// "..." means "include everything else", so skip orphan check for root
if (rootEntries.has("...")) return;
const rootFiles = await readdir(CONTENT_DIR, { withFileTypes: true });
for (const file of rootFiles) {
if (file.name === "meta.json") continue;
const name = file.isFile() ? basename(file.name, ".mdx") : file.name;
if (file.isFile() && !file.name.endsWith(".mdx")) continue;
if (!rootEntries.has(name)) {
orphans.push(`docs/${file.name}`);
}
}
// Check each nested directory that has a meta.json
for (const metaPath of metaFiles) {
if (metaPath === rootMetaPath) continue;
const dir = dirname(metaPath);
const meta = JSON.parse(await readFile(metaPath, "utf-8"));
const entries = new Set(
((meta.pages || []) as string[]).filter((e: string) => !isSeparator(e))
);
// "..." means "include everything else", so skip orphan check
if (entries.has("...")) continue;
const files = await readdir(dir, { withFileTypes: true });
const relDir = relative(CONTENT_DIR, dir);
for (const file of files) {
if (file.name === "meta.json") continue;
const name = file.isFile() ? basename(file.name, ".mdx") : file.name;
if (file.isFile() && !file.name.endsWith(".mdx")) continue;
if (!entries.has(name)) {
orphans.push(`${relDir}/${file.name}`);
}
}
}
if (orphans.length > 0) {
console.warn(
`Found ${orphans.length} orphan file(s) not in any meta.json:\n` +
orphans.map((o) => ` - ${o}`).join("\n")
);
}
// Warn but don't fail — orphans aren't necessarily bugs
expect(orphans.length).toBeLessThan(20);
});
});