testFilesInAMirror
A mirror keeps its Markdown and images as files of the codebase, written back as they change.
import { okIn, tempDir } from "#codebase/tcmts/cli/test/e2e/harness"; import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; export const testFilesInAMirror = () => { const root = tempDir("files mirror"); const proj = path.join(root, "proj"); const cbDir = path.join(root, "cb"); for (const d of ["src", "docs", "media", "ext"]) fs.mkdirSync(path.join(proj, d), { recursive: true }); fs.mkdirSync(cbDir); fs.writeFileSync(path.join(proj, "package.json"), JSON.stringify({ type: "module" })); fs.writeFileSync(path.join(proj, "src", "math.ts"), "export const square = (x: number): number => x * x;\n"); fs.writeFileSync(path.join(proj, "docs", "guide.md"), "# Guide\n\nSee {@link math.square} and [the FAQ](faq.md#why).\n"); fs.writeFileSync(path.join(proj, "docs", "faq.md"), "# FAQ\n\n## Why\n\nBack to [the guide](guide.md).\n"); const png = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 1, 2, 255]); fs.writeFileSync(path.join(proj, "media", "logo.png"), png); // everything in a marked directory is a file, code included fs.writeFileSync(path.join(proj, "ext", ".tcm-files"), ""); fs.writeFileSync(path.join(proj, "ext", "main.js"), "module.exports = 1;\n"); assert.equal(spawnSync("git", ["-C", proj, "init", "-q"]).status, 0); const ok = (...args: string[]) => okIn(cbDir, ...args); const read = (f: string) => fs.readFileSync(path.join(proj, f), "utf8"); ok("init"); fs.rmSync(path.join(cbDir, "scratch.ts")); ok("import", proj, "--into", "app", "--mirror"); assert.match(ok("ls", "app"), /app\.docs\.faq +#\w+ +: md[\s\S]*app\.docs\.guide +#\w+ +: md[\s\S]*app\.ext\.main +#\w+ +: js[\s\S]*app\.media\.logo +#\w+ +: png/); assert.match(ok("mirror", "--check"), /the files hash to what the codebase holds/); // a rename of the code reaches the page that names it, written as short as it reads ok("mv", "app.math.square", "app.math.sq"); assert.equal(read("docs/guide.md"), "# Guide\n\nSee {@link math.sq} and [the FAQ](faq.md#why).\n"); // a page moved is a file moved, and the pages linking to it follow ok("mv", "app.docs.faq", "app.docs.questions"); assert.ok(!fs.existsSync(path.join(proj, "docs", "faq.md"))); assert.equal(read("docs/questions.md"), "# FAQ\n\n## Why\n\nBack to [the guide](guide.md).\n"); assert.match(read("docs/guide.md"), /\[the FAQ\]\(questions\.md#why\)/); // an edit by hand is followed fs.appendFileSync(path.join(proj, "docs", "questions.md"), "\nMore.\n"); assert.match(ok("view", "app.docs.questions"), /More\./); // bytes are kept as bytes ok("export", "app.media", path.join(root, "out")); assert.ok(fs.readFileSync(path.join(root, "out", "logo.png")).equals(png)); // one file is written by its name ok("export", "app.docs.guide", path.join(root, "one")); assert.deepEqual(fs.readdirSync(path.join(root, "one")), ["guide.md"]); // a marked directory removed, its mark with it: its files go too fs.rmSync(path.join(proj, "ext"), { recursive: true }); assert.match(ok("ls", "app"), /- removed +app\.ext\.main \(gone from the files\)/); assert.doesNotMatch(ok("ls", "app"), /app\.ext/); assert.match(ok("mirror", "--check"), /the files hash to what the codebase holds/); }