testFilesInAMirror

tcmts.cli.test.e2e.files.testFilesInAMirror · #5fkisah5 · term

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/);
}

uses