debian-mirror-gitlab/spec/frontend/content_editor/remark_markdown_processing_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1454 lines
42 KiB
JavaScript
Raw Normal View History

2022-10-11 01:57:18 +05:30
import Audio from '~/content_editor/extensions/audio';
2022-07-16 23:28:13 +05:30
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';
2022-10-11 01:57:18 +05:30
import Diagram from '~/content_editor/extensions/diagram';
2022-07-23 23:45:48 +05:30
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
2022-08-27 11:52:29 +05:30
import Frontmatter from '~/content_editor/extensions/frontmatter';
2022-07-16 23:28:13 +05:30
import HardBreak from '~/content_editor/extensions/hard_break';
2022-08-13 15:12:31 +05:30
import HTMLNodes from '~/content_editor/extensions/html_nodes';
2022-07-16 23:28:13 +05:30
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';
2022-07-23 23:45:48 +05:30
import Paragraph from '~/content_editor/extensions/paragraph';
2022-08-27 11:52:29 +05:30
import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
2022-07-16 23:28:13 +05:30
import Sourcemap from '~/content_editor/extensions/sourcemap';
2022-07-23 23:45:48 +05:30
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
import TableHeader from '~/content_editor/extensions/table_header';
2022-10-11 01:57:18 +05:30
import TableOfContents from '~/content_editor/extensions/table_of_contents';
2022-07-23 23:45:48 +05:30
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';
2022-10-11 01:57:18 +05:30
import Video from '~/content_editor/extensions/video';
2022-07-16 23:28:13 +05:30
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import markdownSerializer from '~/content_editor/services/markdown_serializer';
2022-10-11 01:57:18 +05:30
import { SAFE_VIDEO_EXT, SAFE_AUDIO_EXT, DIAGRAM_LANGUAGES } from '~/content_editor/constants';
2022-07-16 23:28:13 +05:30
2022-07-23 23:45:48 +05:30
import { createTestEditor, createDocBuilder } from './test_utils';
2022-07-16 23:28:13 +05:30
const tiptapEditor = createTestEditor({
extensions: [
2022-10-11 01:57:18 +05:30
Audio,
2022-07-16 23:28:13 +05:30
Blockquote,
Bold,
BulletList,
Code,
CodeBlockHighlight,
2022-10-11 01:57:18 +05:30
Diagram,
2022-07-23 23:45:48 +05:30
FootnoteDefinition,
FootnoteReference,
2022-08-27 11:52:29 +05:30
Frontmatter,
2022-07-16 23:28:13 +05:30
HardBreak,
Heading,
HorizontalRule,
Image,
Italic,
Link,
ListItem,
OrderedList,
2022-08-27 11:52:29 +05:30
ReferenceDefinition,
2022-07-16 23:28:13 +05:30
Sourcemap,
2022-07-23 23:45:48 +05:30
Strike,
Table,
TableRow,
TableHeader,
TableCell,
2022-10-11 01:57:18 +05:30
TableOfContents,
2022-07-23 23:45:48 +05:30
TaskList,
TaskItem,
2022-10-11 01:57:18 +05:30
Video,
2022-08-13 15:12:31 +05:30
...HTMLNodes,
2022-07-16 23:28:13 +05:30
],
});
2022-07-23 23:45:48 +05:30
const {
builders: {
doc,
paragraph,
2022-10-11 01:57:18 +05:30
audio,
2022-07-23 23:45:48 +05:30
bold,
blockquote,
bulletList,
code,
codeBlock,
2022-08-13 15:12:31 +05:30
div,
2022-10-11 01:57:18 +05:30
diagram,
2022-07-23 23:45:48 +05:30
footnoteDefinition,
footnoteReference,
2022-08-27 11:52:29 +05:30
frontmatter,
2022-07-23 23:45:48 +05:30
hardBreak,
heading,
horizontalRule,
image,
italic,
link,
listItem,
orderedList,
2022-08-13 15:12:31 +05:30
pre,
2022-08-27 11:52:29 +05:30
referenceDefinition,
2022-07-23 23:45:48 +05:30
strike,
table,
tableRow,
tableHeader,
tableCell,
2022-10-11 01:57:18 +05:30
tableOfContents,
2022-07-23 23:45:48 +05:30
taskItem,
taskList,
2022-10-11 01:57:18 +05:30
video,
2022-07-23 23:45:48 +05:30
},
} = createDocBuilder({
tiptapEditor,
names: {
2022-10-11 01:57:18 +05:30
audio: { nodeType: Audio.name },
2022-07-23 23:45:48 +05:30
blockquote: { nodeType: Blockquote.name },
bold: { markType: Bold.name },
bulletList: { nodeType: BulletList.name },
code: { markType: Code.name },
codeBlock: { nodeType: CodeBlockHighlight.name },
2022-10-11 01:57:18 +05:30
diagram: { nodeType: Diagram.name },
2022-07-23 23:45:48 +05:30
footnoteDefinition: { nodeType: FootnoteDefinition.name },
footnoteReference: { nodeType: FootnoteReference.name },
2022-08-27 11:52:29 +05:30
frontmatter: { nodeType: Frontmatter.name },
2022-07-23 23:45:48 +05:30
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 },
2022-08-27 11:52:29 +05:30
referenceDefinition: { nodeType: ReferenceDefinition.name },
2022-07-23 23:45:48 +05:30
strike: { nodeType: Strike.name },
table: { nodeType: Table.name },
tableCell: { nodeType: TableCell.name },
tableHeader: { nodeType: TableHeader.name },
tableRow: { nodeType: TableRow.name },
2022-10-11 01:57:18 +05:30
tableOfContents: { nodeType: TableOfContents.name },
2022-07-23 23:45:48 +05:30
taskItem: { nodeType: TaskItem.name },
taskList: { nodeType: TaskList.name },
2022-10-11 01:57:18 +05:30
video: { nodeType: Video.name },
2022-08-13 15:12:31 +05:30
...HTMLNodes.reduce(
(builders, htmlNode) => ({
...builders,
[htmlNode.name]: { nodeType: htmlNode.name },
}),
{},
),
2022-07-23 23:45:48 +05:30
},
});
2022-07-16 23:28:13 +05:30
describe('Client side Markdown processing', () => {
2022-08-13 15:12:31 +05:30
const deserialize = async (markdown) => {
2022-07-16 23:28:13 +05:30
const { document } = await remarkMarkdownDeserializer().deserialize({
schema: tiptapEditor.schema,
2022-08-13 15:12:31 +05:30
markdown,
2022-07-16 23:28:13 +05:30
});
return document;
};
const serialize = (document) =>
markdownSerializer({}).serialize({
doc: document,
pristineDoc: document,
});
2022-08-13 15:12:31 +05:30
const source = (sourceMarkdown) => ({
sourceMapKey: expect.any(String),
2022-07-23 23:45:48 +05:30
sourceMarkdown,
});
const examples = [
2022-07-16 23:28:13 +05:30
{
markdown: '__bold text__',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('__bold text__'), bold(source('__bold text__'), 'bold text')),
2022-07-23 23:45:48 +05:30
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '**bold text**',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('**bold text**'), bold(source('**bold text**'), 'bold text')),
2022-07-23 23:45:48 +05:30
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '<strong>bold text</strong>',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<strong>bold text</strong>'),
bold(source('<strong>bold text</strong>'), 'bold text'),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '<b>bold text</b>',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('<b>bold text</b>'), bold(source('<b>bold text</b>'), 'bold text')),
2022-07-23 23:45:48 +05:30
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '_italic text_',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('_italic text_'), italic(source('_italic text_'), 'italic text')),
2022-07-23 23:45:48 +05:30
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '*italic text*',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('*italic text*'), italic(source('*italic text*'), 'italic text')),
2022-07-23 23:45:48 +05:30
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '<em>italic text</em>',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<em>italic text</em>'),
italic(source('<em>italic text</em>'), 'italic text'),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '<i>italic text</i>',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<i>italic text</i>'),
italic(source('<i>italic text</i>'), 'italic text'),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '`inline code`',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('`inline code`'), code(source('`inline code`'), 'inline code')),
2022-07-23 23:45:48 +05:30
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '**`inline code bold`**',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('**`inline code bold`**'),
2022-07-23 23:45:48 +05:30
bold(
2022-08-13 15:12:31 +05:30
source('**`inline code bold`**'),
code(source('`inline code bold`'), 'inline code bold'),
2022-07-23 23:45:48 +05:30
),
),
),
},
{
markdown: '_`inline code italics`_',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('_`inline code italics`_'),
2022-07-23 23:45:48 +05:30
italic(
2022-08-13 15:12:31 +05:30
source('_`inline code italics`_'),
code(source('`inline code italics`'), 'inline code italics'),
2022-07-23 23:45:48 +05:30
),
),
),
},
{
markdown: `
<i class="foo">
*bar*
</i>
`,
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<i class="foo">\n *bar*\n</i>'),
italic(source('<i class="foo">\n *bar*\n</i>'), '\n *bar*\n'),
2022-07-23 23:45:48 +05:30
),
),
},
{
markdown: `
<img src="bar" alt="foo" />
`,
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<img src="bar" alt="foo" />'),
2022-08-27 11:52:29 +05:30
image({
...source('<img src="bar" alt="foo" />'),
alt: 'foo',
canonicalSrc: 'bar',
src: 'bar',
}),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
2022-07-23 23:45:48 +05:30
markdown: `
- List item 1
<img src="bar" alt="foo" />
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('- List item 1'),
listItem(source('- List item 1'), paragraph(source('List item 1'), 'List item 1')),
2022-07-23 23:45:48 +05:30
),
paragraph(
2022-08-13 15:12:31 +05:30
source('<img src="bar" alt="foo" />'),
2022-08-27 11:52:29 +05:30
image({
...source('<img src="bar" alt="foo" />'),
alt: 'foo',
src: 'bar',
canonicalSrc: 'bar',
}),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '[GitLab](https://gitlab.com "Go to GitLab")',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('[GitLab](https://gitlab.com "Go to GitLab")'),
2022-07-23 23:45:48 +05:30
link(
{
2022-08-13 15:12:31 +05:30
...source('[GitLab](https://gitlab.com "Go to GitLab")'),
2022-07-23 23:45:48 +05:30
href: 'https://gitlab.com',
2022-08-27 11:52:29 +05:30
canonicalSrc: 'https://gitlab.com',
2022-07-23 23:45:48 +05:30
title: 'Go to GitLab',
},
'GitLab',
),
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '**[GitLab](https://gitlab.com "Go to GitLab")**',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('**[GitLab](https://gitlab.com "Go to GitLab")**'),
2022-07-23 23:45:48 +05:30
bold(
2022-08-13 15:12:31 +05:30
source('**[GitLab](https://gitlab.com "Go to GitLab")**'),
2022-07-23 23:45:48 +05:30
link(
{
2022-08-13 15:12:31 +05:30
...source('[GitLab](https://gitlab.com "Go to GitLab")'),
2022-07-23 23:45:48 +05:30
href: 'https://gitlab.com',
2022-08-27 11:52:29 +05:30
canonicalSrc: 'https://gitlab.com',
2022-07-23 23:45:48 +05:30
title: 'Go to GitLab',
},
'GitLab',
),
),
),
),
},
{
markdown: 'www.commonmark.org',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('www.commonmark.org'),
2022-07-23 23:45:48 +05:30
link(
{
2022-08-13 15:12:31 +05:30
...source('www.commonmark.org'),
2022-08-27 11:52:29 +05:30
canonicalSrc: 'http://www.commonmark.org',
2022-07-23 23:45:48 +05:30
href: 'http://www.commonmark.org',
},
'www.commonmark.org',
),
),
),
},
{
markdown: 'Visit www.commonmark.org/help for more information.',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('Visit www.commonmark.org/help for more information.'),
2022-07-23 23:45:48 +05:30
'Visit ',
link(
{
2022-08-13 15:12:31 +05:30
...source('www.commonmark.org/help'),
2022-08-27 11:52:29 +05:30
canonicalSrc: 'http://www.commonmark.org/help',
2022-07-23 23:45:48 +05:30
href: 'http://www.commonmark.org/help',
},
'www.commonmark.org/help',
),
' for more information.',
),
),
},
{
markdown: 'hello@mail+xyz.example isnt valid, but hello+xyz@mail.example is.',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('hello@mail+xyz.example isnt valid, but hello+xyz@mail.example is.'),
2022-07-23 23:45:48 +05:30
'hello@mail+xyz.example isnt valid, but ',
link(
{
2022-08-13 15:12:31 +05:30
...source('hello+xyz@mail.example'),
2022-08-27 11:52:29 +05:30
canonicalSrc: 'mailto:hello+xyz@mail.example',
2022-07-23 23:45:48 +05:30
href: 'mailto:hello+xyz@mail.example',
},
'hello+xyz@mail.example',
),
' is.',
),
),
},
{
markdown: '[https://gitlab.com>',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('[https://gitlab.com>'),
2022-07-23 23:45:48 +05:30
'[',
link(
{
2022-08-13 15:12:31 +05:30
sourceMapKey: null,
sourceMarkdown: null,
2022-08-27 11:52:29 +05:30
canonicalSrc: 'https://gitlab.com',
2022-07-23 23:45:48 +05:30
href: 'https://gitlab.com',
},
'https://gitlab.com',
),
'>',
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
This is a paragraph with a\\
hard line break`,
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('This is a paragraph with a\\\nhard line break'),
2022-07-23 23:45:48 +05:30
'This is a paragraph with a',
2022-08-13 15:12:31 +05:30
hardBreak(source('\\\n')),
2022-07-23 23:45:48 +05:30
'\nhard line break',
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '![GitLab Logo](https://gitlab.com/logo.png "GitLab Logo")',
2022-07-23 23:45:48 +05:30
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('![GitLab Logo](https://gitlab.com/logo.png "GitLab Logo")'),
2022-07-23 23:45:48 +05:30
image({
2022-08-13 15:12:31 +05:30
...source('![GitLab Logo](https://gitlab.com/logo.png "GitLab Logo")'),
2022-07-23 23:45:48 +05:30
alt: 'GitLab Logo',
2022-08-27 11:52:29 +05:30
canonicalSrc: 'https://gitlab.com/logo.png',
2022-07-23 23:45:48 +05:30
src: 'https://gitlab.com/logo.png',
title: 'GitLab Logo',
}),
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: '---',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(horizontalRule(source('---'))),
2022-07-16 23:28:13 +05:30
},
{
markdown: '***',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(horizontalRule(source('***'))),
2022-07-16 23:28:13 +05:30
},
{
markdown: '___',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(horizontalRule(source('___'))),
2022-07-16 23:28:13 +05:30
},
{
markdown: '<hr>',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(horizontalRule(source('<hr>'))),
2022-07-16 23:28:13 +05:30
},
{
markdown: '# Heading 1',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('# Heading 1'), level: 1 }, 'Heading 1')),
2022-07-16 23:28:13 +05:30
},
{
markdown: '## Heading 2',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('## Heading 2'), level: 2 }, 'Heading 2')),
2022-07-16 23:28:13 +05:30
},
{
markdown: '### Heading 3',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('### Heading 3'), level: 3 }, 'Heading 3')),
2022-07-16 23:28:13 +05:30
},
{
markdown: '#### Heading 4',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('#### Heading 4'), level: 4 }, 'Heading 4')),
2022-07-16 23:28:13 +05:30
},
{
markdown: '##### Heading 5',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('##### Heading 5'), level: 5 }, 'Heading 5')),
2022-07-16 23:28:13 +05:30
},
{
markdown: '###### Heading 6',
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('###### Heading 6'), level: 6 }, 'Heading 6')),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
Heading
one
======
`,
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('Heading\none\n======'), level: 1 }, 'Heading\none')),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
Heading
two
-------
`,
2022-08-13 15:12:31 +05:30
expectedDoc: doc(heading({ ...source('Heading\ntwo\n-------'), level: 2 }, 'Heading\ntwo')),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
- List item 1
- List item 2
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('- List item 1\n- List item 2'),
listItem(source('- List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('- List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
* List item 1
* List item 2
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('* List item 1\n* List item 2'),
listItem(source('* List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('* List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
+ List item 1
+ List item 2
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('+ List item 1\n+ List item 2'),
listItem(source('+ List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('+ List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
1. List item 1
1. List item 2
`,
expectedDoc: doc(
orderedList(
2022-08-13 15:12:31 +05:30
source('1. List item 1\n1. List item 2'),
listItem(source('1. List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('1. List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
1. List item 1
2. List item 2
`,
expectedDoc: doc(
orderedList(
2022-08-13 15:12:31 +05:30
source('1. List item 1\n2. List item 2'),
listItem(source('1. List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('2. List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
1) List item 1
2) List item 2
`,
expectedDoc: doc(
orderedList(
2022-08-13 15:12:31 +05:30
source('1) List item 1\n2) List item 2'),
listItem(source('1) List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('2) List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
- List item 1
- Sub list item 1
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('- List item 1\n - Sub list item 1'),
2022-07-23 23:45:48 +05:30
listItem(
2022-08-13 15:12:31 +05:30
source('- List item 1\n - Sub list item 1'),
paragraph(source('List item 1'), 'List item 1'),
2022-07-23 23:45:48 +05:30
bulletList(
2022-08-13 15:12:31 +05:30
source('- Sub list item 1'),
2022-07-23 23:45:48 +05:30
listItem(
2022-08-13 15:12:31 +05:30
source('- Sub list item 1'),
paragraph(source('Sub list item 1'), 'Sub list item 1'),
2022-07-23 23:45:48 +05:30
),
),
),
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
- List item 1 paragraph 1
2022-07-16 23:28:13 +05:30
2022-07-23 23:45:48 +05:30
List item 1 paragraph 2
- List item 2
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('- List item 1 paragraph 1\n\n List item 1 paragraph 2\n- List item 2'),
2022-07-23 23:45:48 +05:30
listItem(
2022-08-13 15:12:31 +05:30
source('- List item 1 paragraph 1\n\n List item 1 paragraph 2'),
paragraph(source('List item 1 paragraph 1'), 'List item 1 paragraph 1'),
paragraph(source('List item 1 paragraph 2'), 'List item 1 paragraph 2'),
2022-07-23 23:45:48 +05:30
),
2022-08-13 15:12:31 +05:30
listItem(source('- List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
- List item with an image ![bar](foo.png)
`,
expectedDoc: doc(
bulletList(
2022-08-13 15:12:31 +05:30
source('- List item with an image ![bar](foo.png)'),
2022-07-23 23:45:48 +05:30
listItem(
2022-08-13 15:12:31 +05:30
source('- List item with an image ![bar](foo.png)'),
2022-07-23 23:45:48 +05:30
paragraph(
2022-08-13 15:12:31 +05:30
source('List item with an image ![bar](foo.png)'),
2022-07-23 23:45:48 +05:30
'List item with an image',
2022-08-27 11:52:29 +05:30
image({
...source('![bar](foo.png)'),
alt: 'bar',
canonicalSrc: 'foo.png',
src: 'foo.png',
}),
2022-07-23 23:45:48 +05:30
),
),
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
> This is a blockquote
`,
expectedDoc: doc(
blockquote(
2022-08-13 15:12:31 +05:30
source('> This is a blockquote'),
paragraph(source('This is a blockquote'), 'This is a blockquote'),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
> - List item 1
> - List item 2
`,
expectedDoc: doc(
blockquote(
2022-08-13 15:12:31 +05:30
source('> - List item 1\n> - List item 2'),
2022-07-23 23:45:48 +05:30
bulletList(
2022-08-13 15:12:31 +05:30
source('- List item 1\n> - List item 2'),
listItem(source('- List item 1'), paragraph(source('List item 1'), 'List item 1')),
listItem(source('- List item 2'), paragraph(source('List item 2'), 'List item 2')),
2022-07-23 23:45:48 +05:30
),
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
code block
const fn = () => 'GitLab';
`,
expectedDoc: doc(
2022-08-13 15:12:31 +05:30
paragraph(source('code block'), 'code block'),
2022-07-23 23:45:48 +05:30
codeBlock(
{
2022-08-13 15:12:31 +05:30
...source(" const fn = () => 'GitLab';"),
2022-07-23 23:45:48 +05:30
class: 'code highlight',
language: null,
},
"const fn = () => 'GitLab';",
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
\`\`\`javascript
const fn = () => 'GitLab';
\`\`\`\
`,
expectedDoc: doc(
codeBlock(
{
2022-08-13 15:12:31 +05:30
...source("```javascript\nconst fn = () => 'GitLab';\n```"),
2022-07-23 23:45:48 +05:30
class: 'code highlight',
language: 'javascript',
},
"const fn = () => 'GitLab';",
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
~~~javascript
const fn = () => 'GitLab';
~~~
`,
expectedDoc: doc(
codeBlock(
{
2022-08-13 15:12:31 +05:30
...source("~~~javascript\nconst fn = () => 'GitLab';\n~~~"),
2022-07-23 23:45:48 +05:30
class: 'code highlight',
language: 'javascript',
},
"const fn = () => 'GitLab';",
),
),
2022-07-16 23:28:13 +05:30
},
{
markdown: `
2022-07-23 23:45:48 +05:30
\`\`\`
\`\`\`\
`,
expectedDoc: doc(
codeBlock(
{
2022-08-13 15:12:31 +05:30
...source('```\n```'),
2022-07-23 23:45:48 +05:30
class: 'code highlight',
language: null,
},
'',
),
),
},
{
markdown: `
\`\`\`javascript
const fn = () => 'GitLab';
2022-07-16 23:28:13 +05:30
2022-07-23 23:45:48 +05:30
\`\`\`\
`,
expectedDoc: doc(
codeBlock(
{
2022-08-13 15:12:31 +05:30
...source("```javascript\nconst fn = () => 'GitLab';\n\n```"),
2022-07-23 23:45:48 +05:30
class: 'code highlight',
language: 'javascript',
},
"const fn = () => 'GitLab';\n",
),
),
},
{
markdown: '~~Strikedthrough text~~',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('~~Strikedthrough text~~'),
strike(source('~~Strikedthrough text~~'), 'Strikedthrough text'),
2022-07-23 23:45:48 +05:30
),
),
},
{
markdown: '<del>Strikedthrough text</del>',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<del>Strikedthrough text</del>'),
strike(source('<del>Strikedthrough text</del>'), 'Strikedthrough text'),
2022-07-23 23:45:48 +05:30
),
),
},
{
markdown: '<strike>Strikedthrough text</strike>',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<strike>Strikedthrough text</strike>'),
strike(source('<strike>Strikedthrough text</strike>'), 'Strikedthrough text'),
2022-07-23 23:45:48 +05:30
),
),
},
{
markdown: '<s>Strikedthrough text</s>',
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('<s>Strikedthrough text</s>'),
strike(source('<s>Strikedthrough text</s>'), 'Strikedthrough text'),
2022-07-23 23:45:48 +05:30
),
),
2022-07-16 23:28:13 +05:30
},
2022-07-23 23:45:48 +05:30
{
markdown: `
- [ ] task list item 1
- [ ] task list item 2
`,
expectedDoc: doc(
taskList(
{
numeric: false,
2022-08-13 15:12:31 +05:30
...source('- [ ] task list item 1\n- [ ] task list item 2'),
2022-07-23 23:45:48 +05:30
},
taskItem(
{
checked: false,
2022-08-13 15:12:31 +05:30
...source('- [ ] task list item 1'),
2022-07-23 23:45:48 +05:30
},
2022-08-13 15:12:31 +05:30
paragraph(source('task list item 1'), 'task list item 1'),
2022-07-23 23:45:48 +05:30
),
taskItem(
{
checked: false,
2022-08-13 15:12:31 +05:30
...source('- [ ] task list item 2'),
2022-07-23 23:45:48 +05:30
},
2022-08-13 15:12:31 +05:30
paragraph(source('task list item 2'), 'task list item 2'),
2022-07-23 23:45:48 +05:30
),
),
),
},
{
markdown: `
- [x] task list item 1
- [x] task list item 2
`,
expectedDoc: doc(
taskList(
{
numeric: false,
2022-08-13 15:12:31 +05:30
...source('- [x] task list item 1\n- [x] task list item 2'),
2022-07-23 23:45:48 +05:30
},
taskItem(
{
checked: true,
2022-08-13 15:12:31 +05:30
...source('- [x] task list item 1'),
2022-07-23 23:45:48 +05:30
},
2022-08-13 15:12:31 +05:30
paragraph(source('task list item 1'), 'task list item 1'),
2022-07-23 23:45:48 +05:30
),
taskItem(
{
checked: true,
2022-08-13 15:12:31 +05:30
...source('- [x] task list item 2'),
2022-07-23 23:45:48 +05:30
},
2022-08-13 15:12:31 +05:30
paragraph(source('task list item 2'), 'task list item 2'),
2022-07-23 23:45:48 +05:30
),
),
),
},
{
markdown: `
1. [ ] task list item 1
2. [ ] task list item 2
`,
expectedDoc: doc(
taskList(
{
numeric: true,
2022-08-13 15:12:31 +05:30
...source('1. [ ] task list item 1\n2. [ ] task list item 2'),
2022-07-23 23:45:48 +05:30
},
taskItem(
{
checked: false,
2022-08-13 15:12:31 +05:30
...source('1. [ ] task list item 1'),
2022-07-23 23:45:48 +05:30
},
2022-08-13 15:12:31 +05:30
paragraph(source('task list item 1'), 'task list item 1'),
2022-07-23 23:45:48 +05:30
),
taskItem(
{
checked: false,
2022-08-13 15:12:31 +05:30
...source('2. [ ] task list item 2'),
2022-07-23 23:45:48 +05:30
},
2022-08-13 15:12:31 +05:30
paragraph(source('task list item 2'), 'task list item 2'),
2022-07-23 23:45:48 +05:30
),
),
),
},
{
markdown: `
| a | b |
|---|---|
| c | d |
`,
expectedDoc: doc(
table(
2022-08-13 15:12:31 +05:30
source('| a | b |\n|---|---|\n| c | d |'),
2022-07-23 23:45:48 +05:30
tableRow(
2022-08-13 15:12:31 +05:30
source('| a | b |'),
tableHeader(source('| a |'), paragraph(source('a'), 'a')),
tableHeader(source(' b |'), paragraph(source('b'), 'b')),
2022-07-23 23:45:48 +05:30
),
tableRow(
2022-08-13 15:12:31 +05:30
source('| c | d |'),
tableCell(source('| c |'), paragraph(source('c'), 'c')),
tableCell(source(' d |'), paragraph(source('d'), 'd')),
2022-07-23 23:45:48 +05:30
),
),
),
},
{
markdown: `
<table>
<tr>
<th colspan="2" rowspan="5">Header</th>
</tr>
<tr>
<td colspan="2" rowspan="5">Body</td>
</tr>
</table>
`,
expectedDoc: doc(
table(
2022-08-13 15:12:31 +05:30
source(
2022-07-23 23:45:48 +05:30
'<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(
2022-08-13 15:12:31 +05:30
source('<tr>\n <th colspan="2" rowspan="5">Header</th>\n </tr>'),
2022-07-23 23:45:48 +05:30
tableHeader(
{
2022-08-13 15:12:31 +05:30
...source('<th colspan="2" rowspan="5">Header</th>'),
2022-07-23 23:45:48 +05:30
colspan: 2,
rowspan: 5,
},
2022-08-13 15:12:31 +05:30
paragraph(source('Header'), 'Header'),
2022-07-23 23:45:48 +05:30
),
),
tableRow(
2022-08-13 15:12:31 +05:30
source('<tr>\n <td colspan="2" rowspan="5">Body</td>\n </tr>'),
2022-07-23 23:45:48 +05:30
tableCell(
{
2022-08-13 15:12:31 +05:30
...source('<td colspan="2" rowspan="5">Body</td>'),
2022-07-23 23:45:48 +05:30
colspan: 2,
rowspan: 5,
},
2022-08-13 15:12:31 +05:30
paragraph(source('Body'), 'Body'),
2022-07-23 23:45:48 +05:30
),
),
),
),
},
{
markdown: `
This is a footnote [^footnote]
Paragraph
[^footnote]: Footnote definition
Paragraph
`,
expectedDoc: doc(
paragraph(
2022-08-13 15:12:31 +05:30
source('This is a footnote [^footnote]'),
2022-07-23 23:45:48 +05:30
'This is a footnote ',
footnoteReference({
2022-08-13 15:12:31 +05:30
...source('[^footnote]'),
2022-07-23 23:45:48 +05:30
identifier: 'footnote',
label: 'footnote',
}),
),
2022-08-13 15:12:31 +05:30
paragraph(source('Paragraph'), 'Paragraph'),
2022-07-23 23:45:48 +05:30
footnoteDefinition(
{
2022-08-13 15:12:31 +05:30
...source('[^footnote]: Footnote definition'),
2022-07-23 23:45:48 +05:30
identifier: 'footnote',
label: 'footnote',
},
2022-08-13 15:12:31 +05:30
paragraph(source('Footnote definition'), 'Footnote definition'),
),
paragraph(source('Paragraph'), 'Paragraph'),
),
},
{
markdown: `
<div>div</div>
`,
expectedDoc: doc(div(source('<div>div</div>'), paragraph(source('div'), 'div'))),
},
{
markdown: `
[![moon](moon.jpg)](/uri)
`,
expectedDoc: doc(
paragraph(
source('[![moon](moon.jpg)](/uri)'),
link(
2022-08-27 11:52:29 +05:30
{
...source('[![moon](moon.jpg)](/uri)'),
canonicalSrc: '/uri',
href: '/uri',
},
image({
...source('![moon](moon.jpg)'),
canonicalSrc: 'moon.jpg',
src: 'moon.jpg',
alt: 'moon',
}),
2022-08-13 15:12:31 +05:30
),
),
),
},
{
markdown: `
<del>
*foo*
</del>
`,
expectedDoc: doc(
paragraph(
source('*foo*'),
strike(source('<del>\n\n*foo*\n\n</del>'), italic(source('*foo*'), 'foo')),
),
),
expectedMarkdown: '*foo*',
},
{
markdown: `
~[moon](moon.jpg) and [sun](sun.jpg)~
`,
expectedDoc: doc(
paragraph(
source('~[moon](moon.jpg) and [sun](sun.jpg)~'),
strike(
source('~[moon](moon.jpg) and [sun](sun.jpg)~'),
2022-08-27 11:52:29 +05:30
link(
{
...source('[moon](moon.jpg)'),
canonicalSrc: 'moon.jpg',
href: 'moon.jpg',
},
'moon',
),
2022-08-13 15:12:31 +05:30
),
strike(source('~[moon](moon.jpg) and [sun](sun.jpg)~'), ' and '),
strike(
source('~[moon](moon.jpg) and [sun](sun.jpg)~'),
2022-08-27 11:52:29 +05:30
link(
{
...source('[sun](sun.jpg)'),
href: 'sun.jpg',
canonicalSrc: 'sun.jpg',
},
'sun',
),
2022-08-13 15:12:31 +05:30
),
),
),
},
{
markdown: `
<del>
**Paragraph 1**
_Paragraph 2_
</del>
`,
expectedDoc: doc(
paragraph(
source('**Paragraph 1**'),
strike(
source('<del>\n\n**Paragraph 1**\n\n_Paragraph 2_\n\n</del>'),
bold(source('**Paragraph 1**'), 'Paragraph 1'),
),
),
paragraph(
source('_Paragraph 2_'),
strike(
source('<del>\n\n**Paragraph 1**\n\n_Paragraph 2_\n\n</del>'),
italic(source('_Paragraph 2_'), 'Paragraph 2'),
),
),
),
expectedMarkdown: `**Paragraph 1**
_Paragraph 2_`,
},
/* TODO
* Implement proper editing support for HTML comments in the Content Editor
* https://gitlab.com/gitlab-org/gitlab/-/issues/342173
*/
{
markdown: '<!-- HTML comment -->',
expectedDoc: doc(paragraph()),
expectedMarkdown: '',
},
{
markdown: `
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then {
return 1;
} else {
return 0;
}
}
]]>
`,
expectedDoc: doc(paragraph()),
expectedMarkdown: '',
},
{
markdown: `
<!-- foo -->*bar*
*baz*
`,
expectedDoc: doc(
paragraph(source('*bar*'), '*bar*\n'),
paragraph(source('*baz*'), italic(source('*baz*'), 'baz')),
),
expectedMarkdown: `*bar*
*baz*`,
},
{
markdown: `
<table><tr><td>
<pre>
**Hello**,
_world_.
</pre>
</td></tr></table>
`,
expectedDoc: doc(
table(
source('<table><tr><td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td></tr></table>'),
tableRow(
source('<tr><td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td></tr>'),
tableCell(
source('<td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td>'),
pre(
source('<pre>\n**Hello**,\n\n_world_.\n</pre>'),
paragraph(source('**Hello**,'), '**Hello**,\n'),
paragraph(source('_world_.\n'), italic(source('_world_'), 'world'), '.\n'),
),
paragraph(),
),
),
2022-07-23 23:45:48 +05:30
),
),
},
2022-08-27 11:52:29 +05:30
{
markdown: `
[GitLab][gitlab-url]
[gitlab-url]: https://gitlab.com "GitLab"
`,
expectedDoc: doc(
paragraph(
source('[GitLab][gitlab-url]'),
link(
{
...source('[GitLab][gitlab-url]'),
href: 'https://gitlab.com',
canonicalSrc: 'gitlab-url',
title: 'GitLab',
isReference: true,
},
'GitLab',
),
),
referenceDefinition(
{
...source('[gitlab-url]: https://gitlab.com "GitLab"'),
identifier: 'gitlab-url',
url: 'https://gitlab.com',
title: 'GitLab',
},
'[gitlab-url]: https://gitlab.com "GitLab"',
),
),
},
{
markdown: `
![GitLab Logo][gitlab-logo]
[gitlab-logo]: https://gitlab.com/gitlab-logo.png "GitLab Logo"
`,
expectedDoc: doc(
paragraph(
source('![GitLab Logo][gitlab-logo]'),
image({
...source('![GitLab Logo][gitlab-logo]'),
src: 'https://gitlab.com/gitlab-logo.png',
canonicalSrc: 'gitlab-logo',
alt: 'GitLab Logo',
title: 'GitLab Logo',
isReference: true,
}),
),
referenceDefinition(
{
...source('[gitlab-logo]: https://gitlab.com/gitlab-logo.png "GitLab Logo"'),
identifier: 'gitlab-logo',
url: 'https://gitlab.com/gitlab-logo.png',
title: 'GitLab Logo',
},
'[gitlab-logo]: https://gitlab.com/gitlab-logo.png "GitLab Logo"',
),
),
},
{
markdown: `
---
title: 'layout'
---
`,
expectedDoc: doc(
frontmatter(
{ ...source("---\ntitle: 'layout'\n---"), language: 'yaml' },
"title: 'layout'",
),
),
},
{
markdown: `
+++
title: 'layout'
+++
`,
expectedDoc: doc(
frontmatter(
{ ...source("+++\ntitle: 'layout'\n+++"), language: 'toml' },
"title: 'layout'",
),
),
},
{
markdown: `
;;;
{ title: 'layout' }
;;;
`,
expectedDoc: doc(
frontmatter(
{ ...source(";;;\n{ title: 'layout' }\n;;;"), language: 'json' },
"{ title: 'layout' }",
),
),
},
2022-10-11 01:57:18 +05:30
...SAFE_AUDIO_EXT.map((extension) => {
const src = `http://test.host/video.${extension}`;
const markdown = `![audio](${src})`;
return {
markdown,
expectedDoc: doc(
paragraph(
source(markdown),
audio({
...source(markdown),
canonicalSrc: src,
src,
alt: 'audio',
}),
),
),
};
}),
...SAFE_VIDEO_EXT.map((extension) => {
const src = `http://test.host/video.${extension}`;
const markdown = `![video](${src})`;
return {
markdown,
expectedDoc: doc(
paragraph(
source(markdown),
video({
...source(markdown),
canonicalSrc: src,
src,
alt: 'video',
}),
),
),
};
}),
...DIAGRAM_LANGUAGES.map((language) => {
const markdown = `\`\`\`${language}
content
\`\`\``;
return {
markdown,
expectedDoc: doc(diagram({ ...source(markdown), language }, 'content')),
};
}),
{
markdown: '[[_TOC_]]',
expectedDoc: doc(tableOfContents(source('[[_TOC_]]'))),
},
{
markdown: '[TOC]',
expectedDoc: doc(tableOfContents(source('[TOC]'))),
},
2022-07-23 23:45:48 +05:30
];
const runOnly = examples.find((example) => example.only === true);
const runExamples = runOnly ? [runOnly] : examples;
2022-08-13 15:12:31 +05:30
it.each(runExamples)(
'processes %s correctly',
async ({ markdown, expectedDoc, expectedMarkdown }) => {
const trimmed = markdown.trim();
const document = await deserialize(trimmed);
2022-07-16 23:28:13 +05:30
2022-08-27 11:52:29 +05:30
expect(expectedDoc).not.toBe(false);
2022-08-13 15:12:31 +05:30
expect(document.toJSON()).toEqual(expectedDoc.toJSON());
expect(serialize(document)).toEqual(expectedMarkdown ?? trimmed);
},
);
/**
* DISCLAIMER: THIS IS A SECURITY ORIENTED TEST THAT ENSURES
* THE CLIENT-SIDE PARSER IGNORES DANGEROUS TAGS THAT ARE NOT
* EXPLICITELY SUPPORTED.
*
* PLEASE CONSIDER THIS INFORMATION WHILE MODIFYING THESE TESTS
*/
it.each([
{
markdown: `
<script>
alert("Hello world")
</script>
`,
expectedHtml: '<p></p>',
},
{
markdown: `
<foo>Hello</foo>
`,
expectedHtml: '<p></p>',
},
{
markdown: `
<h1 class="heading-with-class">Header</h1>
`,
expectedHtml: '<h1>Header</h1>',
},
{
markdown: `
<a id="link-id">Header</a> and other text
`,
expectedHtml:
'<p><a target="_blank" rel="noopener noreferrer nofollow">Header</a> and other text</p>',
},
{
markdown: `
<style>
body {
display: none;
}
</style>
`,
expectedHtml: '<p></p>',
},
{
markdown: '<div style="transform">div</div>',
expectedHtml: '<div><p>div</p></div>',
},
])(
'removes unknown tags and unsupported attributes from HTML output',
async ({ markdown, expectedHtml }) => {
const document = await deserialize(markdown);
tiptapEditor.commands.setContent(document.toJSON());
expect(tiptapEditor.getHTML()).toEqual(expectedHtml);
},
);
2022-08-27 11:52:29 +05:30
describe('attribute sanitization', () => {
// eslint-disable-next-line no-script-url
const protocolBasedInjectionSimpleNoSpaces = "javascript:alert('XSS');";
// eslint-disable-next-line no-script-url
const protocolBasedInjectionSimpleSpacesBefore = "javascript: alert('XSS');";
const docWithImageFactory = (urlInput, urlOutput) => {
const input = `<img src="${urlInput}">`;
return {
input,
expectedDoc: doc(
paragraph(
source(input),
image({
...source(input),
src: urlOutput,
canonicalSrc: urlOutput,
}),
),
),
};
};
const docWithLinkFactory = (urlInput, urlOutput) => {
const input = `<a href="${urlInput}">foo</a>`;
return {
input,
expectedDoc: doc(
paragraph(
source(input),
link({ ...source(input), href: urlOutput, canonicalSrc: urlOutput }, 'foo'),
),
),
};
};
it.each`
desc | urlInput | urlOutput
${'protocol-based JS injection: simple, no spaces'} | ${protocolBasedInjectionSimpleNoSpaces} | ${null}
${'protocol-based JS injection: simple, spaces before'} | ${"javascript :alert('XSS');"} | ${null}
${'protocol-based JS injection: simple, spaces after'} | ${protocolBasedInjectionSimpleSpacesBefore} | ${null}
${'protocol-based JS injection: simple, spaces before and after'} | ${"javascript : alert('XSS');"} | ${null}
${'protocol-based JS injection: UTF-8 encoding'} | ${'javascript&#58;'} | ${null}
${'protocol-based JS injection: long UTF-8 encoding'} | ${'javascript&#0058;'} | ${null}
${'protocol-based JS injection: long UTF-8 encoding without semicolons'} | ${'&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041'} | ${null}
${'protocol-based JS injection: hex encoding'} | ${'javascript&#x3A;'} | ${null}
${'protocol-based JS injection: long hex encoding'} | ${'javascript&#x003A;'} | ${null}
${'protocol-based JS injection: hex encoding without semicolons'} | ${'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29'} | ${null}
${'protocol-based JS injection: Unicode'} | ${"\u0001java\u0003script:alert('XSS')"} | ${null}
${'protocol-based JS injection: spaces and entities'} | ${"&#14; javascript:alert('XSS');"} | ${null}
${'vbscript'} | ${'vbscript:alert(document.domain)'} | ${null}
${'protocol-based JS injection: preceding colon'} | ${":javascript:alert('XSS');"} | ${":javascript:alert('XSS');"}
${'protocol-based JS injection: null char'} | ${"java\0script:alert('XSS')"} | ${"java<76>script:alert('XSS')"}
${'protocol-based JS injection: invalid URL char'} | ${"java\\script:alert('XSS')"} | ${"java\\script:alert('XSS')"}
`('sanitize $desc:\n\tURL "$urlInput" becomes "$urlOutput"', ({ urlInput, urlOutput }) => {
const exampleFactories = [docWithImageFactory, docWithLinkFactory];
exampleFactories.forEach(async (exampleFactory) => {
const { input, expectedDoc } = exampleFactory(urlInput, urlOutput);
const document = await deserialize(input);
expect(document.toJSON()).toEqual(expectedDoc.toJSON());
});
});
});
2022-07-16 23:28:13 +05:30
});