1013 lines
28 KiB
JavaScript
1013 lines
28 KiB
JavaScript
import Bold from '~/content_editor/extensions/bold';
|
||
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';
|
||
import Image from '~/content_editor/extensions/image';
|
||
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, createDocBuilder } from './test_utils';
|
||
|
||
const tiptapEditor = createTestEditor({
|
||
extensions: [
|
||
Blockquote,
|
||
Bold,
|
||
BulletList,
|
||
Code,
|
||
CodeBlockHighlight,
|
||
FootnoteDefinition,
|
||
FootnoteReference,
|
||
HardBreak,
|
||
Heading,
|
||
HorizontalRule,
|
||
Image,
|
||
Italic,
|
||
Link,
|
||
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({
|
||
schema: tiptapEditor.schema,
|
||
content,
|
||
});
|
||
|
||
return document;
|
||
};
|
||
|
||
const serialize = (document) =>
|
||
markdownSerializer({}).serialize({
|
||
doc: document,
|
||
pristineDoc: document,
|
||
});
|
||
|
||
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: `
|
||
- 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
|
||
======
|
||
`,
|
||
expectedDoc: doc(
|
||
heading({ ...sourceAttrs('0:18', 'Heading\none\n======'), level: 1 }, 'Heading\none'),
|
||
),
|
||
},
|
||
{
|
||
markdown: `
|
||
Heading
|
||
two
|
||
-------
|
||
`,
|
||
expectedDoc: doc(
|
||
heading({ ...sourceAttrs('0:19', 'Heading\ntwo\n-------'), level: 2 }, 'Heading\ntwo'),
|
||
),
|
||
},
|
||
{
|
||
markdown: `
|
||
- 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
|
||
`,
|
||
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
|
||
`,
|
||
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
|
||
`,
|
||
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
|
||
`,
|
||
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
|
||
`,
|
||
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
|
||
`,
|
||
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 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: `
|
||
- 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: `
|
||
> 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: `
|
||
> - 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: `
|
||
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';
|
||
\`\`\`\
|
||
`,
|
||
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: `
|
||
\`\`\`
|
||
\`\`\`\
|
||
`,
|
||
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'),
|
||
),
|
||
),
|
||
},
|
||
{
|
||
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);
|
||
});
|
||
});
|