Files
new-markdown/prototype/tests/roundtrip.test.ts

79 lines
3.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 阶段 0 核心验收:Markdown -> 文档模型 -> Markdown 双向转换保真。
// 断言分两层:
// 1) 内容保真:关键文本/结构在序列化结果中仍存在(允许语法规范化,如列表符号统一);
// 2) 往返稳定:第二轮 parse(serialize(x)) 再 serialize 与第一轮结果逐字一致
// —— 这是「保存后重开不漂移」的硬指标。
import { describe, it, expect, afterEach } from 'vitest'
import type { Editor } from '@milkdown/kit/core'
import { createMarkdownEditor, getMarkdown } from '../src/editor'
import { BASIC_MD, COMPLEX_MD } from '../src/fixtures'
let editors: Editor[] = []
afterEach(async () => {
for (const e of editors) { try { await e.destroy() } catch { /* ignore */ } }
editors = []
document.body.innerHTML = ''
})
async function load(markdown: string): Promise<Editor> {
const root = document.createElement('div')
document.body.appendChild(root)
const editor = createMarkdownEditor(root, markdown)
editors.push(editor)
await editor.create()
return editor
}
async function roundtrip(markdown: string): Promise<{ once: string; twice: string }> {
const first = await load(markdown)
const once = getMarkdown(first)
const second = await load(once)
const twice = getMarkdown(second)
return { once, twice }
}
describe('Markdown 双向转换', () => {
it('基础结构保真:标题/粗斜体/删除线/行内代码/引用/列表/分割线/链接', async () => {
const { once, twice } = await roundtrip(BASIC_MD)
expect(once).toMatch(/^# 一级标题示例/m)
expect(once).toMatch(/^## 二级标题/m)
expect(once).toContain('**粗体**')
expect(once).toContain('删除线')
expect(once).toContain('`行内代码`')
expect(once).toContain('这是一段引用')
expect(once).toContain('无序列表第一项')
expect(once).toContain('嵌套列表项')
expect(once).toContain('有序列表第二项')
expect(once).toContain('https://example.com')
expect(twice).toBe(once)
})
it('复杂结构保真:任务列表状态/表格/代码块语言与内容/图片/六级标题', async () => {
const { once, twice } = await roundtrip(COMPLEX_MD)
// 任务列表:未完成与已完成两种状态都必须保留
expect(once).toMatch(/-\s\[ \]\s待完成事项/)
expect(once).toMatch(/-\s\[[xX]\]\s已完成事项/)
// 表格:表头、单元格与右对齐列
expect(once).toContain('名称')
expect(once).toContain('苹果')
expect(once).toContain('香蕉')
// 代码块:语言标记与代码原文
expect(once).toContain('typescript')
expect(once).toContain('const greeting')
expect(once).toContain('你好,世界')
// 图片路径保留
expect(once).toContain('images/demo.png')
// 最深标题层级
expect(once).toMatch(/^###### 六级标题/m)
// Mermaid 源码作为代码块原文保留(渲染在阶段 3)
expect(once).toContain('graph TD')
expect(twice).toBe(once)
})
it('空文档与纯中文段落往返稳定', async () => {
const { once, twice } = await roundtrip('只有一段中文,没有任何格式标记。\n')
expect(once).toContain('只有一段中文')
expect(twice).toBe(once)
})
})