CLAUDE.md records the Nova constraints this extension is built around — established by reading Nova 14.1's bundle and probing its preview server, and expensive to re-derive: no previewer extension point, generated pages must live under the preview root, dot-directories are served, and no API reaches into a preview WebView. It also flags the one unverified assumption, that a tab showing Preview still appears in nova.workspace.textEditors, with its symptom and fix. Tests/ is the harness that built the extension, kept rather than left in a scratchpad: a stubbed Nova runtime backed by the real filesystem, node suites for generation and the four close paths, and a headless-browser render test that skips itself without a browser. It caught the ./ doubling in rebased links and checkbox state not surviving serialization. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TueXpMi7eAtgWPuMr4ii52
62 lines
3.0 KiB
JavaScript
62 lines
3.0 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
const stub = require("./nova-stub.js");
|
|
|
|
const EXT = path.join(__dirname, "..");
|
|
const ROOT = path.join(__dirname, ".tmp", "generate");
|
|
|
|
fs.rmSync(ROOT, { recursive: true, force: true });
|
|
fs.mkdirSync(path.join(ROOT, "docs"), { recursive: true });
|
|
fs.writeFileSync(path.join(ROOT, "docs", "one.md"), "# One\n\n```mermaid\ngraph TD\nA-->B\n```\n");
|
|
fs.writeFileSync(path.join(ROOT, "docs", "two.md"), "# Two\n");
|
|
|
|
const nova = stub.build({ root: ROOT, extensionPath: EXT });
|
|
global.nova = nova;
|
|
global.Disposable = stub.Disposable;
|
|
global.CompositeDisposable = stub.CompositeDisposable;
|
|
global.Range = stub.Range;
|
|
|
|
const preview = require(path.join(EXT, "Scripts", "preview.js"));
|
|
|
|
const OUT = path.join(ROOT, ".nova", "MermaidPreview");
|
|
const results = [];
|
|
function check(name, condition) {
|
|
results.push([condition ? "PASS" : "FAIL", name]);
|
|
}
|
|
|
|
// -- generate ------------------------------------------------------------
|
|
const one = { path: path.join(ROOT, "docs", "one.md"), mode: "markdown",
|
|
editor: new stub.FakeEditor(path.join(ROOT, "docs", "one.md"), fs.readFileSync(path.join(ROOT, "docs", "one.md"), "utf8")) };
|
|
const two = { path: path.join(ROOT, "docs", "two.md"), mode: "markdown",
|
|
editor: new stub.FakeEditor(path.join(ROOT, "docs", "two.md"), fs.readFileSync(path.join(ROOT, "docs", "two.md"), "utf8")) };
|
|
|
|
const pathOne = preview.generate(one);
|
|
check("generate writes a page", fs.existsSync(pathOne));
|
|
check("assets installed", fs.existsSync(path.join(OUT, "assets", "vendor", "mermaid.min.js")));
|
|
check("stylesheet installed", fs.existsSync(path.join(OUT, "assets", "preview.css")));
|
|
check("page references relative assets", fs.readFileSync(pathOne, "utf8").includes('src="assets/vendor/mermaid.min.js"'));
|
|
check("page embeds the markdown", fs.readFileSync(pathOne, "utf8").includes("graph TD"));
|
|
|
|
const mtime = fs.statSync(path.join(OUT, "assets", "preview.css")).mtimeMs;
|
|
const pathTwo = preview.generate(two);
|
|
check("second page has its own filename", pathOne !== pathTwo && fs.existsSync(pathTwo));
|
|
check("assets not reinstalled when current", fs.statSync(path.join(OUT, "assets", "preview.css")).mtimeMs === mtime);
|
|
|
|
// -- discard -------------------------------------------------------------
|
|
preview.discard(pathOne);
|
|
check("discard removes that page", !fs.existsSync(pathOne));
|
|
check("discard keeps the other page", fs.existsSync(pathTwo));
|
|
check("discard keeps assets while a page remains", fs.existsSync(path.join(OUT, "assets", "preview.css")));
|
|
|
|
preview.discard(pathTwo);
|
|
check("last discard removes the directory", !fs.existsSync(OUT));
|
|
check("workspace otherwise untouched", fs.existsSync(path.join(ROOT, "docs", "one.md")));
|
|
|
|
// -- discard is safe to repeat -------------------------------------------
|
|
let threw = false;
|
|
try { preview.discard(pathTwo); } catch (e) { threw = true; }
|
|
check("discarding twice does not throw", !threw);
|
|
|
|
for (const [status, name] of results) console.log(`${status} ${name}`);
|
|
process.exitCode = results.some(([s]) => s === "FAIL") ? 1 : 0;
|