// docs-list tests cover source docs metadata discovery for docs-aware tooling. import { execFileSync, spawnSync } from "node:child_process"; import { existsSync, mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { renderDocsHeadingMap } from "../../scripts/docs-list.js"; import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js"; const tempDirs: string[] = []; const repoRoot = path.resolve(import.meta.dirname, "../.."); const docsListScriptPath = path.join(repoRoot, "scripts", "docs-list.js"); function makeTempRepoRoot(prefix: string): string { return makeTempDir(tempDirs, prefix); } function runDocsList(cwd: string, args: string[] = []): string { return execFileSync(process.execPath, [docsListScriptPath, ...args], { cwd, encoding: "utf8", }); } afterEach(() => { cleanupTempDirs(tempDirs); }); describe("docs-list", () => { it("reports a concise error outside a source checkout", () => { const tempRepoRoot = makeTempRepoRoot("openclaw-docs-list-missing-"); const result = spawnSync(process.execPath, [docsListScriptPath], { cwd: tempRepoRoot, encoding: "utf8", }); expect(result.status).toBe(1); expect(result.stderr).toBe("docs:list: missing docs directory. Run from repo root.\n"); }); it("prints single-line read_when strings as read hints", () => { const tempRepoRoot = makeTempRepoRoot("openclaw-docs-list-"); mkdirSync(path.join(tempRepoRoot, "docs"), { recursive: true }); writeFileSync( path.join(tempRepoRoot, "docs", "page.md"), `--- summary: "Single-line read_when page" read_when: "Read this page when the hint is inline." --- `, "utf8", ); const output = runDocsList(tempRepoRoot); expect(output).toContain("page.md - Single-line read_when page"); expect(output).toContain("Read when: Read this page when the hint is inline."); }); it("renders the publish docs map on demand without creating a mirror", () => { const tempRepoRoot = makeTempRepoRoot("openclaw-docs-headings-"); mkdirSync(path.join(tempRepoRoot, "docs", "nested"), { recursive: true }); writeFileSync( path.join(tempRepoRoot, "docs", "page.md"), `--- summary: "Page" --- # Visible title ## \`API[*]\` ### ipt>alert(1) #### \`![label](https://example.test/image)\` \`\`\`md ### Hidden fenced heading \`\`\` `, "utf8", ); writeFileSync(path.join(tempRepoRoot, "docs", "nested", "index.mdx"), "# Nested\n"); writeFileSync(path.join(tempRepoRoot, "docs", "AGENTS.md"), "# Instructions\n"); const output = runDocsList(tempRepoRoot, ["--headings"]); expect(output).toContain("## page.md\n\n- Route: /page"); expect(output).toContain(" - H1: Visible title"); expect(output).toContain(" - H2: `API[*]` <script>alert(1)</script>"); expect(output).toContain(" - H3: <scr<script>ipt>alert(1)</script>"); expect(output).toContain(" - H4: `![label](https://example.test/image)`"); expect(output).toContain("## nested/index.mdx\n\n- Route: /nested"); expect(output).not.toContain("Hidden fenced heading"); expect(output).not.toContain("AGENTS.md"); expect(existsSync(path.join(tempRepoRoot, "docs", "docs_map.md"))).toBe(false); }); it("normalizes injected Windows paths for nested page routes", () => { const tempRepoRoot = makeTempRepoRoot("openclaw-docs-headings-windows-"); const docsDir = path.join(tempRepoRoot, "docs"); mkdirSync(path.join(docsDir, "nested"), { recursive: true }); writeFileSync(path.join(docsDir, "nested", "index.mdx"), "# Nested index\n"); writeFileSync(path.join(docsDir, "nested", "page.md"), "# Nested page\n"); const output = renderDocsHeadingMap(docsDir, { relativePath: (base, fullPath) => path.relative(base, fullPath).replace(/[\\/]+/gu, "\\"), }); expect(output).toContain("## nested/index.mdx\n\n- Route: /nested"); expect(output).toContain("## nested/page.md\n\n- Route: /nested/page"); expect(output).not.toContain("\\"); }); });