diff options
Diffstat (limited to 'spec/frontend/content_editor/remark_markdown_processing_spec.js')
-rw-r--r-- | spec/frontend/content_editor/remark_markdown_processing_spec.js | 885 |
1 files changed, 825 insertions, 60 deletions
diff --git a/spec/frontend/content_editor/remark_markdown_processing_spec.js b/spec/frontend/content_editor/remark_markdown_processing_spec.js index 6348b97d918..60dc540e192 100644 --- a/spec/frontend/content_editor/remark_markdown_processing_spec.js +++ b/spec/frontend/content_editor/remark_markdown_processing_spec.js @@ -3,6 +3,8 @@ import Blockquote from '~/content_editor/extensions/blockquote'; import BulletList from '~/content_editor/extensions/bullet_list'; import Code from '~/content_editor/extensions/code'; import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight'; +import FootnoteDefinition from '~/content_editor/extensions/footnote_definition'; +import FootnoteReference from '~/content_editor/extensions/footnote_reference'; import HardBreak from '~/content_editor/extensions/hard_break'; import Heading from '~/content_editor/extensions/heading'; import HorizontalRule from '~/content_editor/extensions/horizontal_rule'; @@ -11,11 +13,19 @@ import Italic from '~/content_editor/extensions/italic'; import Link from '~/content_editor/extensions/link'; import ListItem from '~/content_editor/extensions/list_item'; import OrderedList from '~/content_editor/extensions/ordered_list'; +import Paragraph from '~/content_editor/extensions/paragraph'; import Sourcemap from '~/content_editor/extensions/sourcemap'; +import Strike from '~/content_editor/extensions/strike'; +import Table from '~/content_editor/extensions/table'; +import TableHeader from '~/content_editor/extensions/table_header'; +import TableRow from '~/content_editor/extensions/table_row'; +import TableCell from '~/content_editor/extensions/table_cell'; +import TaskList from '~/content_editor/extensions/task_list'; +import TaskItem from '~/content_editor/extensions/task_item'; import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer'; import markdownSerializer from '~/content_editor/services/markdown_serializer'; -import { createTestEditor } from './test_utils'; +import { createTestEditor, createDocBuilder } from './test_utils'; const tiptapEditor = createTestEditor({ extensions: [ @@ -24,6 +34,8 @@ const tiptapEditor = createTestEditor({ BulletList, Code, CodeBlockHighlight, + FootnoteDefinition, + FootnoteReference, HardBreak, Heading, HorizontalRule, @@ -33,9 +45,72 @@ const tiptapEditor = createTestEditor({ ListItem, OrderedList, Sourcemap, + Strike, + Table, + TableRow, + TableHeader, + TableCell, + TaskList, + TaskItem, ], }); +const { + builders: { + doc, + paragraph, + bold, + blockquote, + bulletList, + code, + codeBlock, + footnoteDefinition, + footnoteReference, + hardBreak, + heading, + horizontalRule, + image, + italic, + link, + listItem, + orderedList, + strike, + table, + tableRow, + tableHeader, + tableCell, + taskItem, + taskList, + }, +} = createDocBuilder({ + tiptapEditor, + names: { + blockquote: { nodeType: Blockquote.name }, + bold: { markType: Bold.name }, + bulletList: { nodeType: BulletList.name }, + code: { markType: Code.name }, + codeBlock: { nodeType: CodeBlockHighlight.name }, + footnoteDefinition: { nodeType: FootnoteDefinition.name }, + footnoteReference: { nodeType: FootnoteReference.name }, + hardBreak: { nodeType: HardBreak.name }, + heading: { nodeType: Heading.name }, + horizontalRule: { nodeType: HorizontalRule.name }, + image: { nodeType: Image.name }, + italic: { nodeType: Italic.name }, + link: { markType: Link.name }, + listItem: { nodeType: ListItem.name }, + orderedList: { nodeType: OrderedList.name }, + paragraph: { nodeType: Paragraph.name }, + strike: { nodeType: Strike.name }, + table: { nodeType: Table.name }, + tableCell: { nodeType: TableCell.name }, + tableHeader: { nodeType: TableHeader.name }, + tableRow: { nodeType: TableRow.name }, + taskItem: { nodeType: TaskItem.name }, + taskList: { nodeType: TaskList.name }, + }, +}); + describe('Client side Markdown processing', () => { const deserialize = async (content) => { const { document } = await remarkMarkdownDeserializer().deserialize({ @@ -52,197 +127,887 @@ describe('Client side Markdown processing', () => { pristineDoc: document, }); - it.each([ + const sourceAttrs = (sourceMapKey, sourceMarkdown) => ({ + sourceMapKey, + sourceMarkdown, + }); + + const examples = [ { markdown: '__bold text__', + expectedDoc: doc( + paragraph( + sourceAttrs('0:13', '__bold text__'), + bold(sourceAttrs('0:13', '__bold text__'), 'bold text'), + ), + ), }, { markdown: '**bold text**', + expectedDoc: doc( + paragraph( + sourceAttrs('0:13', '**bold text**'), + bold(sourceAttrs('0:13', '**bold text**'), 'bold text'), + ), + ), }, { markdown: '<strong>bold text</strong>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:26', '<strong>bold text</strong>'), + bold(sourceAttrs('0:26', '<strong>bold text</strong>'), 'bold text'), + ), + ), }, { markdown: '<b>bold text</b>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:16', '<b>bold text</b>'), + bold(sourceAttrs('0:16', '<b>bold text</b>'), 'bold text'), + ), + ), }, { markdown: '_italic text_', + expectedDoc: doc( + paragraph( + sourceAttrs('0:13', '_italic text_'), + italic(sourceAttrs('0:13', '_italic text_'), 'italic text'), + ), + ), }, { markdown: '*italic text*', + expectedDoc: doc( + paragraph( + sourceAttrs('0:13', '*italic text*'), + italic(sourceAttrs('0:13', '*italic text*'), 'italic text'), + ), + ), }, { markdown: '<em>italic text</em>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:20', '<em>italic text</em>'), + italic(sourceAttrs('0:20', '<em>italic text</em>'), 'italic text'), + ), + ), }, { markdown: '<i>italic text</i>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:18', '<i>italic text</i>'), + italic(sourceAttrs('0:18', '<i>italic text</i>'), 'italic text'), + ), + ), }, { markdown: '`inline code`', + expectedDoc: doc( + paragraph( + sourceAttrs('0:13', '`inline code`'), + code(sourceAttrs('0:13', '`inline code`'), 'inline code'), + ), + ), }, { markdown: '**`inline code bold`**', + expectedDoc: doc( + paragraph( + sourceAttrs('0:22', '**`inline code bold`**'), + bold( + sourceAttrs('0:22', '**`inline code bold`**'), + code(sourceAttrs('2:20', '`inline code bold`'), 'inline code bold'), + ), + ), + ), + }, + { + markdown: '_`inline code italics`_', + expectedDoc: doc( + paragraph( + sourceAttrs('0:23', '_`inline code italics`_'), + italic( + sourceAttrs('0:23', '_`inline code italics`_'), + code(sourceAttrs('1:22', '`inline code italics`'), 'inline code italics'), + ), + ), + ), + }, + { + markdown: ` +<i class="foo"> + *bar* +</i> + `, + expectedDoc: doc( + paragraph( + sourceAttrs('0:28', '<i class="foo">\n *bar*\n</i>'), + italic(sourceAttrs('0:28', '<i class="foo">\n *bar*\n</i>'), '\n *bar*\n'), + ), + ), + }, + { + markdown: ` + +<img src="bar" alt="foo" /> + + `, + expectedDoc: doc( + paragraph( + sourceAttrs('0:27', '<img src="bar" alt="foo" />'), + image({ ...sourceAttrs('0:27', '<img src="bar" alt="foo" />'), alt: 'foo', src: 'bar' }), + ), + ), }, { - markdown: '__`inline code italics`__', + markdown: ` +- List item 1 + +<img src="bar" alt="foo" /> + + `, + expectedDoc: doc( + bulletList( + sourceAttrs('0:13', '- List item 1'), + listItem( + sourceAttrs('0:13', '- List item 1'), + paragraph(sourceAttrs('2:13', 'List item 1'), 'List item 1'), + ), + ), + paragraph( + sourceAttrs('15:42', '<img src="bar" alt="foo" />'), + image({ ...sourceAttrs('15:42', '<img src="bar" alt="foo" />'), alt: 'foo', src: 'bar' }), + ), + ), }, { markdown: '[GitLab](https://gitlab.com "Go to GitLab")', + expectedDoc: doc( + paragraph( + sourceAttrs('0:43', '[GitLab](https://gitlab.com "Go to GitLab")'), + link( + { + ...sourceAttrs('0:43', '[GitLab](https://gitlab.com "Go to GitLab")'), + href: 'https://gitlab.com', + title: 'Go to GitLab', + }, + 'GitLab', + ), + ), + ), }, { markdown: '**[GitLab](https://gitlab.com "Go to GitLab")**', + expectedDoc: doc( + paragraph( + sourceAttrs('0:47', '**[GitLab](https://gitlab.com "Go to GitLab")**'), + bold( + sourceAttrs('0:47', '**[GitLab](https://gitlab.com "Go to GitLab")**'), + link( + { + ...sourceAttrs('2:45', '[GitLab](https://gitlab.com "Go to GitLab")'), + href: 'https://gitlab.com', + title: 'Go to GitLab', + }, + 'GitLab', + ), + ), + ), + ), + }, + { + markdown: 'www.commonmark.org', + expectedDoc: doc( + paragraph( + sourceAttrs('0:18', 'www.commonmark.org'), + link( + { + ...sourceAttrs('0:18', 'www.commonmark.org'), + href: 'http://www.commonmark.org', + }, + 'www.commonmark.org', + ), + ), + ), + }, + { + markdown: 'Visit www.commonmark.org/help for more information.', + expectedDoc: doc( + paragraph( + sourceAttrs('0:51', 'Visit www.commonmark.org/help for more information.'), + 'Visit ', + link( + { + ...sourceAttrs('6:29', 'www.commonmark.org/help'), + href: 'http://www.commonmark.org/help', + }, + 'www.commonmark.org/help', + ), + ' for more information.', + ), + ), + }, + { + markdown: 'hello@mail+xyz.example isn’t valid, but hello+xyz@mail.example is.', + expectedDoc: doc( + paragraph( + sourceAttrs('0:66', 'hello@mail+xyz.example isn’t valid, but hello+xyz@mail.example is.'), + 'hello@mail+xyz.example isn’t valid, but ', + link( + { + ...sourceAttrs('40:62', 'hello+xyz@mail.example'), + href: 'mailto:hello+xyz@mail.example', + }, + 'hello+xyz@mail.example', + ), + ' is.', + ), + ), + }, + { + markdown: '[https://gitlab.com>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:20', '[https://gitlab.com>'), + '[', + link( + { + ...sourceAttrs(), + href: 'https://gitlab.com', + }, + 'https://gitlab.com', + ), + '>', + ), + ), }, { markdown: ` This is a paragraph with a\\ hard line break`, + expectedDoc: doc( + paragraph( + sourceAttrs('0:43', 'This is a paragraph with a\\\nhard line break'), + 'This is a paragraph with a', + hardBreak(sourceAttrs('26:28', '\\\n')), + '\nhard line break', + ), + ), }, { markdown: '![GitLab Logo](https://gitlab.com/logo.png "GitLab Logo")', + expectedDoc: doc( + paragraph( + sourceAttrs('0:57', '![GitLab Logo](https://gitlab.com/logo.png "GitLab Logo")'), + image({ + ...sourceAttrs('0:57', '![GitLab Logo](https://gitlab.com/logo.png "GitLab Logo")'), + alt: 'GitLab Logo', + src: 'https://gitlab.com/logo.png', + title: 'GitLab Logo', + }), + ), + ), }, { markdown: '---', + expectedDoc: doc(horizontalRule(sourceAttrs('0:3', '---'))), }, { markdown: '***', + expectedDoc: doc(horizontalRule(sourceAttrs('0:3', '***'))), }, { markdown: '___', + expectedDoc: doc(horizontalRule(sourceAttrs('0:3', '___'))), }, { markdown: '<hr>', + expectedDoc: doc(horizontalRule(sourceAttrs('0:4', '<hr>'))), }, { markdown: '# Heading 1', + expectedDoc: doc(heading({ ...sourceAttrs('0:11', '# Heading 1'), level: 1 }, 'Heading 1')), }, { markdown: '## Heading 2', + expectedDoc: doc(heading({ ...sourceAttrs('0:12', '## Heading 2'), level: 2 }, 'Heading 2')), }, { markdown: '### Heading 3', + expectedDoc: doc(heading({ ...sourceAttrs('0:13', '### Heading 3'), level: 3 }, 'Heading 3')), }, { markdown: '#### Heading 4', + expectedDoc: doc( + heading({ ...sourceAttrs('0:14', '#### Heading 4'), level: 4 }, 'Heading 4'), + ), }, { markdown: '##### Heading 5', + expectedDoc: doc( + heading({ ...sourceAttrs('0:15', '##### Heading 5'), level: 5 }, 'Heading 5'), + ), }, { markdown: '###### Heading 6', + expectedDoc: doc( + heading({ ...sourceAttrs('0:16', '###### Heading 6'), level: 6 }, 'Heading 6'), + ), }, - { markdown: ` - Heading - one - ====== - `, +Heading +one +====== + `, + expectedDoc: doc( + heading({ ...sourceAttrs('0:18', 'Heading\none\n======'), level: 1 }, 'Heading\none'), + ), }, { markdown: ` - Heading - two - ------- - `, +Heading +two +------- + `, + expectedDoc: doc( + heading({ ...sourceAttrs('0:19', 'Heading\ntwo\n-------'), level: 2 }, 'Heading\ntwo'), + ), }, { markdown: ` - - List item 1 - - List item 2 - `, +- List item 1 +- List item 2 + `, + expectedDoc: doc( + bulletList( + sourceAttrs('0:27', '- List item 1\n- List item 2'), + listItem( + sourceAttrs('0:13', '- List item 1'), + paragraph(sourceAttrs('2:13', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('14:27', '- List item 2'), + paragraph(sourceAttrs('16:27', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - * List item 1 - * List item 2 - `, +* List item 1 +* List item 2 + `, + expectedDoc: doc( + bulletList( + sourceAttrs('0:27', '* List item 1\n* List item 2'), + listItem( + sourceAttrs('0:13', '* List item 1'), + paragraph(sourceAttrs('2:13', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('14:27', '* List item 2'), + paragraph(sourceAttrs('16:27', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - + List item 1 - + List item 2 - `, ++ List item 1 ++ List item 2 + `, + expectedDoc: doc( + bulletList( + sourceAttrs('0:27', '+ List item 1\n+ List item 2'), + listItem( + sourceAttrs('0:13', '+ List item 1'), + paragraph(sourceAttrs('2:13', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('14:27', '+ List item 2'), + paragraph(sourceAttrs('16:27', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - 1. List item 1 - 1. List item 2 - `, +1. List item 1 +1. List item 2 + `, + expectedDoc: doc( + orderedList( + sourceAttrs('0:29', '1. List item 1\n1. List item 2'), + listItem( + sourceAttrs('0:14', '1. List item 1'), + paragraph(sourceAttrs('3:14', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('15:29', '1. List item 2'), + paragraph(sourceAttrs('18:29', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - 1. List item 1 - 2. List item 2 - `, +1. List item 1 +2. List item 2 + `, + expectedDoc: doc( + orderedList( + sourceAttrs('0:29', '1. List item 1\n2. List item 2'), + listItem( + sourceAttrs('0:14', '1. List item 1'), + paragraph(sourceAttrs('3:14', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('15:29', '2. List item 2'), + paragraph(sourceAttrs('18:29', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - 1) List item 1 - 2) List item 2 - `, +1) List item 1 +2) List item 2 + `, + expectedDoc: doc( + orderedList( + sourceAttrs('0:29', '1) List item 1\n2) List item 2'), + listItem( + sourceAttrs('0:14', '1) List item 1'), + paragraph(sourceAttrs('3:14', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('15:29', '2) List item 2'), + paragraph(sourceAttrs('18:29', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - - List item 1 - - Sub list item 1 - `, +- List item 1 + - Sub list item 1 + `, + expectedDoc: doc( + bulletList( + sourceAttrs('0:33', '- List item 1\n - Sub list item 1'), + listItem( + sourceAttrs('0:33', '- List item 1\n - Sub list item 1'), + paragraph(sourceAttrs('2:13', 'List item 1'), 'List item 1'), + bulletList( + sourceAttrs('16:33', '- Sub list item 1'), + listItem( + sourceAttrs('16:33', '- Sub list item 1'), + paragraph(sourceAttrs('18:33', 'Sub list item 1'), 'Sub list item 1'), + ), + ), + ), + ), + ), }, { markdown: ` - - List item 1 paragraph 1 +- List item 1 paragraph 1 - List item 1 paragraph 2 - - List item 2 - `, + List item 1 paragraph 2 +- List item 2 + `, + expectedDoc: doc( + bulletList( + sourceAttrs( + '0:66', + '- List item 1 paragraph 1\n\n List item 1 paragraph 2\n- List item 2', + ), + listItem( + sourceAttrs('0:52', '- List item 1 paragraph 1\n\n List item 1 paragraph 2'), + paragraph(sourceAttrs('2:25', 'List item 1 paragraph 1'), 'List item 1 paragraph 1'), + paragraph(sourceAttrs('29:52', 'List item 1 paragraph 2'), 'List item 1 paragraph 2'), + ), + listItem( + sourceAttrs('53:66', '- List item 2'), + paragraph(sourceAttrs('55:66', 'List item 2'), 'List item 2'), + ), + ), + ), }, { markdown: ` - > This is a blockquote - `, +- List item with an image ![bar](foo.png) +`, + expectedDoc: doc( + bulletList( + sourceAttrs('0:41', '- List item with an image ![bar](foo.png)'), + listItem( + sourceAttrs('0:41', '- List item with an image ![bar](foo.png)'), + paragraph( + sourceAttrs('2:41', 'List item with an image ![bar](foo.png)'), + 'List item with an image', + image({ ...sourceAttrs('26:41', '![bar](foo.png)'), alt: 'bar', src: 'foo.png' }), + ), + ), + ), + ), }, { markdown: ` - > - List item 1 - > - List item 2 - `, +> This is a blockquote + `, + expectedDoc: doc( + blockquote( + sourceAttrs('0:22', '> This is a blockquote'), + paragraph(sourceAttrs('2:22', 'This is a blockquote'), 'This is a blockquote'), + ), + ), }, { markdown: ` - const fn = () => 'GitLab'; - `, +> - List item 1 +> - List item 2 + `, + expectedDoc: doc( + blockquote( + sourceAttrs('0:31', '> - List item 1\n> - List item 2'), + bulletList( + sourceAttrs('2:31', '- List item 1\n> - List item 2'), + listItem( + sourceAttrs('2:15', '- List item 1'), + paragraph(sourceAttrs('4:15', 'List item 1'), 'List item 1'), + ), + listItem( + sourceAttrs('18:31', '- List item 2'), + paragraph(sourceAttrs('20:31', 'List item 2'), 'List item 2'), + ), + ), + ), + ), }, { markdown: ` - \`\`\`javascript - const fn = () => 'GitLab'; - \`\`\`\ - `, +code block + + const fn = () => 'GitLab'; + + `, + expectedDoc: doc( + paragraph(sourceAttrs('0:10', 'code block'), 'code block'), + codeBlock( + { + ...sourceAttrs('12:42', " const fn = () => 'GitLab';"), + class: 'code highlight', + language: null, + }, + "const fn = () => 'GitLab';", + ), + ), }, { markdown: ` - ~~~javascript - const fn = () => 'GitLab'; - ~~~ - `, +\`\`\`javascript +const fn = () => 'GitLab'; +\`\`\`\ + `, + expectedDoc: doc( + codeBlock( + { + ...sourceAttrs('0:44', "```javascript\nconst fn = () => 'GitLab';\n```"), + class: 'code highlight', + language: 'javascript', + }, + "const fn = () => 'GitLab';", + ), + ), }, { markdown: ` - \`\`\` - \`\`\`\ - `, +~~~javascript +const fn = () => 'GitLab'; +~~~ + `, + expectedDoc: doc( + codeBlock( + { + ...sourceAttrs('0:44', "~~~javascript\nconst fn = () => 'GitLab';\n~~~"), + class: 'code highlight', + language: 'javascript', + }, + "const fn = () => 'GitLab';", + ), + ), }, { markdown: ` - \`\`\`javascript - const fn = () => 'GitLab'; +\`\`\` +\`\`\`\ + `, + expectedDoc: doc( + codeBlock( + { + ...sourceAttrs('0:7', '```\n```'), + class: 'code highlight', + language: null, + }, + '', + ), + ), + }, + { + markdown: ` +\`\`\`javascript +const fn = () => 'GitLab'; - \`\`\`\ - `, +\`\`\`\ + `, + expectedDoc: doc( + codeBlock( + { + ...sourceAttrs('0:45', "```javascript\nconst fn = () => 'GitLab';\n\n```"), + class: 'code highlight', + language: 'javascript', + }, + "const fn = () => 'GitLab';\n", + ), + ), + }, + { + markdown: '~~Strikedthrough text~~', + expectedDoc: doc( + paragraph( + sourceAttrs('0:23', '~~Strikedthrough text~~'), + strike(sourceAttrs('0:23', '~~Strikedthrough text~~'), 'Strikedthrough text'), + ), + ), + }, + { + markdown: '<del>Strikedthrough text</del>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:30', '<del>Strikedthrough text</del>'), + strike(sourceAttrs('0:30', '<del>Strikedthrough text</del>'), 'Strikedthrough text'), + ), + ), + }, + { + markdown: '<strike>Strikedthrough text</strike>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:36', '<strike>Strikedthrough text</strike>'), + strike( + sourceAttrs('0:36', '<strike>Strikedthrough text</strike>'), + 'Strikedthrough text', + ), + ), + ), + }, + { + markdown: '<s>Strikedthrough text</s>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:26', '<s>Strikedthrough text</s>'), + strike(sourceAttrs('0:26', '<s>Strikedthrough text</s>'), 'Strikedthrough text'), + ), + ), }, - ])('processes %s correctly', async ({ markdown }) => { + { + markdown: ` +- [ ] task list item 1 +- [ ] task list item 2 + `, + expectedDoc: doc( + taskList( + { + numeric: false, + ...sourceAttrs('0:45', '- [ ] task list item 1\n- [ ] task list item 2'), + }, + taskItem( + { + checked: false, + ...sourceAttrs('0:22', '- [ ] task list item 1'), + }, + paragraph(sourceAttrs('6:22', 'task list item 1'), 'task list item 1'), + ), + taskItem( + { + checked: false, + ...sourceAttrs('23:45', '- [ ] task list item 2'), + }, + paragraph(sourceAttrs('29:45', 'task list item 2'), 'task list item 2'), + ), + ), + ), + }, + { + markdown: ` +- [x] task list item 1 +- [x] task list item 2 + `, + expectedDoc: doc( + taskList( + { + numeric: false, + ...sourceAttrs('0:45', '- [x] task list item 1\n- [x] task list item 2'), + }, + taskItem( + { + checked: true, + ...sourceAttrs('0:22', '- [x] task list item 1'), + }, + paragraph(sourceAttrs('6:22', 'task list item 1'), 'task list item 1'), + ), + taskItem( + { + checked: true, + ...sourceAttrs('23:45', '- [x] task list item 2'), + }, + paragraph(sourceAttrs('29:45', 'task list item 2'), 'task list item 2'), + ), + ), + ), + }, + { + markdown: ` +1. [ ] task list item 1 +2. [ ] task list item 2 + `, + expectedDoc: doc( + taskList( + { + numeric: true, + ...sourceAttrs('0:47', '1. [ ] task list item 1\n2. [ ] task list item 2'), + }, + taskItem( + { + checked: false, + ...sourceAttrs('0:23', '1. [ ] task list item 1'), + }, + paragraph(sourceAttrs('7:23', 'task list item 1'), 'task list item 1'), + ), + taskItem( + { + checked: false, + ...sourceAttrs('24:47', '2. [ ] task list item 2'), + }, + paragraph(sourceAttrs('31:47', 'task list item 2'), 'task list item 2'), + ), + ), + ), + }, + { + markdown: ` +| a | b | +|---|---| +| c | d | +`, + expectedDoc: doc( + table( + sourceAttrs('0:29', '| a | b |\n|---|---|\n| c | d |'), + tableRow( + sourceAttrs('0:9', '| a | b |'), + tableHeader(sourceAttrs('0:5', '| a |'), paragraph(sourceAttrs('2:3', 'a'), 'a')), + tableHeader(sourceAttrs('5:9', ' b |'), paragraph(sourceAttrs('6:7', 'b'), 'b')), + ), + tableRow( + sourceAttrs('20:29', '| c | d |'), + tableCell(sourceAttrs('20:25', '| c |'), paragraph(sourceAttrs('22:23', 'c'), 'c')), + tableCell(sourceAttrs('25:29', ' d |'), paragraph(sourceAttrs('26:27', 'd'), 'd')), + ), + ), + ), + }, + { + markdown: ` +<table> + <tr> + <th colspan="2" rowspan="5">Header</th> + </tr> + <tr> + <td colspan="2" rowspan="5">Body</td> + </tr> +</table> +`, + expectedDoc: doc( + table( + sourceAttrs( + '0:132', + '<table>\n <tr>\n <th colspan="2" rowspan="5">Header</th>\n </tr>\n <tr>\n <td colspan="2" rowspan="5">Body</td>\n </tr>\n</table>', + ), + tableRow( + sourceAttrs('10:66', '<tr>\n <th colspan="2" rowspan="5">Header</th>\n </tr>'), + tableHeader( + { + ...sourceAttrs('19:58', '<th colspan="2" rowspan="5">Header</th>'), + colspan: 2, + rowspan: 5, + }, + paragraph(sourceAttrs('47:53', 'Header'), 'Header'), + ), + ), + tableRow( + sourceAttrs('69:123', '<tr>\n <td colspan="2" rowspan="5">Body</td>\n </tr>'), + tableCell( + { + ...sourceAttrs('78:115', '<td colspan="2" rowspan="5">Body</td>'), + colspan: 2, + rowspan: 5, + }, + paragraph(sourceAttrs('106:110', 'Body'), 'Body'), + ), + ), + ), + ), + }, + { + markdown: ` +This is a footnote [^footnote] + +Paragraph + +[^footnote]: Footnote definition + +Paragraph +`, + expectedDoc: doc( + paragraph( + sourceAttrs('0:30', 'This is a footnote [^footnote]'), + 'This is a footnote ', + footnoteReference({ + ...sourceAttrs('19:30', '[^footnote]'), + identifier: 'footnote', + label: 'footnote', + }), + ), + paragraph(sourceAttrs('32:41', 'Paragraph'), 'Paragraph'), + footnoteDefinition( + { + ...sourceAttrs('43:75', '[^footnote]: Footnote definition'), + identifier: 'footnote', + label: 'footnote', + }, + paragraph(sourceAttrs('56:75', 'Footnote definition'), 'Footnote definition'), + ), + paragraph(sourceAttrs('77:86', 'Paragraph'), 'Paragraph'), + ), + }, + ]; + + const runOnly = examples.find((example) => example.only === true); + const runExamples = runOnly ? [runOnly] : examples; + + it.each(runExamples)('processes %s correctly', async ({ markdown, expectedDoc }) => { const trimmed = markdown.trim(); const document = await deserialize(trimmed); + expect(expectedDoc).not.toBeFalsy(); + expect(document.toJSON()).toEqual(expectedDoc.toJSON()); expect(serialize(document)).toEqual(trimmed); }); }); |