import { expect, test, suite, describe } from 'vitest' import { HopDown } from '../dist/lib'; const hopdown = new HopDown(); suite('Markdown to HTML Round-Trips', () => { function assert(name: string, input: string, output: string, expected: string | undefined = undefined) { return test(name, () => { const html = hopdown.toHTML(input); expect(html.toLowerCase()).toBe(output.toLowerCase()); expect(hopdown.toMarkdown(html)).toBe(expected !== undefined ? expected : input); }); } describe('inline', () => { assert('italic *', '*italic*', '
italic
'); assert('italic _', '_italic_', 'italic
', '*italic*'); assert('bold *', '**bold**', 'bold
'); assert('bold _', '__bold__', 'bold
', '**bold**'); assert('strikethrough', '~~strike~~', 'strike
code
bi
'); assert('link', '[t](http://x)', ''); assert('mixed', 'a **b** *c* `d`', 'a b c d
a b
i* b
'); assert('nested, ummatched bold', '*b** i*', 'b** i
'); }); describe('block', () => { assert('HR ***', '***', 'foo"); assert('Blockquote+break', '> foo\n>\n> bar', "
bar
foo"); assert('Blockquote+Bold`', '> **foo**\n> bar', "bar
foo"); assert('fenced code', '```\none\ntwo\n```', '
bar
one\ntwo'); assert('fenced code+lang', '```ts\nconst foo = "";\n```', '
const foo = "";'); assert('fenced code ~~~', '~~~\none\ntwo\n~~~', '
one\ntwo', '```\none\ntwo\n```'); assert('UL *', '* foo\n* bar', '
foo http://x bar
', 'foo [http://x](http://x) bar', ); assert( 'anchor wrapped with bold', '**[bold](http://x)**', '', ); }); describe('tables', () => { // basic table var md = ( '| Header 1 | Header 2 |\n' + '| --- | --- |\n' + '| Cell 1 | Cell 2 |' ); var html = ( '| Header 1 | Header 2 |
|---|---|
| Cell 1 | Cell 2 |
| Header 1 | Header 2 |
|---|---|
| italic | bold |
code |
| Header 1 | ' + 'Header 2 | ' + 'Header 3 | ' + '
|---|---|---|
| Cell 1 | ' + 'Cell 2 | ' + 'Cell 3 | ' + '
' + html + '
', 'a SPAN') assert('Bare brackets', '< foo', '< foo
', '< foo'); assert( 'HTML Entities', '< > & { { &unknown; foo', '< > & { { &unknown; foo
', '< > & { { &unknown; foo' ); assert('Escape sequence \\*', '\\*not italic\\*', '\\*not italic\\*
'); assert('Escape sequence \\`', '\\`not code\\`', '\\`not code\\`
'); }); }); suite('Markdown to Editor Nodes Round-Trips', () => { function span(name: string, delim: string) { const s = document.createElement('span'); s.className = `delim ${name.toLowerCase()}`; s.textContent = delim; return s; } function assertInline(name: string, delim: string) { return test(name, () => { const input = delim + name + delim; const expected = document.createDocumentFragment(); expected.appendChild(document.createElement('div'));; expected.childNodes[0].appendChild(span(name, delim)); expected.childNodes[0].appendChild(document.createTextNode(name)); expected.childNodes[0].appendChild(span(name, delim)); const node = hopdown.toEditorNode(input); expect(node).toStrictEqual(expected); expect(hopdown.toMarkdown(node)).toBe(input); }); } function assertBlock(name: string, input: string, output: string) { return test(name, () => { const div = document.createElement('div'); div.appendChild(hopdown.toEditorNode(input)); expect(div.innerHTML).toStrictEqual(output); }); } describe('inline', () => { assertInline('strong', '**'); assertInline('em', '*'); }); describe('block', () => { assertBlock( 'Fenced Code', '```\nfoo\n```', ( '