支持浏览器侧素材快照撰写、拒绝报告读时修复、Mermaid PNG 导出与 Word 导出加固。 Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { findMermaidFenceMatches, getMermaidImageFileName, sanitizeMermaidSource } from "./mermaid";
|
|
|
|
test("inserts a missing arrow before a bare edge label", () => {
|
|
const input = "E --> G|价值输出 V_s| H[PPO 损失]";
|
|
assert.equal(
|
|
sanitizeMermaidSource(input),
|
|
"E --> G -->|价值输出 V_s| H[PPO 损失]",
|
|
);
|
|
});
|
|
|
|
test("leaves already-valid labeled edges unchanged", () => {
|
|
const input = "E -->|价值输出 V_s| H[PPO 损失]\nG -->|状态| I";
|
|
assert.equal(sanitizeMermaidSource(input), input);
|
|
});
|
|
|
|
test("fixes a label attached to a shaped node", () => {
|
|
const input = "E[Actor 网络]|价值输出 V_s| H[PPO 损失]";
|
|
assert.equal(
|
|
sanitizeMermaidSource(input),
|
|
"E[Actor 网络] -->|价值输出 V_s| H[PPO 损失]",
|
|
);
|
|
});
|
|
|
|
test("fixes several bare labels on one line", () => {
|
|
const input = "A|x| B|y| C";
|
|
assert.equal(sanitizeMermaidSource(input), "A -->|x| B -->|y| C");
|
|
});
|
|
|
|
test("inserts a space after class when the model starts the id list with a comma", () => {
|
|
const input = " class,E,F,G,H,I,J,K loss;";
|
|
assert.equal(
|
|
sanitizeMermaidSource(input),
|
|
" class E,F,G,H,I,J,K loss;",
|
|
);
|
|
});
|
|
|
|
test("splits zip-assigned class names onto one statement per class", () => {
|
|
const input = " class A,B,C,D input,module,module,module;";
|
|
assert.equal(
|
|
sanitizeMermaidSource(input),
|
|
" class A input;\n class B,C,D module;",
|
|
);
|
|
});
|
|
|
|
test("leaves a valid class assignment and classDef alone", () => {
|
|
const input = [
|
|
" classDef input fill:#e3f2fd,stroke:#1565c0;",
|
|
" class A,B input;",
|
|
].join("\n");
|
|
assert.equal(sanitizeMermaidSource(input), input);
|
|
});
|
|
|
|
test("repairs the DAC-PPO architecture diagram class block", () => {
|
|
const input = `graph TD
|
|
A[智能体观测 o_t] --> B[个体 Encoder]
|
|
E --> G -->|价值输出 V_s| H[PPO 损失]
|
|
classDef input fill:#e3f2fd,stroke:#1565c0;
|
|
classDef module fill:#fff3e0,stroke:#e65100;
|
|
classDef loss fill:#e8f5e9,stroke:#2e7d32;
|
|
class A,B,C,D input,module,module,module;
|
|
class,E,F,G,H,I,J,K loss;`;
|
|
const output = sanitizeMermaidSource(input);
|
|
assert.match(output, /class A input;/);
|
|
assert.match(output, /class B,C,D module;/);
|
|
assert.match(output, /class E,F,G,H,I,J,K loss;/);
|
|
assert.doesNotMatch(output, /class,/);
|
|
assert.doesNotMatch(output, /input,module/);
|
|
});
|
|
|
|
test("turns mermaid source filenames into png downloads", () => {
|
|
assert.equal(getMermaidImageFileName("outputs/图2.mmd"), "图2.png");
|
|
assert.equal(getMermaidImageFileName("chart.mermaid", "png"), "chart.png");
|
|
assert.equal(getMermaidImageFileName("flowchart.png"), "flowchart.png");
|
|
});
|
|
|
|
test("finds mermaid fences for Word image replacement", () => {
|
|
const markdown = "前文\n```mermaid\nflowchart TB\nA-->B\n```\n后文\n```mmd\ngraph LR\nC-->D\n```";
|
|
const fences = findMermaidFenceMatches(markdown);
|
|
assert.equal(fences.length, 2);
|
|
assert.equal(fences[0].source, "flowchart TB\nA-->B");
|
|
assert.equal(fences[1].source, "graph LR\nC-->D");
|
|
});
|