feat(web): render agent reports as safe-subset Markdown
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b7cbc06567
commit
41e3647f0a
3 files changed
+370
-2
No files matched your search
@@ -0,0 +1,74 @@
|
||||
import { createElement } from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Markdown, parseMarkdown, safeHref } from "./markdown.tsx";
|
||||
|
||||
const html = (text: string) => renderToStaticMarkup(createElement(Markdown, { text }));
|
||||
|
||||
describe("Markdown 安全子集", () => {
|
||||
it("块级:标题、列表、表格、代码块、引用、分隔线", () => {
|
||||
const src = [
|
||||
"## 结论",
|
||||
"",
|
||||
"**当前订阅 2 个**,来源 `inventory.query`:",
|
||||
"",
|
||||
"| # | 标题 | 年份 |",
|
||||
"|---|:---|---:|",
|
||||
"| 1 | 沙丘2 | 2024 |",
|
||||
"| 2 | 沙丘:预言 | 2024 |",
|
||||
"",
|
||||
"- 缺 1080p",
|
||||
"- 缺 4K",
|
||||
"",
|
||||
"1. 先搜",
|
||||
"2. 再提交",
|
||||
"",
|
||||
"> 仅供参考",
|
||||
"",
|
||||
"---",
|
||||
"```json",
|
||||
'{"a":1}',
|
||||
"```",
|
||||
].join("\n");
|
||||
const blocks = parseMarkdown(src);
|
||||
expect(blocks.map((b) => b.type)).toEqual(["heading", "paragraph", "table", "list", "list", "quote", "hr", "code"]);
|
||||
const out = html(src);
|
||||
expect(out).toContain("<h4");
|
||||
expect(out).toContain("<strong");
|
||||
expect(out).toContain("<table");
|
||||
expect(out).toContain("<td");
|
||||
expect(out).toContain("沙丘:预言");
|
||||
expect(out).toContain('<ol start="1"');
|
||||
expect(out).toContain("<ul");
|
||||
expect(out).toContain("<blockquote");
|
||||
expect(out).toContain("<pre");
|
||||
expect(out).toContain("text-align:right");
|
||||
});
|
||||
|
||||
it("原文里的 HTML 按文字显示;危险链接不生成 <a>", () => {
|
||||
const out = html(
|
||||
"<script>alert(1)</script> <img src=x onerror=alert(1)> [点我](javascript:alert(1)) [文档](https://example.com/a)",
|
||||
);
|
||||
expect(out).not.toContain("<script");
|
||||
expect(out).not.toContain("<img");
|
||||
expect(out).toContain("<script>");
|
||||
expect(out).not.toContain('href="javascript');
|
||||
expect(out).toContain('href="https://example.com/a"');
|
||||
expect(out).toContain('rel="noopener noreferrer nofollow"');
|
||||
expect(safeHref("/console/tasks")).toBe("/console/tasks");
|
||||
expect(safeHref("//evil.example")).toBeNull();
|
||||
expect(safeHref("data:text/html,x")).toBeNull();
|
||||
});
|
||||
|
||||
it("行内:代码里的星号不当强调;单换行保留为换行", () => {
|
||||
const out = html("用 `a*b*c` 查询\n第二行 *强调*");
|
||||
expect(out).toContain("<code");
|
||||
expect(out).toContain("a*b*c");
|
||||
expect(out).toContain("<br/>");
|
||||
expect(out).toContain("<em>强调</em>");
|
||||
});
|
||||
|
||||
it("空文本不渲染", () => {
|
||||
expect(html(" \n\n")).toBe("");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user