2021-08-19 21:10:32 +00:00
|
|
|
import Blockquote from '~/content_editor/extensions/blockquote';
|
|
|
|
import Bold from '~/content_editor/extensions/bold';
|
|
|
|
import BulletList from '~/content_editor/extensions/bullet_list';
|
|
|
|
import Code from '~/content_editor/extensions/code';
|
|
|
|
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
|
2021-09-01 18:08:49 +00:00
|
|
|
import DescriptionItem from '~/content_editor/extensions/description_item';
|
|
|
|
import DescriptionList from '~/content_editor/extensions/description_list';
|
2021-10-08 21:09:48 +00:00
|
|
|
import Details from '~/content_editor/extensions/details';
|
|
|
|
import DetailsContent from '~/content_editor/extensions/details_content';
|
2021-08-19 21:10:32 +00:00
|
|
|
import Emoji from '~/content_editor/extensions/emoji';
|
2021-08-31 12:11:07 +00:00
|
|
|
import Figure from '~/content_editor/extensions/figure';
|
|
|
|
import FigureCaption from '~/content_editor/extensions/figure_caption';
|
2021-12-02 00:17:32 +00:00
|
|
|
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
|
|
|
|
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
|
2021-08-19 21:10:32 +00:00
|
|
|
import HardBreak from '~/content_editor/extensions/hard_break';
|
|
|
|
import Heading from '~/content_editor/extensions/heading';
|
|
|
|
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
|
2022-07-01 21:08:27 +00:00
|
|
|
import HTMLNodes from '~/content_editor/extensions/html_nodes';
|
2021-08-19 21:10:32 +00:00
|
|
|
import Image from '~/content_editor/extensions/image';
|
2021-08-26 21:11:25 +00:00
|
|
|
import InlineDiff from '~/content_editor/extensions/inline_diff';
|
2021-08-19 21:10:32 +00:00
|
|
|
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';
|
2022-07-29 15:12:25 +00:00
|
|
|
import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
|
2022-05-11 15:07:26 +00:00
|
|
|
import Sourcemap from '~/content_editor/extensions/sourcemap';
|
2021-08-19 21:10:32 +00:00
|
|
|
import Strike from '~/content_editor/extensions/strike';
|
|
|
|
import Table from '~/content_editor/extensions/table';
|
|
|
|
import TableCell from '~/content_editor/extensions/table_cell';
|
|
|
|
import TableHeader from '~/content_editor/extensions/table_header';
|
|
|
|
import TableRow from '~/content_editor/extensions/table_row';
|
2021-08-26 21:11:25 +00:00
|
|
|
import TaskItem from '~/content_editor/extensions/task_item';
|
|
|
|
import TaskList from '~/content_editor/extensions/task_list';
|
2021-08-19 21:10:32 +00:00
|
|
|
import markdownSerializer from '~/content_editor/services/markdown_serializer';
|
2022-05-11 15:07:26 +00:00
|
|
|
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
|
2022-10-26 18:11:17 +00:00
|
|
|
import { createTiptapEditor, createDocBuilder } from '../test_utils';
|
2021-08-19 21:10:32 +00:00
|
|
|
|
|
|
|
jest.mock('~/emoji');
|
2022-10-26 18:11:17 +00:00
|
|
|
const tiptapEditor = createTiptapEditor([Sourcemap]);
|
2021-08-19 21:10:32 +00:00
|
|
|
|
|
|
|
const {
|
|
|
|
builders: {
|
2022-08-23 00:09:41 +00:00
|
|
|
audio,
|
2021-08-19 21:10:32 +00:00
|
|
|
doc,
|
|
|
|
blockquote,
|
|
|
|
bold,
|
|
|
|
bulletList,
|
|
|
|
code,
|
|
|
|
codeBlock,
|
2021-10-08 21:09:48 +00:00
|
|
|
details,
|
|
|
|
detailsContent,
|
2022-07-01 21:08:27 +00:00
|
|
|
div,
|
2021-09-01 18:08:49 +00:00
|
|
|
descriptionItem,
|
|
|
|
descriptionList,
|
2021-08-19 21:10:32 +00:00
|
|
|
emoji,
|
2021-12-02 00:17:32 +00:00
|
|
|
footnoteDefinition,
|
|
|
|
footnoteReference,
|
2021-08-31 12:11:07 +00:00
|
|
|
figure,
|
|
|
|
figureCaption,
|
2021-08-19 21:10:32 +00:00
|
|
|
heading,
|
|
|
|
hardBreak,
|
|
|
|
horizontalRule,
|
|
|
|
image,
|
2021-08-26 21:11:25 +00:00
|
|
|
inlineDiff,
|
2021-08-19 21:10:32 +00:00
|
|
|
italic,
|
|
|
|
link,
|
|
|
|
listItem,
|
|
|
|
orderedList,
|
|
|
|
paragraph,
|
2022-07-29 15:12:25 +00:00
|
|
|
referenceDefinition,
|
2021-08-19 21:10:32 +00:00
|
|
|
strike,
|
|
|
|
table,
|
|
|
|
tableCell,
|
|
|
|
tableHeader,
|
|
|
|
tableRow,
|
2021-08-26 21:11:25 +00:00
|
|
|
taskItem,
|
|
|
|
taskList,
|
2022-08-23 00:09:41 +00:00
|
|
|
video,
|
2021-08-19 21:10:32 +00:00
|
|
|
},
|
|
|
|
} = createDocBuilder({
|
|
|
|
tiptapEditor,
|
|
|
|
names: {
|
|
|
|
blockquote: { nodeType: Blockquote.name },
|
|
|
|
bold: { markType: Bold.name },
|
|
|
|
bulletList: { nodeType: BulletList.name },
|
|
|
|
code: { markType: Code.name },
|
|
|
|
codeBlock: { nodeType: CodeBlockHighlight.name },
|
2021-10-08 21:09:48 +00:00
|
|
|
details: { nodeType: Details.name },
|
|
|
|
detailsContent: { nodeType: DetailsContent.name },
|
2021-09-01 18:08:49 +00:00
|
|
|
descriptionItem: { nodeType: DescriptionItem.name },
|
|
|
|
descriptionList: { nodeType: DescriptionList.name },
|
2021-08-19 21:10:32 +00:00
|
|
|
emoji: { markType: Emoji.name },
|
2021-08-31 12:11:07 +00:00
|
|
|
figure: { nodeType: Figure.name },
|
|
|
|
figureCaption: { nodeType: FigureCaption.name },
|
2021-12-02 00:17:32 +00:00
|
|
|
footnoteDefinition: { nodeType: FootnoteDefinition.name },
|
|
|
|
footnoteReference: { nodeType: FootnoteReference.name },
|
2021-08-19 21:10:32 +00:00
|
|
|
hardBreak: { nodeType: HardBreak.name },
|
|
|
|
heading: { nodeType: Heading.name },
|
|
|
|
horizontalRule: { nodeType: HorizontalRule.name },
|
|
|
|
image: { nodeType: Image.name },
|
2021-08-26 21:11:25 +00:00
|
|
|
inlineDiff: { markType: InlineDiff.name },
|
2021-08-19 21:10:32 +00:00
|
|
|
italic: { nodeType: Italic.name },
|
|
|
|
link: { markType: Link.name },
|
|
|
|
listItem: { nodeType: ListItem.name },
|
|
|
|
orderedList: { nodeType: OrderedList.name },
|
|
|
|
paragraph: { nodeType: Paragraph.name },
|
2022-07-29 15:12:25 +00:00
|
|
|
referenceDefinition: { nodeType: ReferenceDefinition.name },
|
2021-08-19 21:10:32 +00:00
|
|
|
strike: { markType: Strike.name },
|
|
|
|
table: { nodeType: Table.name },
|
|
|
|
tableCell: { nodeType: TableCell.name },
|
|
|
|
tableHeader: { nodeType: TableHeader.name },
|
|
|
|
tableRow: { nodeType: TableRow.name },
|
2021-08-26 21:11:25 +00:00
|
|
|
taskItem: { nodeType: TaskItem.name },
|
|
|
|
taskList: { nodeType: TaskList.name },
|
2022-07-12 21:09:53 +00:00
|
|
|
...HTMLNodes.reduce(
|
|
|
|
(builders, htmlNode) => ({
|
|
|
|
...builders,
|
|
|
|
[htmlNode.name]: { nodeType: htmlNode.name },
|
|
|
|
}),
|
|
|
|
{},
|
|
|
|
),
|
2021-08-19 21:10:32 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const serialize = (...content) =>
|
|
|
|
markdownSerializer({}).serialize({
|
2022-05-11 00:08:02 +00:00
|
|
|
doc: doc(...content),
|
2021-08-19 21:10:32 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('markdownSerializer', () => {
|
2021-08-26 21:11:25 +00:00
|
|
|
it('correctly serializes bold', () => {
|
|
|
|
expect(serialize(paragraph(bold('bold')))).toBe('**bold**');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes italics', () => {
|
|
|
|
expect(serialize(paragraph(italic('italics')))).toBe('_italics_');
|
|
|
|
});
|
|
|
|
|
2022-01-13 21:14:07 +00:00
|
|
|
it('correctly serializes code blocks wrapped by italics and bold marks', () => {
|
|
|
|
const text = 'code block';
|
|
|
|
|
|
|
|
expect(serialize(paragraph(italic(code(text))))).toBe(`_\`${text}\`_`);
|
|
|
|
expect(serialize(paragraph(code(italic(text))))).toBe(`_\`${text}\`_`);
|
|
|
|
expect(serialize(paragraph(bold(code(text))))).toBe(`**\`${text}\`**`);
|
|
|
|
expect(serialize(paragraph(code(bold(text))))).toBe(`**\`${text}\`**`);
|
|
|
|
expect(serialize(paragraph(strike(code(text))))).toBe(`~~\`${text}\`~~`);
|
|
|
|
expect(serialize(paragraph(code(strike(text))))).toBe(`~~\`${text}\`~~`);
|
|
|
|
});
|
|
|
|
|
2021-08-26 21:11:25 +00:00
|
|
|
it('correctly serializes inline diff', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
inlineDiff({ type: 'addition' }, '+30 lines'),
|
|
|
|
inlineDiff({ type: 'deletion' }, '-10 lines'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe('{++30 lines+}{--10 lines-}');
|
|
|
|
});
|
|
|
|
|
2021-08-19 21:10:32 +00:00
|
|
|
it('correctly serializes a line break', () => {
|
|
|
|
expect(serialize(paragraph('hello', hardBreak(), 'world'))).toBe('hello\\\nworld');
|
|
|
|
});
|
|
|
|
|
2021-08-23 18:11:07 +00:00
|
|
|
it('correctly serializes a link', () => {
|
|
|
|
expect(serialize(paragraph(link({ href: 'https://example.com' }, 'example url')))).toBe(
|
|
|
|
'[example url](https://example.com)',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-08-26 21:11:25 +00:00
|
|
|
it('correctly serializes a plain URL link', () => {
|
|
|
|
expect(serialize(paragraph(link({ href: 'https://example.com' }, 'https://example.com')))).toBe(
|
2022-06-14 06:09:22 +00:00
|
|
|
'https://example.com',
|
2021-08-26 21:11:25 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-08-23 18:11:07 +00:00
|
|
|
it('correctly serializes a link with a title', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(link({ href: 'https://example.com', title: 'click this link' }, 'example url')),
|
|
|
|
),
|
|
|
|
).toBe('[example url](https://example.com "click this link")');
|
|
|
|
});
|
|
|
|
|
2021-08-26 21:11:25 +00:00
|
|
|
it('correctly serializes a plain URL link with a title', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
link({ href: 'https://example.com', title: 'link title' }, 'https://example.com'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe('[https://example.com](https://example.com "link title")');
|
|
|
|
});
|
|
|
|
|
2021-08-23 18:11:07 +00:00
|
|
|
it('correctly serializes a link with a canonicalSrc', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
link(
|
|
|
|
{
|
|
|
|
href: '/uploads/abcde/file.zip',
|
|
|
|
canonicalSrc: 'file.zip',
|
|
|
|
title: 'click here to download',
|
|
|
|
},
|
|
|
|
'download file',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe('[download file](file.zip "click here to download")');
|
|
|
|
});
|
|
|
|
|
2022-08-15 15:09:53 +00:00
|
|
|
it('correctly serializes link references', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
link(
|
|
|
|
{
|
|
|
|
href: 'gitlab-url',
|
|
|
|
isReference: true,
|
|
|
|
},
|
|
|
|
'GitLab',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe('[GitLab][gitlab-url]');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes image references', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
image({
|
|
|
|
canonicalSrc: 'gitlab-url',
|
|
|
|
src: 'image.svg',
|
|
|
|
alt: 'GitLab',
|
|
|
|
isReference: true,
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe('![GitLab][gitlab-url]');
|
|
|
|
});
|
|
|
|
|
2021-08-26 21:11:25 +00:00
|
|
|
it('correctly serializes strikethrough', () => {
|
|
|
|
expect(serialize(paragraph(strike('deleted content')))).toBe('~~deleted content~~');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes blockquotes with hard breaks', () => {
|
|
|
|
expect(serialize(blockquote('some text', hardBreak(), hardBreak(), 'new line'))).toBe(
|
|
|
|
`
|
|
|
|
> some text\\
|
|
|
|
> \\
|
|
|
|
> new line
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes blockquote with multiple block nodes', () => {
|
|
|
|
expect(serialize(blockquote(paragraph('some paragraph'), codeBlock('var x = 10;')))).toBe(
|
|
|
|
`
|
|
|
|
> some paragraph
|
|
|
|
>
|
|
|
|
> \`\`\`
|
|
|
|
> var x = 10;
|
|
|
|
> \`\`\`
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a multiline blockquote', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
blockquote(
|
|
|
|
{ multiline: true },
|
|
|
|
paragraph('some paragraph with ', bold('bold')),
|
|
|
|
codeBlock('var y = 10;'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
>>>
|
|
|
|
some paragraph with **bold**
|
|
|
|
|
|
|
|
\`\`\`
|
|
|
|
var y = 10;
|
|
|
|
\`\`\`
|
|
|
|
|
|
|
|
>>>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a code block with language', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
codeBlock(
|
|
|
|
{ language: 'json' },
|
|
|
|
'this is not really json but just trying out whether this case works or not',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
\`\`\`json
|
|
|
|
this is not really json but just trying out whether this case works or not
|
|
|
|
\`\`\`
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes emoji', () => {
|
|
|
|
expect(serialize(paragraph(emoji({ name: 'dog' })))).toBe(':dog:');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes headings', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
heading({ level: 1 }, 'Heading 1'),
|
|
|
|
heading({ level: 2 }, 'Heading 2'),
|
|
|
|
heading({ level: 3 }, 'Heading 3'),
|
|
|
|
heading({ level: 4 }, 'Heading 4'),
|
|
|
|
heading({ level: 5 }, 'Heading 5'),
|
|
|
|
heading({ level: 6 }, 'Heading 6'),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
# Heading 1
|
|
|
|
|
|
|
|
## Heading 2
|
|
|
|
|
|
|
|
### Heading 3
|
|
|
|
|
|
|
|
#### Heading 4
|
|
|
|
|
|
|
|
##### Heading 5
|
|
|
|
|
|
|
|
###### Heading 6
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes horizontal rule', () => {
|
|
|
|
expect(serialize(horizontalRule(), horizontalRule(), horizontalRule())).toBe(
|
|
|
|
`
|
|
|
|
---
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
---
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-08-23 18:11:07 +00:00
|
|
|
it('correctly serializes an image', () => {
|
|
|
|
expect(serialize(paragraph(image({ src: 'img.jpg', alt: 'foo bar' })))).toBe(
|
|
|
|
'![foo bar](img.jpg)',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-01-19 18:14:01 +00:00
|
|
|
it('does not serialize an image when src and canonicalSrc are empty', () => {
|
|
|
|
expect(serialize(paragraph(image({})))).toBe('');
|
|
|
|
});
|
|
|
|
|
2021-08-23 18:11:07 +00:00
|
|
|
it('correctly serializes an image with a title', () => {
|
|
|
|
expect(serialize(paragraph(image({ src: 'img.jpg', title: 'baz', alt: 'foo bar' })))).toBe(
|
|
|
|
'![foo bar](img.jpg "baz")',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes an image with a canonicalSrc', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
image({
|
|
|
|
src: '/uploads/abcde/file.png',
|
|
|
|
alt: 'this is an image',
|
|
|
|
canonicalSrc: 'file.png',
|
|
|
|
title: 'foo bar baz',
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe('![this is an image](file.png "foo bar baz")');
|
|
|
|
});
|
|
|
|
|
2021-08-26 21:11:25 +00:00
|
|
|
it('correctly serializes bullet list', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
bulletList(
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(paragraph('list item 3')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
* list item 1
|
|
|
|
* list item 2
|
|
|
|
* list item 3
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes bullet list with different bullet styles', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
bulletList(
|
|
|
|
{ bullet: '+' },
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(
|
|
|
|
paragraph('list item 3'),
|
|
|
|
bulletList(
|
|
|
|
{ bullet: '-' },
|
|
|
|
listItem(paragraph('sub-list item 1')),
|
|
|
|
listItem(paragraph('sub-list item 2')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
+ list item 1
|
|
|
|
+ list item 2
|
|
|
|
+ list item 3
|
|
|
|
- sub-list item 1
|
|
|
|
- sub-list item 2
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a numeric list', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
orderedList(
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(paragraph('list item 3')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
1. list item 1
|
|
|
|
2. list item 2
|
|
|
|
3. list item 3
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a numeric list with parens', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
orderedList(
|
|
|
|
{ parens: true },
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(paragraph('list item 3')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
1) list item 1
|
|
|
|
2) list item 2
|
|
|
|
3) list item 3
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a numeric list with a different start order', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
orderedList(
|
|
|
|
{ start: 17 },
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(paragraph('list item 3')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
17. list item 1
|
|
|
|
18. list item 2
|
|
|
|
19. list item 3
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a numeric list with an invalid start order', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
orderedList(
|
|
|
|
{ start: NaN },
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(paragraph('list item 3')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
1. list item 1
|
|
|
|
2. list item 2
|
|
|
|
3. list item 3
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a bullet list inside an ordered list', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
orderedList(
|
|
|
|
{ start: 17 },
|
|
|
|
listItem(paragraph('list item 1')),
|
|
|
|
listItem(paragraph('list item 2')),
|
|
|
|
listItem(
|
|
|
|
paragraph('list item 3'),
|
|
|
|
bulletList(
|
|
|
|
listItem(paragraph('sub-list item 1')),
|
|
|
|
listItem(paragraph('sub-list item 2')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
// notice that 4 space indent works fine in this case,
|
|
|
|
// when it usually wouldn't
|
|
|
|
`
|
|
|
|
17. list item 1
|
|
|
|
18. list item 2
|
|
|
|
19. list item 3
|
|
|
|
* sub-list item 1
|
|
|
|
* sub-list item 2
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a task list', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
taskList(
|
|
|
|
taskItem({ checked: true }, paragraph('list item 1')),
|
|
|
|
taskItem(paragraph('list item 2')),
|
|
|
|
taskItem(
|
|
|
|
paragraph('list item 3'),
|
|
|
|
taskList(
|
|
|
|
taskItem({ checked: true }, paragraph('sub-list item 1')),
|
|
|
|
taskItem(paragraph('sub-list item 2')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
* [x] list item 1
|
|
|
|
* [ ] list item 2
|
|
|
|
* [ ] list item 3
|
|
|
|
* [x] sub-list item 1
|
|
|
|
* [ ] sub-list item 2
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a numeric task list + with start order', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
taskList(
|
|
|
|
{ numeric: true },
|
|
|
|
taskItem({ checked: true }, paragraph('list item 1')),
|
|
|
|
taskItem(paragraph('list item 2')),
|
|
|
|
taskItem(
|
|
|
|
paragraph('list item 3'),
|
|
|
|
taskList(
|
|
|
|
{ numeric: true, start: 1351, parens: true },
|
|
|
|
taskItem({ checked: true }, paragraph('sub-list item 1')),
|
|
|
|
taskItem(paragraph('sub-list item 2')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
1. [x] list item 1
|
|
|
|
2. [ ] list item 2
|
|
|
|
3. [ ] list item 3
|
|
|
|
1351) [x] sub-list item 1
|
|
|
|
1352) [ ] sub-list item 2
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-09-01 18:08:49 +00:00
|
|
|
it('correctly renders a description list', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
descriptionList(
|
|
|
|
descriptionItem(paragraph('Beast of Bodmin')),
|
|
|
|
descriptionItem({ isTerm: false }, paragraph('A large feline inhabiting Bodmin Moor.')),
|
|
|
|
|
|
|
|
descriptionItem(paragraph('Morgawr')),
|
|
|
|
descriptionItem({ isTerm: false }, paragraph('A sea serpent.')),
|
|
|
|
|
|
|
|
descriptionItem(paragraph('Owlman')),
|
|
|
|
descriptionItem(
|
|
|
|
{ isTerm: false },
|
|
|
|
paragraph('A giant ', italic('owl-like'), ' creature.'),
|
|
|
|
),
|
|
|
|
),
|
2022-03-03 03:19:02 +00:00
|
|
|
heading('this is a heading'),
|
2021-09-01 18:08:49 +00:00
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<dl>
|
|
|
|
<dt>Beast of Bodmin</dt>
|
|
|
|
<dd>A large feline inhabiting Bodmin Moor.</dd>
|
|
|
|
<dt>Morgawr</dt>
|
|
|
|
<dd>A sea serpent.</dd>
|
|
|
|
<dt>Owlman</dt>
|
|
|
|
<dd>
|
|
|
|
|
|
|
|
A giant _owl-like_ creature.
|
|
|
|
|
|
|
|
</dd>
|
|
|
|
</dl>
|
2022-03-03 03:19:02 +00:00
|
|
|
|
|
|
|
# this is a heading
|
2021-09-01 18:08:49 +00:00
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-10-08 21:09:48 +00:00
|
|
|
it('correctly renders a simple details/summary', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
details(
|
|
|
|
detailsContent(paragraph('this is the summary')),
|
|
|
|
detailsContent(paragraph('this content will be hidden')),
|
|
|
|
),
|
2022-03-03 03:19:02 +00:00
|
|
|
heading('this is a heading'),
|
2021-10-08 21:09:48 +00:00
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<details>
|
|
|
|
<summary>this is the summary</summary>
|
|
|
|
this content will be hidden
|
|
|
|
</details>
|
2022-03-03 03:19:02 +00:00
|
|
|
|
|
|
|
# this is a heading
|
2021-10-08 21:09:48 +00:00
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly renders details/summary with styled content', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
details(
|
|
|
|
detailsContent(paragraph('this is the ', bold('summary'))),
|
|
|
|
detailsContent(
|
|
|
|
codeBlock(
|
|
|
|
{ language: 'javascript' },
|
|
|
|
'var a = 2;\nvar b = 3;\nvar c = a + d;\n\nconsole.log(c);',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
detailsContent(paragraph('this content will be ', italic('hidden'))),
|
|
|
|
),
|
|
|
|
details(detailsContent(paragraph('summary 2')), detailsContent(paragraph('content 2'))),
|
2022-03-03 03:19:02 +00:00
|
|
|
).trim(),
|
2021-10-08 21:09:48 +00:00
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<details>
|
|
|
|
<summary>
|
|
|
|
|
|
|
|
this is the **summary**
|
|
|
|
|
|
|
|
</summary>
|
|
|
|
|
|
|
|
\`\`\`javascript
|
|
|
|
var a = 2;
|
|
|
|
var b = 3;
|
|
|
|
var c = a + d;
|
|
|
|
|
|
|
|
console.log(c);
|
|
|
|
\`\`\`
|
|
|
|
|
|
|
|
this content will be _hidden_
|
|
|
|
|
|
|
|
</details>
|
2022-03-03 03:19:02 +00:00
|
|
|
|
2021-10-08 21:09:48 +00:00
|
|
|
<details>
|
|
|
|
<summary>summary 2</summary>
|
|
|
|
content 2
|
|
|
|
</details>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly renders nested details', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
details(
|
|
|
|
detailsContent(paragraph('dream level 1')),
|
|
|
|
detailsContent(
|
|
|
|
details(
|
|
|
|
detailsContent(paragraph('dream level 2')),
|
|
|
|
detailsContent(
|
|
|
|
details(
|
|
|
|
detailsContent(paragraph('dream level 3')),
|
|
|
|
detailsContent(paragraph(italic('inception'))),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2022-03-03 03:19:02 +00:00
|
|
|
).trim(),
|
2021-10-08 21:09:48 +00:00
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<details>
|
|
|
|
<summary>dream level 1</summary>
|
|
|
|
|
|
|
|
<details>
|
|
|
|
<summary>dream level 2</summary>
|
|
|
|
|
|
|
|
<details>
|
|
|
|
<summary>dream level 3</summary>
|
|
|
|
|
|
|
|
_inception_
|
|
|
|
|
|
|
|
</details>
|
2022-03-03 03:19:02 +00:00
|
|
|
|
2021-10-08 21:09:48 +00:00
|
|
|
</details>
|
2022-03-03 03:19:02 +00:00
|
|
|
|
2021-10-08 21:09:48 +00:00
|
|
|
</details>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-08-31 12:11:07 +00:00
|
|
|
it('correctly renders div', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
2022-07-01 21:08:27 +00:00
|
|
|
div(paragraph('just a paragraph in a div')),
|
|
|
|
div(paragraph('just some ', bold('styled'), ' ', italic('content'), ' in a div')),
|
2021-08-31 12:11:07 +00:00
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
'<div>just a paragraph in a div</div>\n<div>\n\njust some **styled** _content_ in a div\n\n</div>',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly renders figure', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
figure(
|
|
|
|
paragraph(image({ src: 'elephant.jpg', alt: 'An elephant at sunset' })),
|
|
|
|
figureCaption('An elephant at sunset'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<figure>
|
|
|
|
|
|
|
|
![An elephant at sunset](elephant.jpg)
|
|
|
|
|
|
|
|
<figcaption>An elephant at sunset</figcaption>
|
|
|
|
</figure>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly renders figure with styled caption', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
figure(
|
|
|
|
paragraph(image({ src: 'elephant.jpg', alt: 'An elephant at sunset' })),
|
|
|
|
figureCaption(italic('An elephant at sunset')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<figure>
|
|
|
|
|
|
|
|
![An elephant at sunset](elephant.jpg)
|
|
|
|
|
|
|
|
<figcaption>
|
|
|
|
|
|
|
|
_An elephant at sunset_
|
|
|
|
|
|
|
|
</figcaption>
|
|
|
|
</figure>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-08-19 21:10:32 +00:00
|
|
|
it('correctly serializes a table with inline content', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
// each table cell must contain at least one paragraph
|
|
|
|
tableRow(
|
|
|
|
tableHeader(paragraph('header')),
|
|
|
|
tableHeader(paragraph('header')),
|
|
|
|
tableHeader(paragraph('header')),
|
|
|
|
),
|
|
|
|
tableRow(
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
),
|
|
|
|
tableRow(
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
| header | header | header |
|
|
|
|
|--------|--------|--------|
|
|
|
|
| cell | cell | cell |
|
|
|
|
| cell | cell | cell |
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a table with line breaks', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(tableHeader(paragraph('header')), tableHeader(paragraph('header'))),
|
|
|
|
tableRow(
|
|
|
|
tableCell(paragraph('cell with', hardBreak(), 'line', hardBreak(), 'breaks')),
|
|
|
|
tableCell(paragraph('cell')),
|
|
|
|
),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
| header | header |
|
|
|
|
|--------|--------|
|
|
|
|
| cell with<br>line<br>breaks | cell |
|
|
|
|
| cell | cell |
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes two consecutive tables', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(tableHeader(paragraph('header')), tableHeader(paragraph('header'))),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
),
|
|
|
|
table(
|
|
|
|
tableRow(tableHeader(paragraph('header')), tableHeader(paragraph('header'))),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
| header | header |
|
|
|
|
|--------|--------|
|
|
|
|
| cell | cell |
|
|
|
|
| cell | cell |
|
|
|
|
|
|
|
|
| header | header |
|
|
|
|
|--------|--------|
|
|
|
|
| cell | cell |
|
|
|
|
| cell | cell |
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes a table with block content', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(
|
|
|
|
tableHeader(paragraph('examples of')),
|
|
|
|
tableHeader(paragraph('block content')),
|
|
|
|
tableHeader(paragraph('in tables')),
|
|
|
|
tableHeader(paragraph('in content editor')),
|
|
|
|
),
|
|
|
|
tableRow(
|
|
|
|
tableCell(heading({ level: 1 }, 'heading 1')),
|
|
|
|
tableCell(heading({ level: 2 }, 'heading 2')),
|
|
|
|
tableCell(paragraph(bold('just bold'))),
|
|
|
|
tableCell(paragraph(bold('bold'), ' ', italic('italic'), ' ', code('code'))),
|
|
|
|
),
|
|
|
|
tableRow(
|
|
|
|
tableCell(
|
|
|
|
paragraph('all marks in three paragraphs:'),
|
|
|
|
paragraph('the ', bold('quick'), ' ', italic('brown'), ' ', code('fox')),
|
|
|
|
paragraph(
|
|
|
|
link({ href: '/home' }, 'jumps'),
|
|
|
|
' over the ',
|
|
|
|
strike('lazy'),
|
|
|
|
' ',
|
|
|
|
emoji({ name: 'dog' }),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
tableCell(
|
|
|
|
paragraph(image({ src: 'img.jpg', alt: 'some image' }), hardBreak(), 'image content'),
|
|
|
|
),
|
|
|
|
tableCell(
|
|
|
|
blockquote('some text', hardBreak(), hardBreak(), 'in a multiline blockquote'),
|
|
|
|
),
|
|
|
|
tableCell(
|
|
|
|
codeBlock(
|
|
|
|
{ language: 'javascript' },
|
|
|
|
'var a = 2;\nvar b = 3;\nvar c = a + d;\n\nconsole.log(c);',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
tableRow(
|
|
|
|
tableCell(bulletList(listItem('item 1'), listItem('item 2'), listItem('item 2'))),
|
|
|
|
tableCell(orderedList(listItem('item 1'), listItem('item 2'), listItem('item 2'))),
|
|
|
|
tableCell(
|
|
|
|
paragraph('paragraphs separated by'),
|
|
|
|
horizontalRule(),
|
|
|
|
paragraph('a horizontal rule'),
|
|
|
|
),
|
|
|
|
tableCell(
|
|
|
|
table(
|
|
|
|
tableRow(tableHeader(paragraph('table')), tableHeader(paragraph('inside'))),
|
|
|
|
tableRow(tableCell(paragraph('another')), tableCell(paragraph('table'))),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>examples of</th>
|
|
|
|
<th>block content</th>
|
|
|
|
<th>in tables</th>
|
|
|
|
<th>in content editor</th>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
# heading 1
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
## heading 2
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
**just bold**
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
**bold** _italic_ \`code\`
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
all marks in three paragraphs:
|
|
|
|
|
|
|
|
the **quick** _brown_ \`fox\`
|
|
|
|
|
|
|
|
[jumps](/home) over the ~~lazy~~ :dog:
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
![some image](img.jpg)<br>image content
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
> some text\\
|
|
|
|
> \\
|
|
|
|
> in a multiline blockquote
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
\`\`\`javascript
|
|
|
|
var a = 2;
|
|
|
|
var b = 3;
|
|
|
|
var c = a + d;
|
|
|
|
|
|
|
|
console.log(c);
|
|
|
|
\`\`\`
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
* item 1
|
|
|
|
* item 2
|
|
|
|
* item 2
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
1. item 1
|
|
|
|
2. item 2
|
|
|
|
3. item 2
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
paragraphs separated by
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
a horizontal rule
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
| table | inside |
|
|
|
|
|-------|--------|
|
|
|
|
| another | table |
|
|
|
|
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly renders content after a markdown table', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(tableRow(tableHeader(paragraph('header'))), tableRow(tableCell(paragraph('cell')))),
|
|
|
|
heading({ level: 1 }, 'this is a heading'),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
| header |
|
|
|
|
|--------|
|
|
|
|
| cell |
|
|
|
|
|
|
|
|
# this is a heading
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly renders content after an html table', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(tableHeader(paragraph('header'))),
|
|
|
|
tableRow(tableCell(blockquote('hi'), paragraph('there'))),
|
|
|
|
),
|
|
|
|
heading({ level: 1 }, 'this is a heading'),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>header</th>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
|
|
|
|
> hi
|
|
|
|
|
|
|
|
there
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
|
|
|
# this is a heading
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes tables with misplaced header cells', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(tableHeader(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableHeader(paragraph('cell'))),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>cell</th>
|
|
|
|
<td>cell</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>cell</td>
|
|
|
|
<th>cell</th>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes table without any headers', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
tableRow(tableCell(paragraph('cell')), tableCell(paragraph('cell'))),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<td>cell</td>
|
|
|
|
<td>cell</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>cell</td>
|
|
|
|
<td>cell</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly serializes table with rowspan and colspan', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
table(
|
|
|
|
tableRow(
|
|
|
|
tableHeader(paragraph('header')),
|
|
|
|
tableHeader(paragraph('header')),
|
|
|
|
tableHeader(paragraph('header')),
|
|
|
|
),
|
|
|
|
tableRow(
|
|
|
|
tableCell({ colspan: 2 }, paragraph('cell with rowspan: 2')),
|
|
|
|
tableCell({ rowspan: 2 }, paragraph('cell')),
|
|
|
|
),
|
|
|
|
tableRow(tableCell({ colspan: 2 }, paragraph('cell with rowspan: 2'))),
|
|
|
|
),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>header</th>
|
|
|
|
<th>header</th>
|
|
|
|
<th>header</th>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="2">cell with rowspan: 2</td>
|
|
|
|
<td rowspan="2">cell</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="2">cell with rowspan: 2</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
2021-12-02 00:17:32 +00:00
|
|
|
|
|
|
|
it('correctly serializes footnotes', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
2022-06-08 15:08:15 +00:00
|
|
|
paragraph('Oranges are orange ', footnoteReference({ label: '1', identifier: '1' })),
|
|
|
|
footnoteDefinition({ label: '1', identifier: '1' }, 'Oranges are fruits'),
|
2021-12-02 00:17:32 +00:00
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
Oranges are orange [^1]
|
|
|
|
|
|
|
|
[^1]: Oranges are fruits
|
2022-06-08 15:08:15 +00:00
|
|
|
`.trimLeft(),
|
2021-12-02 00:17:32 +00:00
|
|
|
);
|
|
|
|
});
|
2022-05-11 15:07:26 +00:00
|
|
|
|
2022-10-04 15:09:33 +00:00
|
|
|
it('correctly adds a space between a preceding block element and a markdown table', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
bulletList(listItem(paragraph('List item 1')), listItem(paragraph('List item 2'))),
|
|
|
|
table(tableRow(tableHeader(paragraph('header'))), tableRow(tableCell(paragraph('cell')))),
|
|
|
|
).trim(),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
* List item 1
|
|
|
|
* List item 2
|
|
|
|
|
|
|
|
| header |
|
|
|
|
|--------|
|
|
|
|
| cell |
|
|
|
|
`.trim(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-07-29 15:12:25 +00:00
|
|
|
it('correctly serializes reference definition', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
referenceDefinition('[gitlab]: https://gitlab.com'),
|
|
|
|
referenceDefinition('[foobar]: foobar.com'),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
[gitlab]: https://gitlab.com
|
|
|
|
[foobar]: foobar.com`.trimLeft(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly adds a space between a reference definition and a block content', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph('paragraph'),
|
|
|
|
referenceDefinition('[gitlab]: https://gitlab.com'),
|
|
|
|
referenceDefinition('[foobar]: foobar.com'),
|
|
|
|
heading({ level: 2 }, 'heading'),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
paragraph
|
|
|
|
|
|
|
|
[gitlab]: https://gitlab.com
|
|
|
|
[foobar]: foobar.com
|
|
|
|
|
|
|
|
## heading`.trimLeft(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-08-23 00:09:41 +00:00
|
|
|
it('serializes audio and video elements', () => {
|
|
|
|
expect(
|
|
|
|
serialize(
|
|
|
|
paragraph(
|
|
|
|
audio({ alt: 'audio', canonicalSrc: 'audio.mp3' }),
|
|
|
|
' and ',
|
|
|
|
video({ alt: 'video', canonicalSrc: 'video.mov' }),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
).toBe(
|
|
|
|
`
|
|
|
|
![audio](audio.mp3) and ![video](video.mov)`.trimLeft(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-06-14 06:09:22 +00:00
|
|
|
const defaultEditAction = (initialContent) => {
|
|
|
|
tiptapEditor.chain().setContent(initialContent.toJSON()).insertContent(' modified').run();
|
|
|
|
};
|
|
|
|
|
|
|
|
const prependContentEditAction = (initialContent) => {
|
|
|
|
tiptapEditor
|
|
|
|
.chain()
|
|
|
|
.setContent(initialContent.toJSON())
|
|
|
|
.setTextSelection(0)
|
|
|
|
.insertContent('modified ')
|
|
|
|
.run();
|
|
|
|
};
|
|
|
|
|
2022-05-11 15:07:26 +00:00
|
|
|
it.each`
|
2022-08-12 15:11:42 +00:00
|
|
|
mark | markdown | modifiedMarkdown | editAction
|
|
|
|
${'bold'} | ${'**bold**'} | ${'**bold modified**'} | ${defaultEditAction}
|
|
|
|
${'bold'} | ${'__bold__'} | ${'__bold modified__'} | ${defaultEditAction}
|
|
|
|
${'bold'} | ${'<strong>bold</strong>'} | ${'<strong>bold modified</strong>'} | ${defaultEditAction}
|
|
|
|
${'bold'} | ${'<b>bold</b>'} | ${'<b>bold modified</b>'} | ${defaultEditAction}
|
|
|
|
${'italic'} | ${'_italic_'} | ${'_italic modified_'} | ${defaultEditAction}
|
|
|
|
${'italic'} | ${'*italic*'} | ${'*italic modified*'} | ${defaultEditAction}
|
|
|
|
${'italic'} | ${'<em>italic</em>'} | ${'<em>italic modified</em>'} | ${defaultEditAction}
|
|
|
|
${'italic'} | ${'<i>italic</i>'} | ${'<i>italic modified</i>'} | ${defaultEditAction}
|
|
|
|
${'link'} | ${'[gitlab](https://gitlab.com)'} | ${'[gitlab modified](https://gitlab.com)'} | ${defaultEditAction}
|
|
|
|
${'link'} | ${'<a href="https://gitlab.com">link</a>'} | ${'<a href="https://gitlab.com">link modified</a>'} | ${defaultEditAction}
|
|
|
|
${'link'} | ${'link www.gitlab.com'} | ${'modified link www.gitlab.com'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com'} | ${'modified link https://www.gitlab.com'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link(https://www.gitlab.com)'} | ${'modified link(https://www.gitlab.com)'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link(engineering@gitlab.com)'} | ${'modified link(engineering@gitlab.com)'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link <https://www.gitlab.com>'} | ${'modified link <https://www.gitlab.com>'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link [https://www.gitlab.com>'} | ${'modified link \\[https://www.gitlab.com>'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link <https://www.gitlab.com'} | ${'modified link <https://www.gitlab.com'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com>'} | ${'modified link https://www.gitlab.com>'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com/path'} | ${'modified link https://www.gitlab.com/path'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com?query=search'} | ${'modified link https://www.gitlab.com?query=search'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com/#fragment'} | ${'modified link https://www.gitlab.com/#fragment'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com/?query=search'} | ${'modified link https://www.gitlab.com/?query=search'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link https://www.gitlab.com#fragment'} | ${'modified link https://www.gitlab.com#fragment'} | ${prependContentEditAction}
|
|
|
|
${'link'} | ${'link **https://www.gitlab.com]**'} | ${'modified link **https://www.gitlab.com\\]**'} | ${prependContentEditAction}
|
|
|
|
${'code'} | ${'`code`'} | ${'`code modified`'} | ${defaultEditAction}
|
|
|
|
${'code'} | ${'<code>code</code>'} | ${'<code>code modified</code>'} | ${defaultEditAction}
|
|
|
|
${'strike'} | ${'~~striked~~'} | ${'~~striked modified~~'} | ${defaultEditAction}
|
|
|
|
${'strike'} | ${'<del>striked</del>'} | ${'<del>striked modified</del>'} | ${defaultEditAction}
|
|
|
|
${'strike'} | ${'<strike>striked</strike>'} | ${'<strike>striked modified</strike>'} | ${defaultEditAction}
|
|
|
|
${'strike'} | ${'<s>striked</s>'} | ${'<s>striked modified</s>'} | ${defaultEditAction}
|
|
|
|
${'list'} | ${'- list item'} | ${'- list item modified'} | ${defaultEditAction}
|
|
|
|
${'list'} | ${'* list item'} | ${'* list item modified'} | ${defaultEditAction}
|
|
|
|
${'list'} | ${'+ list item'} | ${'+ list item modified'} | ${defaultEditAction}
|
|
|
|
${'list'} | ${'- list item 1\n- list item 2'} | ${'- list item 1\n- list item 2 modified'} | ${defaultEditAction}
|
|
|
|
${'list'} | ${'2) list item'} | ${'2) list item modified'} | ${defaultEditAction}
|
|
|
|
${'list'} | ${'1. list item'} | ${'1. list item modified'} | ${defaultEditAction}
|
|
|
|
${'taskList'} | ${'2) [ ] task list item'} | ${'2) [ ] task list item modified'} | ${defaultEditAction}
|
|
|
|
${'taskList'} | ${'2) [x] task list item'} | ${'2) [x] task list item modified'} | ${defaultEditAction}
|
|
|
|
${'image'} | ${'![image](image.png)'} | ${'![image](image.png) modified'} | ${defaultEditAction}
|
|
|
|
${'footnoteReference'} | ${'[^1] footnote\n\n[^1]: footnote definition'} | ${'modified [^1] footnote\n\n[^1]: footnote definition'} | ${prependContentEditAction}
|
2022-05-11 15:07:26 +00:00
|
|
|
`(
|
2022-07-29 18:08:58 +00:00
|
|
|
'preserves original $mark syntax when sourceMarkdown is available for $markdown',
|
2022-07-14 12:08:33 +00:00
|
|
|
async ({ markdown, modifiedMarkdown, editAction }) => {
|
2022-05-11 15:07:26 +00:00
|
|
|
const { document } = await remarkMarkdownDeserializer().deserialize({
|
|
|
|
schema: tiptapEditor.schema,
|
2022-07-14 12:08:33 +00:00
|
|
|
markdown,
|
2022-05-11 15:07:26 +00:00
|
|
|
});
|
|
|
|
|
2022-06-14 06:09:22 +00:00
|
|
|
editAction(document);
|
2022-05-11 15:07:26 +00:00
|
|
|
|
|
|
|
const serialized = markdownSerializer({}).serialize({
|
|
|
|
pristineDoc: document,
|
|
|
|
doc: tiptapEditor.state.doc,
|
|
|
|
});
|
|
|
|
|
2022-07-14 12:08:33 +00:00
|
|
|
expect(serialized).toEqual(modifiedMarkdown);
|
2022-05-11 15:07:26 +00:00
|
|
|
},
|
|
|
|
);
|
2021-08-19 21:10:32 +00:00
|
|
|
});
|