import { describe, it, expect } from 'vitest' import { renderMarkdown, renderMarkdownSync } from '../markdown' describe('renderMarkdown (MetonaEditor 内置解析器)', () => { it('should render a simple heading', async () => { const result = await renderMarkdown('# Hello World') expect(result).toContain('
{
const md = '```javascript\nconst x = 1;\n```'
const result = await renderMarkdown(md)
expect(result).toContain(' {
const result = await renderMarkdown('[link](https://example.com)')
expect(result).toContain(' {
const result = await renderMarkdown('- item 1\n- item 2')
expect(result).toContain(' {
const result = await renderMarkdown('1. first\n2. second')
expect(result).toContain(' {
const result = await renderMarkdown('> quote')
expect(result).toContain(' {
const md = '| A | B |\n|---|---|\n| 1 | 2 |'
const result = await renderMarkdown(md)
expect(result).toContain(' {
const result = await renderMarkdown('~~deleted~~')
expect(result).toContain(' {
const result = await renderMarkdown('- [x] done\n- [ ] todo')
expect(result).toContain('me-task-item')
expect(result).toContain('checked')
})
it('should render mermaid as .me-mermaid container', async () => {
const md = '```mermaid\ngraph TD\nA-->B\n```'
const result = await renderMarkdown(md)
expect(result).toContain('me-mermaid')
expect(result).toContain('class="mermaid"')
expect(result).toContain('graph TD')
})
it('should handle empty content', async () => {
const result = await renderMarkdown('')
expect(result).toBe('')
})
it('should handle null and undefined filePath', async () => {
expect(await renderMarkdown('# No File', null)).toContain('No File')
expect(await renderMarkdown('# No File')).toContain('No File')
})
it('should be sync-callable via renderMarkdownSync', () => {
const result = renderMarkdownSync('# Sync')
expect(result).toContain(' {
const FILE = '/home/user/docs/note.md'
it('should fix relative image paths to file://', async () => {
const result = await renderMarkdown('', FILE)
expect(result).toContain('src="file:///home/user/docs/pic.png"')
})
it('should fix nested relative paths', async () => {
const result = await renderMarkdown('', FILE)
expect(result).toContain('src="file:///home/user/docs/assets/a.png"')
})
it('should fix unix absolute paths', async () => {
const result = await renderMarkdown('', FILE)
expect(result).toContain('src="file:///abs/pic.png"')
})
it('should not touch http/https/data:/file: URLs', async () => {
const md = '  '
const result = await renderMarkdown(md, FILE)
expect(result).toContain('https://x.com/a.png')
expect(result).toContain('data:image/png;base64,xx')
expect(result).toContain('file:///c.png')
})
it('should skip out-of-directory traversal (../ and ../../)', async () => {
const md = ' '
const result = await renderMarkdown(md, FILE)
// 越界路径保持原样(不注入 file://)
expect(result).not.toContain('file:')
expect(result).toContain('../outside.png')
expect(result).toContain('../../outside.png')
})
it('should not treat sibling directory with same prefix as inside', async () => {
// /home/user/docs-other/ 与 /home/user/docs/ 前缀相似但目录不同
const result = await renderMarkdown('', FILE)
expect(result).not.toContain('file:')
expect(result).toContain('../docs-other/pic.png')
})
it('should not touch images when filePath is null', async () => {
const result = await renderMarkdown('')
expect(result).toContain('./pic.png')
expect(result).not.toContain('file:')
})
it('should fix relative image paths from root-level file', async () => {
const result = await renderMarkdown('', '/note.md')
expect(result).toContain('src="file:///pic.png"')
})
it('should escape special chars in fixed path', async () => {
const result = await renderMarkdown('', FILE)
expect(result).toContain('src="file:///home/user/docs/a&b.png"')
})
})
describe('XSS 防护(内置 safeUrl)', () => {
it('should not emit anchor for javascript: URLs', async () => {
const result = await renderMarkdown('[x](javascript:alert(1))')
// 内置解析器将危险 URL 转义原样输出(不生成 ),文本不构成可执行链接
expect(result).not.toContain(' {
const result = await renderMarkdown(')')
expect(result).not.toContain('![]()