97 lines
3.2 KiB
JavaScript
97 lines
3.2 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createRequire } from "node:module";
|
|
import test from "node:test";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const marked = require("../web/static/vendor/markdown/marked.umd.js");
|
|
globalThis.window = { marked };
|
|
|
|
const { normalizeMarkdownFences, renderMd, renderMermaidIn } = await import("../web/static/js/markdown.js");
|
|
|
|
test("repairs equal-length nested markdown fences", () => {
|
|
const broken = [
|
|
"```markdown",
|
|
"```mermaid",
|
|
"flowchart LR",
|
|
" A --> B",
|
|
"```",
|
|
"```",
|
|
"",
|
|
"正文 **正常**。",
|
|
"",
|
|
].join("\n");
|
|
|
|
const repaired = normalizeMarkdownFences(broken);
|
|
|
|
assert.match(repaired, /^````markdown\n```mermaid/);
|
|
assert.match(repaired, /```\n````\n\n正文 \*\*正常\*\*。/);
|
|
const html = renderMd(broken);
|
|
assert.match(html, /<p>正文 <strong>正常<\/strong>。<\/p>/);
|
|
});
|
|
|
|
test("preserves unrelated unclosed fences", () => {
|
|
const broken = "正文\n```python\nprint('x')\n";
|
|
assert.equal(normalizeMarkdownFences(broken), broken);
|
|
});
|
|
|
|
function fakeMermaidBlock(source = "flowchart LR\nA --> B") {
|
|
const created = [];
|
|
const doc = {
|
|
createElement(tag) {
|
|
const el = {
|
|
tag,
|
|
children: [],
|
|
attributes: {},
|
|
appendChild(child) { this.children.push(child); },
|
|
setAttribute(name, value) { this.attributes[name] = value; },
|
|
};
|
|
created.push(el);
|
|
return el;
|
|
},
|
|
};
|
|
const pre = {
|
|
isConnected: true,
|
|
ownerDocument: doc,
|
|
beforeNodes: [],
|
|
before(node) { this.beforeNodes.push(node); },
|
|
replaceWith(node) { this.replacement = node; },
|
|
};
|
|
const code = { dataset: {}, textContent: source, parentElement: pre };
|
|
const container = { querySelectorAll() { return [code]; } };
|
|
return { code, pre, container, created };
|
|
}
|
|
|
|
test("renders completed mermaid blocks with strict local configuration", async () => {
|
|
const calls = { initialize: [], render: [] };
|
|
window.mermaid = {
|
|
initialize(config) { calls.initialize.push(config); },
|
|
async render(id, source) {
|
|
calls.render.push({ id, source });
|
|
return { svg: "<svg viewBox='0 0 10 10'></svg>" };
|
|
},
|
|
};
|
|
const fixture = fakeMermaidBlock();
|
|
|
|
const result = await renderMermaidIn(fixture.container);
|
|
|
|
assert.deepEqual(result, { rendered: 1, failed: 0, unavailable: 0 });
|
|
assert.equal(calls.initialize.length, 1);
|
|
assert.equal(calls.initialize[0].securityLevel, "strict");
|
|
assert.equal(calls.initialize[0].startOnLoad, false);
|
|
assert.equal(calls.render[0].source, "flowchart LR\nA --> B");
|
|
assert.equal(fixture.pre.replacement.className, "mermaid-diagram");
|
|
assert.equal(fixture.pre.replacement.children[0].innerHTML, "<svg viewBox='0 0 10 10'></svg>");
|
|
});
|
|
|
|
test("keeps mermaid source visible when syntax is invalid", async () => {
|
|
window.mermaid.render = async () => { throw new Error("parse failed"); };
|
|
const fixture = fakeMermaidBlock("not valid");
|
|
|
|
const result = await renderMermaidIn(fixture.container);
|
|
|
|
assert.deepEqual(result, { rendered: 0, failed: 1, unavailable: 0 });
|
|
assert.equal(fixture.pre.replacement, undefined);
|
|
assert.equal(fixture.pre.beforeNodes[0].textContent, "Mermaid 图表语法有误,已保留源码。");
|
|
assert.equal(fixture.code.dataset.mermaidState, "error");
|
|
});
|