Files
Mermaid-Preview-Nova/Tests/close.test.js
T
thatguygriffandClaude Opus 5 aebc72b85e Add CLAUDE.md and the test harness
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
2026-08-20 23:02:08 -03:00

78 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", "close");
const OUT = path.join(ROOT, ".nova", "MermaidPreview");
fs.rmSync(ROOT, { recursive: true, force: true });
fs.mkdirSync(path.join(ROOT, "docs"), { recursive: true });
const MD = path.join(ROOT, "docs", "note.md");
fs.writeFileSync(MD, "# Note\n\n```mermaid\ngraph TD\nA-->B\n```\n");
const nova = stub.build({ root: ROOT, extensionPath: EXT });
global.nova = nova;
global.Disposable = stub.Disposable;
global.CompositeDisposable = stub.CompositeDisposable;
global.Range = stub.Range;
require(path.join(EXT, "Scripts", "main.js")).activate();
const results = [];
const check = (name, ok) => results.push([ok ? "PASS" : "FAIL", name]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const pages = () => (fs.existsSync(OUT) ? fs.readdirSync(OUT).filter((f) => f.endsWith(".html")) : []);
function openSource() {
const editor = new stub.FakeEditor(MD, fs.readFileSync(MD, "utf8"));
nova.workspace.textEditors.push(editor);
nova.workspace.activeTextEditor = editor;
return editor;
}
function pageEditor() {
return nova.workspace.textEditors.find((e) => e.document.path.endsWith(".html"));
}
(async () => {
// -- 1. closing the generated page removes it ------------------------
let source = openSource();
await nova.__invoke("mermaidpreview.preview");
check("preview opens a page", pages().length === 1);
const page = pageEditor();
nova.__close(page);
check("page survives the grace period", pages().length === 1);
await sleep(3000);
check("closing the page tab deletes it", pages().length === 0);
check("last page out removes the directory", !fs.existsSync(OUT));
// -- 2. a tab flipping to preview must not count as a close ----------
nova.workspace.textEditors = [];
source = openSource();
await nova.__invoke("mermaidpreview.preview");
const flipping = pageEditor();
flipping.fireDestroy(); // destroyed, but the tab is still open
await sleep(3000);
check("a rebuilt editor is not mistaken for a close", pages().length === 1);
// -- 3. closing the Markdown file cleans up too ----------------------
nova.__close(source);
check("closing the source deletes the page immediately", pages().length === 0);
check("directory removed with it", !fs.existsSync(OUT));
// -- 4. cleanup can be turned off ------------------------------------
nova.workspace.textEditors = [];
nova.config.set("mermaidpreview.cleanUpOnClose", false);
source = openSource();
await nova.__invoke("mermaidpreview.preview");
const kept = pageEditor();
nova.__close(kept);
nova.__close(source);
await sleep(3000);
check("nothing is deleted when the setting is off", pages().length === 1);
for (const [status, name] of results) console.log(`${status} ${name}`);
process.exitCode = results.some(([s]) => s === "FAIL") ? 1 : 0;
})();