|
| 1 | +/** |
| 2 | + * noteMarkdown 单测:Markdown 往返一致性 |
| 3 | + * Unit tests for Markdown round-trip |
| 4 | + */ |
| 5 | +import { describe, it, expect } from 'vitest'; |
| 6 | +import { noteToMarkdown, markdownToNoteContent } from './noteMarkdown'; |
| 7 | + |
| 8 | +/** 构造 TipTap doc JSON 字符串 */ |
| 9 | +function tiptapDoc(...content: unknown[]): string { |
| 10 | + return JSON.stringify({ type: 'doc', content }); |
| 11 | +} |
| 12 | + |
| 13 | +describe('noteMarkdown', () => { |
| 14 | + it('标题与段落转 Markdown', () => { |
| 15 | + const md = noteToMarkdown(tiptapDoc( |
| 16 | + { type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: '标题' }] }, |
| 17 | + { type: 'paragraph', content: [{ type: 'text', text: '正文' }] }, |
| 18 | + )); |
| 19 | + expect(md).toContain('# 标题'); |
| 20 | + expect(md).toContain('正文'); |
| 21 | + }); |
| 22 | + |
| 23 | + it('粗体/斜体/行内代码', () => { |
| 24 | + const md = noteToMarkdown(tiptapDoc({ |
| 25 | + type: 'paragraph', |
| 26 | + content: [ |
| 27 | + { type: 'text', text: '粗', marks: [{ type: 'bold' }] }, |
| 28 | + { type: 'text', text: '斜', marks: [{ type: 'italic' }] }, |
| 29 | + { type: 'text', text: '码', marks: [{ type: 'code' }] }, |
| 30 | + ], |
| 31 | + })); |
| 32 | + expect(md).toContain('**粗**'); |
| 33 | + expect(md).toContain('*斜*'); |
| 34 | + expect(md).toContain('`码`'); |
| 35 | + }); |
| 36 | + |
| 37 | + it('无序列表', () => { |
| 38 | + const md = noteToMarkdown(tiptapDoc({ |
| 39 | + type: 'bulletList', |
| 40 | + content: [ |
| 41 | + { type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '项一' }] }] }, |
| 42 | + ], |
| 43 | + })); |
| 44 | + expect(md).toMatch(/[-*] 项一/); |
| 45 | + }); |
| 46 | + |
| 47 | + it('wikiLink 降级为 [[label]] 文本(方括号被 markdown 转义)', () => { |
| 48 | + const md = noteToMarkdown(tiptapDoc({ |
| 49 | + type: 'paragraph', |
| 50 | + content: [{ type: 'wikiLink', attrs: { id: 'n1', label: '目标笔记' } }], |
| 51 | + })); |
| 52 | + // 标签保留;方括号可能被 markdown 转义为 \[\[ |
| 53 | + expect(md).toContain('目标笔记'); |
| 54 | + expect(md.replace(/\\/g, '')).toContain('[[目标笔记]]'); |
| 55 | + }); |
| 56 | + |
| 57 | + it('Markdown 转 TipTap JSON(标题+粗体)', () => { |
| 58 | + const json = markdownToNoteContent('# 标题\n\n**粗体** 正文'); |
| 59 | + const doc = JSON.parse(json) as { type: string }; |
| 60 | + expect(doc.type).toBe('doc'); |
| 61 | + const str = JSON.stringify(doc); |
| 62 | + expect(str).toContain('标题'); |
| 63 | + expect(str).toContain('heading'); |
| 64 | + expect(str).toContain('bold'); |
| 65 | + }); |
| 66 | + |
| 67 | + it('导图笔记降级为大纲纯文本', () => { |
| 68 | + const mindmap = JSON.stringify({ |
| 69 | + type: 'mindmap', |
| 70 | + version: 1, |
| 71 | + root: { id: 'r', text: '中心', children: [{ id: 'a', text: '分支', children: [] }] }, |
| 72 | + }); |
| 73 | + const md = noteToMarkdown(mindmap); |
| 74 | + expect(md).toContain('中心'); |
| 75 | + expect(md).toContain('分支'); |
| 76 | + }); |
| 77 | + |
| 78 | + it('损坏内容返回空字符串', () => { |
| 79 | + expect(noteToMarkdown('not json')).toBe(''); |
| 80 | + }); |
| 81 | +}); |
0 commit comments