feat: 实现 Markdown 渲染核心
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { renderMarkdown } from "../src/render-markdown.js";
|
||||
|
||||
describe("renderMarkdown", () => {
|
||||
it("渲染常用 Markdown 扩展并识别能力", () => {
|
||||
const result = renderMarkdown(`
|
||||
# 示例文档
|
||||
|
||||
| 名称 | 状态 |
|
||||
| --- | --- |
|
||||
| PDF | 可用 |
|
||||
|
||||
- [x] 已完成
|
||||
- [ ] 待处理
|
||||
|
||||
\`\`\`ts
|
||||
const answer = 42;
|
||||
\`\`\`
|
||||
|
||||
脚注引用[^note]。
|
||||
|
||||
[^note]: 脚注正文。
|
||||
`);
|
||||
|
||||
expect(result.articleHtml).toContain('id="write"');
|
||||
expect(result.bodyHtml).toContain("<table>");
|
||||
expect(result.bodyHtml).toContain("md-task-list-item");
|
||||
expect(result.bodyHtml).toContain("hljs-keyword");
|
||||
expect(result.bodyHtml).toContain("footnote");
|
||||
expect(result.metadata.title).toBe("示例文档");
|
||||
expect(result.features).toEqual(
|
||||
expect.arrayContaining(["code", "table", "task-list", "footnote"])
|
||||
);
|
||||
});
|
||||
|
||||
it("读取并规范化 Front Matter 元数据", () => {
|
||||
const result = renderMarkdown(`---
|
||||
title: 项目报告
|
||||
author: 张三
|
||||
subject: 技术方案
|
||||
keywords:
|
||||
- Markdown
|
||||
- PDF
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# 不作为标题
|
||||
`);
|
||||
|
||||
expect(result.metadata).toEqual({
|
||||
title: "项目报告",
|
||||
author: "张三",
|
||||
subject: "技术方案",
|
||||
keywords: ["Markdown", "PDF"],
|
||||
language: "zh-CN"
|
||||
});
|
||||
});
|
||||
|
||||
it("服务端渲染公式并保留 Mermaid 安全占位", () => {
|
||||
const result = renderMarkdown(`
|
||||
内联公式 $E=mc^2$。
|
||||
|
||||
\`\`\`mermaid
|
||||
flowchart LR
|
||||
A[开始] --> B[结束]
|
||||
\`\`\`
|
||||
`);
|
||||
|
||||
expect(result.bodyHtml).toContain("katex");
|
||||
expect(result.bodyHtml).toContain('class="mermaid"');
|
||||
expect(result.bodyHtml).toContain("data-mermaid-pending");
|
||||
expect(result.features).toEqual(
|
||||
expect.arrayContaining(["katex", "mermaid"])
|
||||
);
|
||||
});
|
||||
|
||||
it("阻止原始 HTML 和危险链接形成可执行内容", () => {
|
||||
const result = renderMarkdown(`
|
||||
<script>alert("xss")</script>
|
||||
|
||||
[危险链接](javascript:alert("xss"))
|
||||
`);
|
||||
|
||||
expect(result.bodyHtml).not.toContain("<script");
|
||||
expect(result.bodyHtml).not.toMatch(/href=["']javascript:/u);
|
||||
expect(result.bodyHtml).toContain("<script>");
|
||||
});
|
||||
|
||||
it("为重复中文标题生成唯一锚点", () => {
|
||||
const result = renderMarkdown(`
|
||||
# 章节
|
||||
|
||||
## 章节
|
||||
|
||||
## 章节
|
||||
`);
|
||||
|
||||
expect(result.bodyHtml).toContain('id="%E7%AB%A0%E8%8A%82"');
|
||||
expect(result.bodyHtml).toContain('id="%E7%AB%A0%E8%8A%82-2"');
|
||||
expect(result.bodyHtml).toContain('id="%E7%AB%A0%E8%8A%82-3"');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user