v0.2.0: 全面代码质量优化

- ESLint flat config + Prettier + EditorConfig
- Markdown处理器LRU缓存
- Zustand选择器优化减少重渲染
- CodeMirror Compartment主题热切换
- Preview防抖(150ms) + IndexedDB去抖(500ms)
- 组件拆分: App.tsx 310→104行, Sidebar.tsx 244→90行
- 统一错误处理 errorHandler.ts
- ConfirmDialog替代原生confirm
- LoadingSpinner加载状态
- Toast多条堆叠+类型区分
- 可访问性增强(ARIA属性、键盘导航)
- Vitest测试框架(78个测试用例)
- Git hooks(husky + lint-staged)
- 项目文档(README.md, CONTRIBUTING.md)
This commit is contained in:
thzxx
2026-06-03 22:13:32 +08:00
parent 3cecb0f9eb
commit 7a4e2b0a67
72 changed files with 5103 additions and 894 deletions
+106
View File
@@ -0,0 +1,106 @@
import { describe, it, expect } from 'vitest'
import { renderMarkdown } from '../markdown'
describe('renderMarkdown', () => {
it('should render a simple heading', async () => {
const result = await renderMarkdown('# Hello World')
expect(result).toContain('<h1')
expect(result).toContain('Hello World')
})
it('should render bold text', async () => {
const result = await renderMarkdown('**bold**')
expect(result).toContain('<strong')
expect(result).toContain('bold')
})
it('should render italic text', async () => {
const result = await renderMarkdown('*italic*')
expect(result).toContain('<em')
expect(result).toContain('italic')
})
it('should render inline code', async () => {
const result = await renderMarkdown('`code`')
expect(result).toContain('<code')
expect(result).toContain('code')
})
it('should render code blocks with syntax highlighting', async () => {
const md = '```javascript\nconst x = 1;\n```'
const result = await renderMarkdown(md)
expect(result).toContain('<pre')
expect(result).toContain('<code')
expect(result).toContain('const')
})
it('should render links', async () => {
const result = await renderMarkdown('[link](https://example.com)')
expect(result).toContain('<a')
expect(result).toContain('https://example.com')
expect(result).toContain('link')
})
it('should render unordered lists', async () => {
const result = await renderMarkdown('- item 1\n- item 2')
expect(result).toContain('<ul')
expect(result).toContain('<li')
expect(result).toContain('item 1')
expect(result).toContain('item 2')
})
it('should render ordered lists', async () => {
const result = await renderMarkdown('1. first\n2. second')
expect(result).toContain('<ol')
expect(result).toContain('first')
expect(result).toContain('second')
})
it('should render blockquotes', async () => {
const result = await renderMarkdown('> quote')
expect(result).toContain('<blockquote')
expect(result).toContain('quote')
})
it('should render tables via GFM', async () => {
const md = '| A | B |\n|---|---|\n| 1 | 2 |'
const result = await renderMarkdown(md)
expect(result).toContain('<table')
expect(result).toContain('<td')
})
it('should render strikethrough via GFM', async () => {
const result = await renderMarkdown('~~deleted~~')
expect(result).toContain('<del')
expect(result).toContain('deleted')
})
it('should handle empty content', async () => {
const result = await renderMarkdown('')
expect(result).toBe('')
})
it('should return error HTML on rendering failure with invalid input', async () => {
// unified should still handle gracefully, but verify error path works
const result = await renderMarkdown('normal text')
expect(result).not.toContain('渲染错误')
})
it('should cache processors for same filePath', async () => {
// Call twice with same filePath to test caching
const result1 = await renderMarkdown('# Test', '/test/file.md')
const result2 = await renderMarkdown('# Test 2', '/test/file.md')
expect(result1).toContain('<h1')
expect(result2).toContain('Test 2')
})
it('should handle null filePath', async () => {
const result = await renderMarkdown('# No File', null)
expect(result).toContain('No File')
})
it('should handle undefined filePath', async () => {
const result = await renderMarkdown('# No File')
expect(result).toContain('No File')
})
})