// @vitest-environment jsdom // markdown.test.js —— Markdown 渲染 + DOMPurify 消毒 + 自定义表格/代码类(FR-CHAT-004) import { describe, it, expect } from 'vitest'; import { renderMarkdown, NON_MONO_HEADER } from '../../../../frontend/src/components/chat/markdown.js'; describe('renderMarkdown(FR-CHAT-004)', () => { it('LLM 输出 markdown → 消毒 HTML', () => { const html = renderMarkdown('**加粗** 与 *斜体* 与 `行内代码`'); expect(html).toContain('加粗'); expect(html).toContain('斜体'); expect(html).toContain('行内代码'); }); it('脚本注入被消毒(活 DOM 中无可执行元素/属性/协议)', () => { const html = renderMarkdown('你好 [x](javascript:alert(3))'); const doc = new DOMParser().parseFromString(html, 'text/html'); expect(doc.querySelectorAll('script').length).toBe(0); expect(doc.querySelectorAll('img').length).toBe(0); expect(doc.querySelectorAll('[onerror]').length).toBe(0); const hrefs = [...doc.querySelectorAll('a')].map((a) => a.getAttribute('href') || ''); expect(hrefs.filter((h) => h.toLowerCase().startsWith('javascript:'))).toEqual([]); }); it('表格挂 md-table 类(thead th / tbody td)', () => { const html = renderMarkdown('| A | B |\n|---|---|\n| 1 | 2 |'); expect(html).toContain(''); expect(html).toContain(''); expect(html).toContain(''); expect(html).toContain(''); }); it('代码块挂 code-block 类并转义内容', () => { const html = renderMarkdown('```\nconst x = "
A