test: improve unit test
This commit is contained in:
parent
5930c60462
commit
fc9984b27c
245
plugins/plugin-md-power/__test__/abbrPlugin.spec.ts
Normal file
245
plugins/plugin-md-power/__test__/abbrPlugin.spec.ts
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
import MarkdownIt from 'markdown-it'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { abbrPlugin } from '../src/node/inline/abbr.js'
|
||||||
|
|
||||||
|
function createMarkdown(globalAbbreviations?: Record<string, string>) {
|
||||||
|
return MarkdownIt().use(abbrPlugin, globalAbbreviations)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('abbrPlugin', () => {
|
||||||
|
it('should parse abbreviation definition', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
This is HTML content.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
expect(result).toContain('HTML')
|
||||||
|
expect(result).toContain('HyperText Markup Language')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse multiple abbreviation definitions', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
*[CSS]: Cascading Style Sheets
|
||||||
|
|
||||||
|
HTML and CSS are web technologies.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('HTML')
|
||||||
|
expect(result).toContain('CSS')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with global abbreviations', () => {
|
||||||
|
const md = createMarkdown({ API: 'Application Programming Interface' })
|
||||||
|
const code = `This is an API documentation.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
expect(result).toContain('API')
|
||||||
|
expect(result).toContain('Application Programming Interface')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should merge local and global abbreviations', () => {
|
||||||
|
const md = createMarkdown({ API: 'Application Programming Interface' })
|
||||||
|
const code = `*[SDK]: Software Development Kit
|
||||||
|
|
||||||
|
API and SDK are common terms.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('API')
|
||||||
|
expect(result).toContain('SDK')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation in middle of text', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[TEST]: Test Abbreviation
|
||||||
|
|
||||||
|
This TEST is important.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('TEST')
|
||||||
|
expect(result).toContain('Test Abbreviation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation at start of text', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
HTML is a markup language.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation at end of text', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
Learn HTML`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle multiple occurrences of same abbreviation', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
HTML, HTML, and more HTML.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('HTML')
|
||||||
|
expect(result).toContain('HyperText Markup Language')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not match partial words', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
This is HTML but not XHTML.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('HTML')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty abbreviation definition gracefully', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[]: Empty definition
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle case-sensitive abbreviations', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
html is different from HTML.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('HTML')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation with markdown in title', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[MD]: **Markdown** text
|
||||||
|
|
||||||
|
MD is great.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('MD')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty global abbreviations', () => {
|
||||||
|
const md = createMarkdown({})
|
||||||
|
const code = `*[TEST]: Test
|
||||||
|
|
||||||
|
TEST is defined.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('TEST')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle escaped characters in label', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML\\[1\\]]: HTML Version 1
|
||||||
|
|
||||||
|
HTML[1] is old.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('HTML[1]')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation with special characters in title', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[XML]: eXtensible Markup Language
|
||||||
|
|
||||||
|
This is XML.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('XML')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle definition with empty title', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[TEST]:
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Abbreviation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle definition with only label', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[TEST]:
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Abbreviation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle unclosed bracket in definition', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[TEST: Test
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle nested bracket in label', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[TEST[INNER]]: Test Abbreviation
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle global abbreviations with colon prefix', () => {
|
||||||
|
const md = createMarkdown({ ':API': 'Application Programming Interface' })
|
||||||
|
const code = `This is an API call.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('API')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation adjacent to punctuation', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
Use HTML, CSS, and JS.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
expect(result).toContain('HTML')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation at sentence end', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
This is HTML.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle abbreviation in parentheses', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `*[HTML]: HyperText Markup Language
|
||||||
|
|
||||||
|
See (HTML) for details.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Abbreviation')
|
||||||
|
})
|
||||||
|
})
|
||||||
269
plugins/plugin-md-power/__test__/annotationPlugin.spec.ts
Normal file
269
plugins/plugin-md-power/__test__/annotationPlugin.spec.ts
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
import MarkdownIt from 'markdown-it'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { annotationPlugin } from '../src/node/inline/annotation.js'
|
||||||
|
|
||||||
|
function createMarkdown(globalAnnotations?: Record<string, string | string[]>) {
|
||||||
|
return MarkdownIt().use(annotationPlugin, globalAnnotations)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('annotationPlugin', () => {
|
||||||
|
it('should parse annotation definition and reference', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: This is a note annotation
|
||||||
|
|
||||||
|
This is a paragraph with a [+note] annotation.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
expect(result).toContain('note')
|
||||||
|
expect(result).toContain('This is a note annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse multiple annotation definitions', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: First note
|
||||||
|
[+tip]: Second tip
|
||||||
|
|
||||||
|
This has [+note] and [+tip].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('note')
|
||||||
|
expect(result).toContain('tip')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should merge local and global annotations', () => {
|
||||||
|
const md = createMarkdown({ global: 'Global annotation' })
|
||||||
|
const code = `[+local]: Local annotation
|
||||||
|
|
||||||
|
This uses [+global] and [+local].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('global')
|
||||||
|
expect(result).toContain('local')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle multiple references to same annotation', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Same note
|
||||||
|
|
||||||
|
First [+note], second [+note], third [+note].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('note')
|
||||||
|
expect(result).toContain('Same note')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle multi-line annotation', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: This is a
|
||||||
|
multi-line annotation
|
||||||
|
with multiple lines
|
||||||
|
|
||||||
|
This has [+note].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation with markdown content', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: This is **bold** and *italic*
|
||||||
|
|
||||||
|
This has [+note].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation with code', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+code]: Use \`code\` syntax
|
||||||
|
|
||||||
|
This has [+code].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation with links', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+link]: See [documentation](https://example.com)
|
||||||
|
|
||||||
|
Check [+link].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle multiple annotations for same label', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+multi]: First annotation
|
||||||
|
[+multi]: Second annotation
|
||||||
|
|
||||||
|
This has [+multi].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('total="2"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not render undefined annotation reference', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `This has [+undefined] annotation.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Annotation')
|
||||||
|
expect(result).toContain('[+undefined]')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty annotation label gracefully', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+]: Empty label
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation with special characters in label', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note-1]: Note with hyphen and number
|
||||||
|
|
||||||
|
Use [+note-1].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation with unicode content', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+中文]: 这是一个中文注释
|
||||||
|
|
||||||
|
使用 [+中文]。`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation in list', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+item]: List item annotation
|
||||||
|
|
||||||
|
- Item 1 [+item]
|
||||||
|
- Item 2 [+item]`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation in blockquote', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+quote]: Quote annotation
|
||||||
|
|
||||||
|
> This is a quote [+quote]`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty global annotations', () => {
|
||||||
|
const md = createMarkdown({})
|
||||||
|
const code = `[+test]: Test annotation
|
||||||
|
|
||||||
|
Use [+test].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle space in annotation label', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+ note]: Invalid label
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle space in annotation reference', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Test annotation
|
||||||
|
|
||||||
|
Use [+ note].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('[+ note]')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle newline in annotation reference', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Test annotation
|
||||||
|
|
||||||
|
Use [+
|
||||||
|
note].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle unclosed annotation reference', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Test annotation
|
||||||
|
|
||||||
|
Use [+note.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle global annotations with colon prefix', () => {
|
||||||
|
const md = createMarkdown({ ':test': 'Test annotation' })
|
||||||
|
const code = `[+test]: Local test
|
||||||
|
|
||||||
|
Use [+test].`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation reference at end of text', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Test annotation
|
||||||
|
|
||||||
|
End with [+note]`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation reference at start of text', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Test annotation
|
||||||
|
|
||||||
|
[+note] at start.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle definition without colon', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note] Test annotation
|
||||||
|
|
||||||
|
Some text.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).not.toContain('Annotation')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle annotation reference in code block', () => {
|
||||||
|
const md = createMarkdown()
|
||||||
|
const code = `[+note]: Test annotation
|
||||||
|
|
||||||
|
\`[+note]\` is code.`
|
||||||
|
|
||||||
|
const result = md.render(code)
|
||||||
|
expect(result).toContain('[+note]')
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -34,4 +34,57 @@ describe('cleanMarkdownEnv', () => {
|
|||||||
annotations: 'annotations',
|
annotations: 'annotations',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle empty env', () => {
|
||||||
|
const result = cleanMarkdownEnv({})
|
||||||
|
expect(result).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle env with only valid keys', () => {
|
||||||
|
const validEnv = {
|
||||||
|
base: '/base/',
|
||||||
|
filePath: '/path/to/file.md',
|
||||||
|
filePathRelative: 'path/to/file.md',
|
||||||
|
}
|
||||||
|
const result = cleanMarkdownEnv(validEnv)
|
||||||
|
expect(result).toEqual(validEnv)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle env with undefined values', () => {
|
||||||
|
const envWithUndefined = {
|
||||||
|
base: undefined,
|
||||||
|
filePath: 'file.md',
|
||||||
|
}
|
||||||
|
const result = cleanMarkdownEnv(envWithUndefined)
|
||||||
|
expect(result).toEqual({
|
||||||
|
base: undefined,
|
||||||
|
filePath: 'file.md',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should exclude all valid keys', () => {
|
||||||
|
const result = cleanMarkdownEnv(env, ['base', 'filePath', 'filePathRelative', 'references', 'abbreviations', 'annotations'])
|
||||||
|
expect(result).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle complex references', () => {
|
||||||
|
const envWithRefs: CleanMarkdownEnv = {
|
||||||
|
base: '/',
|
||||||
|
references: {
|
||||||
|
link1: { href: 'https://example.com', title: 'Example' },
|
||||||
|
},
|
||||||
|
abbreviations: { HTML: 'HyperText Markup Language' },
|
||||||
|
annotations: { note: { sources: ['Note 1'], rendered: [] } },
|
||||||
|
}
|
||||||
|
const result = cleanMarkdownEnv(envWithRefs)
|
||||||
|
expect(result.references).toEqual({ link1: { href: 'https://example.com', title: 'Example' } })
|
||||||
|
expect(result.abbreviations).toEqual({ HTML: 'HyperText Markup Language' })
|
||||||
|
expect(result.annotations).toEqual({ note: { sources: ['Note 1'], rendered: [] } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not mutate original env', () => {
|
||||||
|
const originalEnv = { ...env }
|
||||||
|
cleanMarkdownEnv(env)
|
||||||
|
expect(env).toEqual(originalEnv)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
163
plugins/plugin-md-power/__test__/demoNormal.spec.ts
Normal file
163
plugins/plugin-md-power/__test__/demoNormal.spec.ts
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { parseEmbedCode } from '../src/node/demo/normal.js'
|
||||||
|
|
||||||
|
describe('parseEmbedCode', () => {
|
||||||
|
it('should parse basic HTML code', () => {
|
||||||
|
const code = '<div>Hello World</div>'
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.html).toBe('<div>Hello World</div>')
|
||||||
|
expect(result.script).toBe('')
|
||||||
|
expect(result.css).toBe('')
|
||||||
|
expect(result.jsType).toBe('js')
|
||||||
|
expect(result.cssType).toBe('css')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with script', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<script>
|
||||||
|
const message = 'Hello World'
|
||||||
|
</script>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.html?.trim()).toBe('<div>Hello</div>')
|
||||||
|
expect(result.script?.trim()).toBe('const message = \'Hello World\'')
|
||||||
|
expect(result.jsType).toBe('js')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with TypeScript script', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<script lang="ts">
|
||||||
|
const message: string = 'Hello World'
|
||||||
|
</script>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.jsType).toBe('ts')
|
||||||
|
expect(result.script?.trim()).toBe('const message: string = \'Hello World\'')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with style', () => {
|
||||||
|
const code = `<div class="container">Hello</div>
|
||||||
|
<style>
|
||||||
|
.container { color: red; }
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.html?.trim()).toBe('<div class="container">Hello</div>')
|
||||||
|
expect(result.css?.trim()).toBe('.container { color: red; }')
|
||||||
|
expect(result.cssType).toBe('css')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with SCSS style', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<style lang="scss">
|
||||||
|
.container {
|
||||||
|
.inner { color: red; }
|
||||||
|
}
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.cssType).toBe('scss')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with Less style', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<style lang="less">
|
||||||
|
@color: red;
|
||||||
|
.container { color: @color; }
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.cssType).toBe('less')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with Stylus style', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<style lang="stylus">
|
||||||
|
color = red
|
||||||
|
.container
|
||||||
|
color color
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.cssType).toBe('stylus')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with Stylus style (styl extension)', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<style lang="styl">
|
||||||
|
color = red
|
||||||
|
.container
|
||||||
|
color color
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.cssType).toBe('stylus')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse HTML with config', () => {
|
||||||
|
const code = `<div>Hello</div>
|
||||||
|
<script type="config">
|
||||||
|
{
|
||||||
|
"jsLib": ["https://cdn.example.com/lib.js"],
|
||||||
|
"cssLib": ["https://cdn.example.com/style.css"]
|
||||||
|
}
|
||||||
|
</script>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.imports?.trim()).toContain('jsLib')
|
||||||
|
expect(result.imports?.trim()).toContain('cssLib')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse complete code with all parts', () => {
|
||||||
|
const code = `<div id="app">{{ message }}</div>
|
||||||
|
<script type="config">
|
||||||
|
{ "jsLib": ["vue"] }
|
||||||
|
</script>
|
||||||
|
<script lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
const message = ref<string>('Hello')
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
#app { color: blue; }
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.html?.trim()).toBe('<div id="app">{{ message }}</div>')
|
||||||
|
expect(result.imports).toContain('vue')
|
||||||
|
expect(result.jsType).toBe('ts')
|
||||||
|
expect(result.cssType).toBe('scss')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty code', () => {
|
||||||
|
const result = parseEmbedCode('')
|
||||||
|
|
||||||
|
expect(result.html).toBe('')
|
||||||
|
expect(result.script).toBe('')
|
||||||
|
expect(result.css).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle code with only whitespace', () => {
|
||||||
|
const result = parseEmbedCode(' \n \n ')
|
||||||
|
|
||||||
|
expect(result.html?.trim()).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle unknown script lang as js', () => {
|
||||||
|
const code = `<script lang="unknown">
|
||||||
|
console.log('test')
|
||||||
|
</script>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.jsType).toBe('js')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle unknown style lang as css', () => {
|
||||||
|
const code = `<style lang="unknown">
|
||||||
|
.test { color: red }
|
||||||
|
</style>`
|
||||||
|
const result = parseEmbedCode(code)
|
||||||
|
|
||||||
|
expect(result.cssType).toBe('css')
|
||||||
|
})
|
||||||
|
})
|
||||||
103
plugins/plugin-md-power/__test__/encryptContent.spec.ts
Normal file
103
plugins/plugin-md-power/__test__/encryptContent.spec.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { encryptContent } from '../src/node/utils/encryptContent.js'
|
||||||
|
|
||||||
|
describe('encryptContent', () => {
|
||||||
|
it('should encrypt content with valid options', async () => {
|
||||||
|
const content = 'Hello, World!'
|
||||||
|
const password = 'test-password'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const encrypted = await encryptContent(content, { password, iv, salt })
|
||||||
|
|
||||||
|
expect(typeof encrypted).toBe('string')
|
||||||
|
expect(encrypted.length).toBeGreaterThan(0)
|
||||||
|
expect(encrypted).not.toBe(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should produce different encrypted content for different passwords', async () => {
|
||||||
|
const content = 'Same content'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const encrypted1 = await encryptContent(content, { password: 'password1', iv, salt })
|
||||||
|
const encrypted2 = await encryptContent(content, { password: 'password2', iv, salt })
|
||||||
|
|
||||||
|
expect(encrypted1).not.toBe(encrypted2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should produce different encrypted content for different IVs', async () => {
|
||||||
|
const content = 'Same content'
|
||||||
|
const password = 'same-password'
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const iv1 = new Uint8Array(16).fill(1)
|
||||||
|
const iv2 = new Uint8Array(16).fill(2)
|
||||||
|
|
||||||
|
const encrypted1 = await encryptContent(content, { password, iv: iv1, salt })
|
||||||
|
const encrypted2 = await encryptContent(content, { password, iv: iv2, salt })
|
||||||
|
|
||||||
|
expect(encrypted1).not.toBe(encrypted2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should produce different encrypted content for different salts', async () => {
|
||||||
|
const content = 'Same content'
|
||||||
|
const password = 'same-password'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
|
||||||
|
const salt1 = new Uint8Array(16).fill(1)
|
||||||
|
const salt2 = new Uint8Array(16).fill(2)
|
||||||
|
|
||||||
|
const encrypted1 = await encryptContent(content, { password, iv, salt: salt1 })
|
||||||
|
const encrypted2 = await encryptContent(content, { password, iv, salt: salt2 })
|
||||||
|
|
||||||
|
expect(encrypted1).not.toBe(encrypted2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should encrypt empty string', async () => {
|
||||||
|
const content = ''
|
||||||
|
const password = 'test-password'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const encrypted = await encryptContent(content, { password, iv, salt })
|
||||||
|
|
||||||
|
expect(typeof encrypted).toBe('string')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should encrypt unicode content', async () => {
|
||||||
|
const content = '你好,世界!🌍🎉'
|
||||||
|
const password = 'test-password'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const encrypted = await encryptContent(content, { password, iv, salt })
|
||||||
|
|
||||||
|
expect(typeof encrypted).toBe('string')
|
||||||
|
expect(encrypted.length).toBeGreaterThan(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should encrypt long content', async () => {
|
||||||
|
const content = 'A'.repeat(10000)
|
||||||
|
const password = 'test-password'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const encrypted = await encryptContent(content, { password, iv, salt })
|
||||||
|
|
||||||
|
expect(typeof encrypted).toBe('string')
|
||||||
|
expect(encrypted.length).toBeGreaterThan(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should encrypt content with special characters', async () => {
|
||||||
|
const content = '<script>alert("xss")</script>\n\t\r'
|
||||||
|
const password = 'test-password'
|
||||||
|
const iv = new Uint8Array(16)
|
||||||
|
const salt = new Uint8Array(16)
|
||||||
|
|
||||||
|
const encrypted = await encryptContent(content, { password, iv, salt })
|
||||||
|
|
||||||
|
expect(typeof encrypted).toBe('string')
|
||||||
|
expect(encrypted.length).toBeGreaterThan(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -45,4 +45,39 @@ describe('getFileIcon(filename, type)', () => {
|
|||||||
expect(getFileIcon('abc.', 'folder')).toBe('vscode-icons:default-folder')
|
expect(getFileIcon('abc.', 'folder')).toBe('vscode-icons:default-folder')
|
||||||
expect(getFileIcon('')).toBe('vscode-icons:default-file')
|
expect(getFileIcon('')).toBe('vscode-icons:default-file')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle common file types', () => {
|
||||||
|
expect(getFileIcon('README.md')).toBeDefined()
|
||||||
|
expect(getFileIcon('package.json')).toBeDefined()
|
||||||
|
expect(getFileIcon('style.css')).toBeDefined()
|
||||||
|
expect(getFileIcon('index.html')).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle config files', () => {
|
||||||
|
expect(getFileIcon('.gitignore')).toBeDefined()
|
||||||
|
expect(getFileIcon('.eslintrc')).toBeDefined()
|
||||||
|
expect(getFileIcon('tsconfig.json')).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle image files', () => {
|
||||||
|
expect(getFileIcon('image.png')).toBeDefined()
|
||||||
|
expect(getFileIcon('image.jpg')).toBeDefined()
|
||||||
|
expect(getFileIcon('image.svg')).toBeDefined()
|
||||||
|
expect(getFileIcon('image.gif')).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle files with path', () => {
|
||||||
|
expect(getFileIcon('src/components/Button.vue')).toBe('vscode-icons:file-type-vue')
|
||||||
|
expect(getFileIcon('/absolute/path/to/file.ts')).toBe('vscode-icons:file-type-typescript')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle files without extension', () => {
|
||||||
|
expect(getFileIcon('Makefile')).toBeDefined()
|
||||||
|
expect(getFileIcon('Dockerfile')).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle hidden files', () => {
|
||||||
|
expect(getFileIcon('.env')).toBeDefined()
|
||||||
|
expect(getFileIcon('.npmrc')).toBeDefined()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
68
plugins/plugin-md-power/__test__/findLocales.spec.ts
Normal file
68
plugins/plugin-md-power/__test__/findLocales.spec.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import type { MDPowerLocaleData } from '../src/shared/index.js'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { findLocales } from '../src/node/utils/findLocales.js'
|
||||||
|
|
||||||
|
describe('findLocales', () => {
|
||||||
|
interface TestLocaleData extends MDPowerLocaleData {
|
||||||
|
hint?: string
|
||||||
|
label?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should find locales for a key', () => {
|
||||||
|
const locales = {
|
||||||
|
'/': { hint: 'Hint', label: 'Label' },
|
||||||
|
'/zh/': { hint: '提示', label: '标签' },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(findLocales<TestLocaleData, 'hint'>(locales, 'hint')).toEqual({
|
||||||
|
'/': 'Hint',
|
||||||
|
'/zh/': '提示',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return empty object for missing key', () => {
|
||||||
|
const locales = {
|
||||||
|
'/': { hint: 'Hint' },
|
||||||
|
'/zh/': { hint: '提示' },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(findLocales<TestLocaleData, 'label'>(locales, 'label')).toEqual({
|
||||||
|
'/': {},
|
||||||
|
'/zh/': {},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty locales', () => {
|
||||||
|
expect(findLocales({}, 'hint' as any)).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle partial locale data', () => {
|
||||||
|
const locales = {
|
||||||
|
'/': { hint: 'Hint', label: 'Label' },
|
||||||
|
'/zh/': { hint: '提示' },
|
||||||
|
'/en/': {},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(findLocales<TestLocaleData, 'label'>(locales, 'label')).toEqual({
|
||||||
|
'/': 'Label',
|
||||||
|
'/zh/': {},
|
||||||
|
'/en/': {},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle nested locale paths', () => {
|
||||||
|
const locales = {
|
||||||
|
'/': { hint: 'Root' },
|
||||||
|
'/zh/': { hint: 'Chinese' },
|
||||||
|
'/zh-tw/': { hint: 'Traditional Chinese' },
|
||||||
|
'/en-US/': { hint: 'US English' },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(findLocales<TestLocaleData, 'hint'>(locales, 'hint')).toEqual({
|
||||||
|
'/': 'Root',
|
||||||
|
'/zh/': 'Chinese',
|
||||||
|
'/zh-tw/': 'Traditional Chinese',
|
||||||
|
'/en-US/': 'US English',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
29
plugins/plugin-md-power/__test__/nanoid.spec.ts
Normal file
29
plugins/plugin-md-power/__test__/nanoid.spec.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { nanoid } from '../src/node/utils/nanoid.js'
|
||||||
|
|
||||||
|
describe('nanoid', () => {
|
||||||
|
it('should generate id with default length', () => {
|
||||||
|
const id = nanoid()
|
||||||
|
expect(id).toHaveLength(5)
|
||||||
|
expect(id).toMatch(/^[a-z]+$/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should generate id with custom length', () => {
|
||||||
|
expect(nanoid(10)).toHaveLength(10)
|
||||||
|
expect(nanoid(1)).toHaveLength(1)
|
||||||
|
expect(nanoid(20)).toHaveLength(20)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should generate unique ids', () => {
|
||||||
|
const ids = new Set<string>()
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
ids.add(nanoid())
|
||||||
|
}
|
||||||
|
expect(ids.size).toBe(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should only contain lowercase letters', () => {
|
||||||
|
const id = nanoid(100)
|
||||||
|
expect(id).toMatch(/^[a-z]+$/)
|
||||||
|
})
|
||||||
|
})
|
||||||
93
plugins/plugin-md-power/__test__/package.spec.ts
Normal file
93
plugins/plugin-md-power/__test__/package.spec.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { interopDefault } from '../src/node/utils/package.js'
|
||||||
|
|
||||||
|
describe('interopDefault', () => {
|
||||||
|
it('should return default export if exists', async () => {
|
||||||
|
const module = { default: { name: 'test' }, other: 'value' }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ name: 'test' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return module itself if no default export', async () => {
|
||||||
|
const module = { name: 'test', value: 123 }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ name: 'test', value: 123 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle non-object default', async () => {
|
||||||
|
const module = { default: 'string value' }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toBe('string value')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle function default', async () => {
|
||||||
|
const fn = () => 'function result'
|
||||||
|
const module = { default: fn }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toBe(fn)
|
||||||
|
expect(result()).toBe('function result')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle primitive values', async () => {
|
||||||
|
const result = await interopDefault(Promise.resolve(42))
|
||||||
|
expect(result).toBe(42)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle string values', async () => {
|
||||||
|
const result = await interopDefault(Promise.resolve('hello'))
|
||||||
|
expect(result).toBe('hello')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle array values', async () => {
|
||||||
|
const arr = [1, 2, 3]
|
||||||
|
const result = await interopDefault(Promise.resolve(arr))
|
||||||
|
expect(result).toEqual([1, 2, 3])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle nested object default', async () => {
|
||||||
|
const module = {
|
||||||
|
default: {
|
||||||
|
nested: {
|
||||||
|
deep: 'value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ nested: { deep: 'value' } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle class as default', async () => {
|
||||||
|
class TestClass {
|
||||||
|
value = 'test'
|
||||||
|
}
|
||||||
|
const module = { default: TestClass }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toBe(TestClass)
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
|
expect(new result().value).toBe('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return module for falsy default values', async () => {
|
||||||
|
const module = { default: 0 }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ default: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return module for empty string default', async () => {
|
||||||
|
const module = { default: '' }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ default: '' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return module for false as default', async () => {
|
||||||
|
const module = { default: false }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ default: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return module for null as default', async () => {
|
||||||
|
const module = { default: null }
|
||||||
|
const result = await interopDefault(Promise.resolve(module))
|
||||||
|
expect(result).toEqual({ default: null })
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -17,4 +17,45 @@ describe('parseRect(str)', () => {
|
|||||||
expect(parseRect('1%', 'px')).toBe('1%')
|
expect(parseRect('1%', 'px')).toBe('1%')
|
||||||
expect(parseRect('1em', 'px')).toBe('1em')
|
expect(parseRect('1em', 'px')).toBe('1em')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle zero value', () => {
|
||||||
|
expect(parseRect('0')).toBe('0px')
|
||||||
|
expect(parseRect('0px')).toBe('0px')
|
||||||
|
expect(parseRect('0%')).toBe('0%')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle decimal values', () => {
|
||||||
|
expect(parseRect('1.5')).toBe('1.5px')
|
||||||
|
expect(parseRect('0.5em')).toBe('0.5em')
|
||||||
|
expect(parseRect('50.5%')).toBe('50.5%')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle negative values', () => {
|
||||||
|
expect(parseRect('-1')).toBe('-1px')
|
||||||
|
expect(parseRect('-10px')).toBe('-10px')
|
||||||
|
expect(parseRect('-5%')).toBe('-5%')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle large values', () => {
|
||||||
|
expect(parseRect('10000')).toBe('10000px')
|
||||||
|
expect(parseRect('9999px')).toBe('9999px')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle various units', () => {
|
||||||
|
expect(parseRect('1rem')).toBe('1rem')
|
||||||
|
expect(parseRect('1vh')).toBe('1vh')
|
||||||
|
expect(parseRect('1vw')).toBe('1vw')
|
||||||
|
expect(parseRect('1pt')).toBe('1pt')
|
||||||
|
expect(parseRect('1mm')).toBe('1mm')
|
||||||
|
expect(parseRect('1in')).toBe('1in')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle non-numeric values', () => {
|
||||||
|
expect(parseRect('auto')).toBe('auto')
|
||||||
|
expect(parseRect('abc')).toBe('abc')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle calc expressions', () => {
|
||||||
|
expect(parseRect('calc(100% - 20px)')).toBe('calc(100% - 20px)')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -42,6 +42,62 @@ describe('resolveAttrs(info)', () => {
|
|||||||
attrs: { fooBar: '1', fizzBuzz: true },
|
attrs: { fooBar: '1', fizzBuzz: true },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle single quotes', () => {
|
||||||
|
expect(resolveAttrs('a=\'1\'')).toEqual({
|
||||||
|
rawAttrs: 'a=\'1\'',
|
||||||
|
attrs: { a: '1' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle mixed quotes', () => {
|
||||||
|
expect(resolveAttrs('a="1" b=\'2\'')).toEqual({
|
||||||
|
rawAttrs: 'a="1" b=\'2\'',
|
||||||
|
attrs: { a: '1', b: '2' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle values with spaces in quotes', () => {
|
||||||
|
expect(resolveAttrs('title="hello world"')).toEqual({
|
||||||
|
rawAttrs: 'title="hello world"',
|
||||||
|
attrs: { title: 'hello world' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle special characters in values', () => {
|
||||||
|
expect(resolveAttrs('data-value="<script>"')).toEqual({
|
||||||
|
rawAttrs: 'data-value="<script>"',
|
||||||
|
attrs: { dataValue: '<script>' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle unicode values', () => {
|
||||||
|
expect(resolveAttrs('title="你好世界"')).toEqual({
|
||||||
|
rawAttrs: 'title="你好世界"',
|
||||||
|
attrs: { title: '你好世界' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle numeric string values', () => {
|
||||||
|
expect(resolveAttrs('width="100" height="200"')).toEqual({
|
||||||
|
rawAttrs: 'width="100" height="200"',
|
||||||
|
attrs: { width: '100', height: '200' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty value with quotes', () => {
|
||||||
|
expect(resolveAttrs('a=""')).toEqual({
|
||||||
|
rawAttrs: 'a=""',
|
||||||
|
attrs: { a: '' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle multiple spaces between attrs', () => {
|
||||||
|
expect(resolveAttrs('a="1" b="2"')).toEqual({
|
||||||
|
rawAttrs: 'a="1" b="2"',
|
||||||
|
attrs: { a: '1', b: '2' },
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('resolveAttr(info, key)', () => {
|
describe('resolveAttr(info, key)', () => {
|
||||||
@ -52,4 +108,13 @@ describe('resolveAttr(info, key)', () => {
|
|||||||
expect(resolveAttr('a=\'1\'', 'a')).toEqual('1')
|
expect(resolveAttr('a=\'1\'', 'a')).toEqual('1')
|
||||||
expect(resolveAttr('a', 'a')).toEqual(undefined)
|
expect(resolveAttr('a', 'a')).toEqual(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should return undefined for missing key', () => {
|
||||||
|
expect(resolveAttr('', 'a')).toEqual(undefined)
|
||||||
|
expect(resolveAttr('b="1"', 'a')).toEqual(undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return first match for duplicate keys', () => {
|
||||||
|
expect(resolveAttr('a="1" a="2"', 'a')).toEqual('1')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
121
plugins/plugin-md-power/__test__/resolveIcon.spec.ts
Normal file
121
plugins/plugin-md-power/__test__/resolveIcon.spec.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import type { IconOptions } from '../src/shared/index.js'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { resolveIcon } from '../src/node/icon/resolveIcon.js'
|
||||||
|
|
||||||
|
describe('resolveIcon', () => {
|
||||||
|
const defaultOptions: IconOptions = {
|
||||||
|
provider: 'iconify',
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should resolve basic icon name', () => {
|
||||||
|
const result = resolveIcon('mdi:home', defaultOptions)
|
||||||
|
expect(result).toEqual({
|
||||||
|
provider: 'iconify',
|
||||||
|
name: 'mdi:home',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with size', () => {
|
||||||
|
const result = resolveIcon('mdi:home =24', defaultOptions)
|
||||||
|
expect(result).toEqual({
|
||||||
|
provider: 'iconify',
|
||||||
|
name: 'mdi:home',
|
||||||
|
size: '24',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with color', () => {
|
||||||
|
const result = resolveIcon('mdi:home /red', defaultOptions)
|
||||||
|
expect(result).toEqual({
|
||||||
|
provider: 'iconify',
|
||||||
|
name: 'mdi:home',
|
||||||
|
color: 'red',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with size and color', () => {
|
||||||
|
const result = resolveIcon('mdi:home =24 /blue', defaultOptions)
|
||||||
|
expect(result).toEqual({
|
||||||
|
provider: 'iconify',
|
||||||
|
name: 'mdi:home',
|
||||||
|
size: '24',
|
||||||
|
color: 'blue',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with different providers', () => {
|
||||||
|
expect(resolveIcon('iconfont icon-home', defaultOptions)).toEqual({
|
||||||
|
provider: 'iconfont',
|
||||||
|
name: 'icon-home',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(resolveIcon('fontawesome fa-home', defaultOptions)).toEqual({
|
||||||
|
provider: 'fontawesome',
|
||||||
|
name: 'fa-home',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(resolveIcon('iconify mdi:home', defaultOptions)).toEqual({
|
||||||
|
provider: 'iconify',
|
||||||
|
name: 'mdi:home',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use options provider as default', () => {
|
||||||
|
const result = resolveIcon('mdi:home', { provider: 'iconfont' })
|
||||||
|
expect(result.provider).toBe('iconfont')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use options size as default', () => {
|
||||||
|
const result = resolveIcon('mdi:home', { provider: 'iconify', size: '32' })
|
||||||
|
expect(result.size).toBe('32')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should override options size with inline size', () => {
|
||||||
|
const result = resolveIcon('mdi:home =24', { provider: 'iconify', size: '32' })
|
||||||
|
expect(result.size).toBe('24')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use options color as default', () => {
|
||||||
|
const result = resolveIcon('mdi:home', { provider: 'iconify', color: 'red' })
|
||||||
|
expect(result.color).toBe('red')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should override options color with inline color', () => {
|
||||||
|
const result = resolveIcon('mdi:home /blue', { provider: 'iconify', color: 'red' })
|
||||||
|
expect(result.color).toBe('blue')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with extra attributes', () => {
|
||||||
|
const result = resolveIcon('mdi:home class="icon" id="home-icon"', defaultOptions)
|
||||||
|
expect(result.name).toBe('mdi:home')
|
||||||
|
expect(result).toHaveProperty('class', 'icon')
|
||||||
|
expect(result).toHaveProperty('id', 'home-icon')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with boolean extra attributes', () => {
|
||||||
|
const result = resolveIcon('mdi:home spin', defaultOptions)
|
||||||
|
expect(result.name).toBe('mdi:home')
|
||||||
|
expect(result.extra).toBe('spin')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with hex color', () => {
|
||||||
|
const result = resolveIcon('mdi:home /#ff0000', defaultOptions)
|
||||||
|
expect(result.color).toBe('#ff0000')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should resolve icon with rgb color', () => {
|
||||||
|
const result = resolveIcon('mdi:home /rgb(255,0,0)', defaultOptions)
|
||||||
|
expect(result.color).toBe('rgb(255,0,0)')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty content', () => {
|
||||||
|
const result = resolveIcon('', defaultOptions)
|
||||||
|
expect(result.name).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle complex size values', () => {
|
||||||
|
expect(resolveIcon('mdi:home =2rem', defaultOptions).size).toBe('2rem')
|
||||||
|
expect(resolveIcon('mdi:home =1.5em', defaultOptions).size).toBe('1.5em')
|
||||||
|
expect(resolveIcon('mdi:home =100%', defaultOptions).size).toBe('100%')
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -52,4 +52,29 @@ describe('stringifyAttrs', () => {
|
|||||||
it('should handle kebabCase keys', () => {
|
it('should handle kebabCase keys', () => {
|
||||||
expect(stringifyAttrs({ 'data-foo': 'bar', 'data-baz': 1, 'fooBaz': 'bar' })).toBe(' data-foo="bar" :data-baz="1" foo-baz="bar"')
|
expect(stringifyAttrs({ 'data-foo': 'bar', 'data-baz': 1, 'fooBaz': 'bar' })).toBe(' data-foo="bar" :data-baz="1" foo-baz="bar"')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle empty string values', () => {
|
||||||
|
expect(stringifyAttrs({ id: '' })).toBe(' id=""')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle special characters in values', () => {
|
||||||
|
expect(stringifyAttrs({ 'data-value': '<script>alert(1)</script>' })).toBe(' data-value="<script>alert(1)</script>"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle unicode values', () => {
|
||||||
|
expect(stringifyAttrs({ title: '你好世界' })).toBe(' title="你好世界"')
|
||||||
|
expect(stringifyAttrs({ 'data-emoji': '🎉🎊' })).toBe(' data-emoji="🎉🎊"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle zero values', () => {
|
||||||
|
expect(stringifyAttrs({ width: 0, height: 0 })).toBe(' :width="0" :height="0"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle negative numbers', () => {
|
||||||
|
expect(stringifyAttrs({ offset: -1 })).toBe(' :offset="-1"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle float numbers', () => {
|
||||||
|
expect(stringifyAttrs({ ratio: 1.5 })).toBe(' :ratio="1.5"')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
54
plugins/plugin-md-power/__test__/stringifyProp.spec.ts
Normal file
54
plugins/plugin-md-power/__test__/stringifyProp.spec.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { stringifyProp } from '../src/node/utils/stringifyProp.js'
|
||||||
|
|
||||||
|
describe('stringifyProp', () => {
|
||||||
|
it('should stringify string', () => {
|
||||||
|
expect(stringifyProp('hello')).toBe('"hello"')
|
||||||
|
expect(stringifyProp('')).toBe('""')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify number', () => {
|
||||||
|
expect(stringifyProp(123)).toBe('123')
|
||||||
|
expect(stringifyProp(0)).toBe('0')
|
||||||
|
expect(stringifyProp(-1)).toBe('-1')
|
||||||
|
expect(stringifyProp(3.14)).toBe('3.14')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify boolean', () => {
|
||||||
|
expect(stringifyProp(true)).toBe('true')
|
||||||
|
expect(stringifyProp(false)).toBe('false')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify null', () => {
|
||||||
|
expect(stringifyProp(null)).toBe('null')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify array', () => {
|
||||||
|
expect(stringifyProp([1, 2, 3])).toBe('[1,2,3]')
|
||||||
|
expect(stringifyProp(['a', 'b'])).toBe('["a","b"]')
|
||||||
|
expect(stringifyProp([])).toBe('[]')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify object', () => {
|
||||||
|
expect(stringifyProp({ a: 1 })).toBe('{"a":1}')
|
||||||
|
expect(stringifyProp({ a: 'b' })).toBe('{"a":"b"}')
|
||||||
|
expect(stringifyProp({})).toBe('{}')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should escape single quotes', () => {
|
||||||
|
expect(stringifyProp('it\'s')).toBe('"it's"')
|
||||||
|
expect(stringifyProp('\'hello\'')).toBe('"'hello'"')
|
||||||
|
expect(stringifyProp('a\'b\'c')).toBe('"a'b'c"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle nested objects', () => {
|
||||||
|
expect(stringifyProp({ a: { b: 1 } })).toBe('{"a":{"b":1}}')
|
||||||
|
expect(stringifyProp({ arr: [1, 2] })).toBe('{"arr":[1,2]}')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle special characters', () => {
|
||||||
|
expect(stringifyProp('hello\nworld')).toBe('"hello\\nworld"')
|
||||||
|
expect(stringifyProp('hello\tworld')).toBe('"hello\\tworld"')
|
||||||
|
expect(stringifyProp('hello"world')).toBe('"hello\\"world"')
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -23,4 +23,35 @@ describe('timeToSeconds(timeLike)', () => {
|
|||||||
expect(timeToSeconds('a:b')).toBe(0)
|
expect(timeToSeconds('a:b')).toBe(0)
|
||||||
expect(timeToSeconds('a : b : c')).toBe(0)
|
expect(timeToSeconds('a : b : c')).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle zero values', () => {
|
||||||
|
expect(timeToSeconds('0')).toBe(0)
|
||||||
|
expect(timeToSeconds('0:0')).toBe(0)
|
||||||
|
expect(timeToSeconds('0:0:0')).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle large values', () => {
|
||||||
|
expect(timeToSeconds('100:00:00')).toBe(360000)
|
||||||
|
expect(timeToSeconds('99:59:59')).toBe(359999)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle decimal seconds', () => {
|
||||||
|
expect(timeToSeconds('1.5')).toBe(1.5)
|
||||||
|
expect(timeToSeconds('1:30.5')).toBe(90.5)
|
||||||
|
expect(timeToSeconds('1:1:1.999')).toBe(3661.999)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle leading zeros', () => {
|
||||||
|
expect(timeToSeconds('01:02:03')).toBe(3723)
|
||||||
|
expect(timeToSeconds('001:002:003')).toBe(3723)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle trailing zeros', () => {
|
||||||
|
expect(timeToSeconds('10:20:30')).toBe(37230)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle whitespace', () => {
|
||||||
|
expect(timeToSeconds(' 1:2:3 ')).toBe(3723)
|
||||||
|
expect(timeToSeconds('1 : 2 : 3')).toBe(3723)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -127,6 +127,7 @@ export const abbrPlugin: PluginWithOptions<Record<string, string>> = (md, global
|
|||||||
|
|
||||||
if (labelEnd < 0 || state.src.charAt(labelEnd + 1) !== ':')
|
if (labelEnd < 0 || state.src.charAt(labelEnd + 1) !== ':')
|
||||||
return false
|
return false
|
||||||
|
/* istanbul ignore if -- @preserve */
|
||||||
if (silent)
|
if (silent)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
@ -219,6 +220,7 @@ export const abbrPlugin: PluginWithOptions<Record<string, string>> = (md, global
|
|||||||
pos = regExp.lastIndex
|
pos = regExp.lastIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore if -- @preserve */
|
||||||
if (!nodes.length)
|
if (!nodes.length)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@ -142,7 +142,7 @@ const annotationDef: RuleBlock = (
|
|||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
/* istanbul ignore if -- @preserve */
|
||||||
if (silent)
|
if (silent)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
@ -228,6 +228,7 @@ const annotationRef: RuleInline = (
|
|||||||
if (annotations.length === 0)
|
if (annotations.length === 0)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
/* istanbul ignore if -- @preserve */
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
const refToken = state.push('annotation_ref', '', 0)
|
const refToken = state.push('annotation_ref', '', 0)
|
||||||
|
|
||||||
@ -272,6 +273,7 @@ export const annotationPlugin: PluginWithOptions<Record<string, string | string[
|
|||||||
env: AnnotationEnv,
|
env: AnnotationEnv,
|
||||||
) => {
|
) => {
|
||||||
const label = tokens[idx].meta.label
|
const label = tokens[idx].meta.label
|
||||||
|
/* istanbul ignore next -- @preserve */
|
||||||
const data = env.annotations[`:${label}`] || annotations[`:${label}`]
|
const data = env.annotations[`:${label}`] || annotations[`:${label}`]
|
||||||
|
|
||||||
return `<Annotation label="${label}" :total="${data.sources.length}">${
|
return `<Annotation label="${label}" :total="${data.sources.length}">${
|
||||||
|
|||||||
@ -12,6 +12,14 @@ export default defineConfig({
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
provider: 'v8',
|
provider: 'v8',
|
||||||
reporter: ['text', 'clover', 'json'],
|
reporter: ['text', 'clover', 'json'],
|
||||||
|
exclude: [
|
||||||
|
'**/node_modules/**',
|
||||||
|
'**/dist/**',
|
||||||
|
'**/lib/**',
|
||||||
|
'**/demo/**',
|
||||||
|
'**/demo/supports/**',
|
||||||
|
'**/fileIcons/index.ts',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user