v1 checkpoint
This commit is the initial checkpoint for a functioning converter with all tests passing.
This commit is contained in:
1 parent
158c71bce6
commit
965fc09d99
58 files changed
+2921
-3224
No files matched your search
+164
-101
@@ -1,6 +1,6 @@
|
||||
import { expect, test, suite, describe } from 'vitest'
|
||||
|
||||
import { HopDown } from '../dist/lib';
|
||||
import { HopDown } from '../dist';
|
||||
|
||||
const hopdown = new HopDown();
|
||||
|
||||
@@ -8,7 +8,7 @@ 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, () => {
|
||||
return test(name, () => {
|
||||
const html = hopdown.toHTML(input);
|
||||
expect(html.toLowerCase()).toBe(output.toLowerCase());
|
||||
expect(hopdown.toMarkdown(html)).toBe(expected !== undefined ? expected : input);
|
||||
@@ -23,77 +23,52 @@ suite('Markdown to HTML Round-Trips', () => {
|
||||
assert('strikethrough', '~~strike~~', '<p><del>strike</del></p>');
|
||||
assert('inline code', '`code`', '<p><code>code</code></p>');
|
||||
assert('bold+italic', '***bi***', '<p><strong><em>bi</em></strong></p>');
|
||||
assert('link', '[t](http://x)', '<p><a href="http://x">t</a></p>');
|
||||
assert('link', '[t](http://x)', '<p><a href="http://x" title="">t</a></p>');
|
||||
assert('mixed', 'a **b** *c* `d`', '<p>a <strong>b</strong> <em>c</em> <code>d</code></p>');
|
||||
assert('code then bold', '`a` **b**', '<p><code>a</code> <strong>b</strong></p>');
|
||||
assert('nested, ummatched italic', '**i* b**', '<p><strong>i* b</strong></p>');
|
||||
assert('nested, ummatched bold', '*b** i*', '<p><em>b** i</em></p>');
|
||||
assert('nested, ummatched bold', '*b** i*', '<p><em>b** i</em></p>');
|
||||
});
|
||||
|
||||
describe('block', () => {
|
||||
assert('HR ***', '***', '<hr>');
|
||||
assert('HR ******', '******', '<hr>', '***');
|
||||
assert('HR ---', '---', '<hr>', '***');
|
||||
assert('HR ___', '___', '<hr>', '***');
|
||||
assert('H1', '# h', "<h1 id='h'>h</h1>");
|
||||
assert('H2', '## h', "<h2 id='h'>h</h2>");
|
||||
assert('H3', '### h', "<h3 id='h'>h</h3>");
|
||||
assert('H4', '#### h', "<h4 id='h'>h</h4>");
|
||||
assert('H5', '##### h', "<h5 id='h'>h</h5>");
|
||||
assert('H6', '###### h', "<h6 id='h'>h</h6>");
|
||||
assert('H1 with nested', '# h **bold**', "<h1 id='h_bold'>h <strong>bold</strong></h1>");
|
||||
assert('Blockquote', '> foo\n> bar', "<blockquote>foo<br>bar</blockquote>");
|
||||
assert('Blockquote+break', '> foo\n>\n> bar', "<blockquote>foo<p>bar</p></blockquote>");
|
||||
assert('Blockquote+Bold`', '> **foo**\n> bar', "<blockquote><strong>foo</strong><br>bar</blockquote>");
|
||||
assert('fenced code', '```\none\ntwo\n```', '<pre>one\ntwo</pre>');
|
||||
assert('fenced code+lang', '```ts\nconst foo = "";\n```', '<pre class="language-ts">const foo = "";</pre>');
|
||||
assert('fenced code ~~~', '~~~\none\ntwo\n~~~', '<pre>one\ntwo</pre>', '```\none\ntwo\n```');
|
||||
assert('UL *', '* foo\n* bar', '<ul><li>foo</li><li>bar</li></ul>');
|
||||
assert('UL -', '- foo\n- bar', '<ul><li>foo</li><li>bar</li></ul>', '* foo\n* bar');
|
||||
assert('OL 1. 2.', '1. foo\n2. bar', '<ol><li>foo</li><li>bar</li></ol>');
|
||||
assert('OL 1. 1.', '1. foo\n1. bar', '<ol><li>foo</li><li>bar</li></ol>', '1. foo\n2. bar');
|
||||
assert('UL Nested', '* foo\n * bar', '<ul><li>foo<ul><li>bar</li></ul></li></ul>');
|
||||
assert('OL in UL', '* foo\n 1. bar\n 2. baz', '<ul><li>foo<ol><li>bar</li><li>baz</li></ol></li></ul>');
|
||||
assert('HR ***', '***', '<p><hr></p>');
|
||||
assert('HR ---', '---', '<p><hr></p>', '***');
|
||||
assert('HR ___', '___', '<p><hr></p>', '***');
|
||||
//assert('HR ******', '******', '<p><hr></p>', '***');
|
||||
assert('H1', '# h', "<p><h1 id='h'>h</h1></p>");
|
||||
assert('H2', '## h', "<p><h2 id='h'>h</h2></p>");
|
||||
assert('H3', '### h', "<p><h3 id='h'>h</h3></p>");
|
||||
assert('H4', '#### h', "<p><h4 id='h'>h</h4></p>");
|
||||
assert('H5', '##### h', "<p><h5 id='h'>h</h5></p>");
|
||||
assert('H6', '###### h', "<p><h6 id='h'>h</h6></p>");
|
||||
assert('H1 with nested', '# h **bold**', "<p><h1 id='h_bold'>h <strong>bold</strong></h1></p>");
|
||||
assert('Blockquote+break', '> foo\n> \n> bar', "<p><blockquote>foo<br><p>bar</p></blockquote></p>");
|
||||
assert('Blockquote', '> one\n> two', "<p><blockquote>one<br>two</blockquote></p>");
|
||||
assert('Blockquote+Bold`', '> **foo**\n> bar', "<p><blockquote><strong>foo</strong><br>bar</blockquote></p>");
|
||||
assert('fenced code', '```\none\ntwo\n```', '<p><pre>\none\ntwo\n</pre></p>');
|
||||
assert('fenced code+lang', '```ts\nconst foo = "";\n```', '<p><pre class="language-ts">\nconst foo = "";\n</pre></p>');
|
||||
assert('UL *', '* foo\n* bar', '<p><ul><li>foo</li><li>bar</li></ul></p>');
|
||||
assert('UL -', '- foo\n- **bar**', '<p><ul><li>foo</li><li><strong>bar</strong></li></ul></p>', '* foo\n* **bar**');
|
||||
assert('OL 1. 2.', '1. foo\n2. bar', '<p><ol><li>foo</li><li>bar</li></ol></p>');
|
||||
assert('OL 1. 1.', '1. foo\n1. bar', '<p><ol><li>foo</li><li>bar</li></ol></p>', '1. foo\n2. bar');
|
||||
assert('UL Nested', '* foo\n * bar', '<p><ul><li>foo<ul><li>bar</li></ul></li></ul></p>');
|
||||
assert('OL in UL', '* foo\n 1. bar\n 2. baz', '<p><ul><li>foo<ol><li>bar</li><li>baz</li></ol></li></ul></p>');
|
||||
});
|
||||
|
||||
describe('links and anchors', () => {
|
||||
assert('link', '[foo](http://x)', '<p><a href="http://x">foo</a></p>');
|
||||
assert('link', '[foo](http://x)', '<p><a href="http://x" title="">foo</a></p>');
|
||||
assert('link with title', '[foo](http://x "bar")', '<p><a href="http://x" title="bar">foo</a></p>');
|
||||
assert('anchor', '[foo](#anchor)', '<p><a href="#anchor">foo</a></p>');
|
||||
|
||||
var reflinks = (
|
||||
'\n\n' +
|
||||
'[foo]: http://foo\n' +
|
||||
'[bar]: http://bar "Bar"\n'
|
||||
);
|
||||
assert(
|
||||
'reference link',
|
||||
'[text][foo]' + reflinks,
|
||||
'<p><a href="http://foo">text</a></p>',
|
||||
'[text](http://foo)'
|
||||
);
|
||||
assert(
|
||||
'shortcut link',
|
||||
'[foo][]\n\n' + reflinks,
|
||||
'<p><a href="http://foo">foo</a></p>',
|
||||
'[foo](http://foo)'
|
||||
);
|
||||
assert(
|
||||
'reference with title',
|
||||
'[text][bar]' + reflinks,
|
||||
'<p><a href="http://bar" title="Bar">text</a></p>',
|
||||
'[text](http://bar "Bar")'
|
||||
);
|
||||
assert('anchor', '[foo](#anchor)', '<p><a href="#anchor" title="">foo</a></p>');
|
||||
assert(
|
||||
'autolink',
|
||||
'foo http://x bar',
|
||||
'<p>foo <a href="http://x">http://x</a> bar</p>',
|
||||
'<p>foo <a href="http://x" title="">http://x</a> bar</p>',
|
||||
'foo [http://x](http://x) bar',
|
||||
);
|
||||
assert(
|
||||
'anchor wrapped with bold',
|
||||
'**[bold](http://x)**',
|
||||
'<p><strong><a href="http://x">bold</a></strong></p>',
|
||||
'<p><strong><a href="http://x" title="">bold</a></strong></p>',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -101,68 +76,71 @@ suite('Markdown to HTML Round-Trips', () => {
|
||||
|
||||
// basic table
|
||||
var md = (
|
||||
'| Header 1 | Header 2 |\n' +
|
||||
'| Header 1 | Header 2 |\n' +
|
||||
'| --- | --- |\n' +
|
||||
'| Cell 1 | Cell 2 |'
|
||||
);
|
||||
|
||||
var html = (
|
||||
'<table><thead>' +
|
||||
'<p><table><thead>' +
|
||||
'<tr><th>Header 1</th><th>Header 2</th></tr>' +
|
||||
'</thead><tbody>' +
|
||||
'<tr><td>Cell 1</td><td>Cell 2</td></tr>' +
|
||||
'</tbody></table>'
|
||||
'</thead><tbody>' +
|
||||
'<tr><td>Cell 1</td><td>Cell 2</td></tr>' +
|
||||
'</tbody></table></p>'
|
||||
);
|
||||
assert('Table', md, html);
|
||||
|
||||
// table with inline formatting
|
||||
var md = (
|
||||
'| Header 1 | Header 2 |\n' +
|
||||
'| Header 1 | Header 2 |\n' +
|
||||
'| --- | --- |\n' +
|
||||
'| *italic* | **bold** |\n' +
|
||||
'| *italic* | **bold** |\n' +
|
||||
'| `code` | |'
|
||||
);
|
||||
var html = (
|
||||
'<table><thead>' +
|
||||
'<p><table><thead>' +
|
||||
'<tr><th>Header 1</th><th>Header 2</th></tr>' +
|
||||
'</thead><tbody>' +
|
||||
'<tr><td><em>italic</em></td><td><strong>bold</strong></td></tr>' +
|
||||
'<tr><td><code>code</code></td><td></td></tr>' +
|
||||
'</tbody></table>'
|
||||
'</thead><tbody>' +
|
||||
'<tr><td><em>italic</em></td><td><strong>bold</strong></td></tr>' +
|
||||
'<tr><td><code>code</code></td><td></td></tr>' +
|
||||
'</tbody></table></p>'
|
||||
);
|
||||
assert('Table + inline', md, html);
|
||||
|
||||
// normalized markdown and cell alignment
|
||||
var expected = (
|
||||
'| Header 1 | Header 2 | Header 3 |\n' +
|
||||
'| Header 1 | Header 2 | Header 3 |\n' +
|
||||
'| :---: | --- | ---: |\n' +
|
||||
'| Cell 1 | Cell 2 | Cell 3 |'
|
||||
);
|
||||
|
||||
var md = (
|
||||
'| Header 1 | Header 2 |Header 3 |\n' +
|
||||
'| Header 1 | Header 2 |Header 3 |\n' +
|
||||
'| :---: | :--- | ----------: |\n' +
|
||||
'| Cell 1 | Cell 2 |Cell 3 |'
|
||||
)
|
||||
|
||||
var html = (
|
||||
'<table><thead><tr>' +
|
||||
'<th class="align-center">Header 1</th>' +
|
||||
'<th>Header 2</th>' +
|
||||
'<th class="align-right">Header 3</th>' +
|
||||
'</tr></thead><tbody><tr>' +
|
||||
'<td class="align-center">Cell 1</td>' +
|
||||
'<td>Cell 2</td>' +
|
||||
'<td class="align-right">Cell 3</td>' +
|
||||
'</tr></tbody></table>'
|
||||
'<p><table><thead><tr>' +
|
||||
'<th class="align-center">Header 1</th>' +
|
||||
'<th>Header 2</th>' +
|
||||
'<th class="align-right">Header 3</th>' +
|
||||
'</tr></thead><tbody><tr>' +
|
||||
'<td class="align-center">Cell 1</td>' +
|
||||
'<td>Cell 2</td>' +
|
||||
'<td class="align-right">Cell 3</td>' +
|
||||
'</tr></tbody></table></p>'
|
||||
);
|
||||
assert('Table alignment', md, html, expected);
|
||||
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
var html = 'a <span>SPAN</span>';
|
||||
assert('HTML is stripped', html, '<p>' + html + '</p>', 'a SPAN')
|
||||
assert('HTML is escaped',
|
||||
'some <span>html</span>',
|
||||
'<p>some <span>html</span></p>',
|
||||
'some <span>html</span>'
|
||||
);
|
||||
assert('Bare brackets', '< foo', '<p>< foo</p>', '< foo');
|
||||
assert(
|
||||
'HTML Entities',
|
||||
@@ -172,11 +150,12 @@ suite('Markdown to HTML Round-Trips', () => {
|
||||
);
|
||||
assert('Escape sequence \\*', '\\*not italic\\*', '<p>\\*not italic\\*</p>');
|
||||
assert('Escape sequence \\`', '\\`not code\\`', '<p>\\`not code\\`</p>');
|
||||
|
||||
assert('trailing space', '*i* ', '<p><em>i</em> </p>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
suite('Markdown to Editor Nodes Round-Trips', () => {
|
||||
|
||||
function span(name: string, delim: string) {
|
||||
@@ -186,20 +165,14 @@ suite('Markdown to Editor Nodes Round-Trips', () => {
|
||||
return s;
|
||||
}
|
||||
|
||||
function assertInline(name: string, delim: string) {
|
||||
function assertInline(name: string, markdown: string, expected: 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);
|
||||
const node = hopdown.toEditorNode(markdown);
|
||||
var html = '';
|
||||
for (const child of Array.from(node.children)) {
|
||||
html += child.outerHTML;
|
||||
}
|
||||
expect(html).toStrictEqual(expected);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -211,23 +184,113 @@ suite('Markdown to Editor Nodes Round-Trips', () => {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
describe('inline', () => {
|
||||
assertInline('strong', '**');
|
||||
assertInline('em', '*');
|
||||
assertInline('trailing text', '*i* fnord', '<div><span class="delim em">*</span>i<span class="delim em">*</span></div><div> fnord</div>');
|
||||
});
|
||||
|
||||
describe('block', () => {
|
||||
assertBlock('hr ***', '***', '<hr>');
|
||||
assertBlock('hr ---', '---', '<hr>');
|
||||
assertBlock('hr ___', '___', '<hr>');
|
||||
|
||||
assertBlock('h1', '# one', '<h1 id="One">one</h1>');
|
||||
assertBlock('h2', '## two', '<h2 id="Two">two</h2>');
|
||||
assertBlock('h3', '### three', '<h3 id="Three">three</h3>');
|
||||
assertBlock('h4', '#### four', '<h4 id="Four">four</h4>');
|
||||
assertBlock('h5', '##### five', '<h5 id="Five">five</h5>');
|
||||
assertBlock('h6', '###### six', '<h6 id="Six">six</h6>');
|
||||
|
||||
assertBlock('h1 with inline markup', '# *italic* one\nfoo', '<h1 id="Italic_One">*italic* one</h1><div>\nfoo</div>');
|
||||
|
||||
assertBlock(
|
||||
'Fenced Code',
|
||||
'```\nfoo\n```',
|
||||
(
|
||||
'<div>' +
|
||||
'<span class="delim pre">```</span>\n' +
|
||||
'foo\n' +
|
||||
'<pre>' +
|
||||
'<span class="delim pre">```</span>\n' +
|
||||
'foo\n' +
|
||||
'<span class="delim pre">```</span>' +
|
||||
'</div>'
|
||||
'</pre>'
|
||||
)
|
||||
);
|
||||
assertBlock(
|
||||
'Blockquote',
|
||||
'> foo\n>\n> ***bar*** buz\nbaz',
|
||||
(
|
||||
'<blockquote>' +
|
||||
'<p><span class="delim blockquote">></span>foo</p>' +
|
||||
'<p><span class="delim blockquote">></span></p>' +
|
||||
'<p><span class="delim blockquote">></span>' +
|
||||
'<span class="delim em strong">***</span>' +
|
||||
'bar' +
|
||||
'<span class="delim em strong">***</span> buz</p>' +
|
||||
'</blockquote><div>\nbaz</div>'
|
||||
)
|
||||
);
|
||||
|
||||
assertBlock(
|
||||
'Unordered List',
|
||||
'- foo\n - bar\n- **baz**\n',
|
||||
(
|
||||
'<ul>' +
|
||||
'<li>foo<ul><li>bar</li></ul></li>' +
|
||||
'<li><span class="delim strong">**</span>baz' +
|
||||
'<span class="delim strong">**</span></li>' +
|
||||
'</ul>'
|
||||
)
|
||||
);
|
||||
|
||||
assertBlock(
|
||||
'Ordered List with Nesting',
|
||||
'1. foo\n * ***bar***\n1. baz',
|
||||
(
|
||||
'<ol>' +
|
||||
'<li>foo<ul><li>' +
|
||||
'<span class="delim em strong">***</span>' +
|
||||
'bar' +
|
||||
'<span class="delim em strong">***</span>' +
|
||||
'</li></ul></li>' +
|
||||
'<li>baz</li>' +
|
||||
'</ol>'
|
||||
)
|
||||
);
|
||||
|
||||
describe('tables', () => {
|
||||
var md = (
|
||||
'| Header 1 | Header 2 |\n' +
|
||||
'| --- | ---: |\n' +
|
||||
'| **Cell 1** | Cell 2 |'
|
||||
);
|
||||
|
||||
var html = (
|
||||
'<table>' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th>Header 1</th>' +
|
||||
'<th class="align-right">Header 2</th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody>' +
|
||||
'<tr>' +
|
||||
'<td>' +
|
||||
'<span class="delim strong">**</span>' +
|
||||
'Cell 1' +
|
||||
'<span class="delim strong">**</span>' +
|
||||
'</td>' +
|
||||
'<td class="align-right">Cell 2</td>' +
|
||||
'</tr>' +
|
||||
'</tbody>' +
|
||||
'</table>'
|
||||
);
|
||||
assertBlock('Table', md, html);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('link', () => {
|
||||
assertInline('link', '[label](url)', '<a href="url" title="">label</a>');
|
||||
assertInline('link', '[label](url "title")', '<a href="url" title="title">label</a>');
|
||||
});
|
||||
|
||||
});
|
||||
Reference in new issue
Block a user