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 HTMLMarks from '~/content_editor/extensions/html_marks' ;
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' ;
2021-08-19 21:10:32 +00:00
import { createTestEditor , createDocBuilder } from '../test_utils' ;
jest . mock ( '~/emoji' ) ;
const tiptapEditor = createTestEditor ( {
extensions : [
Blockquote ,
Bold ,
BulletList ,
Code ,
CodeBlockHighlight ,
2021-09-01 18:08:49 +00:00
DescriptionItem ,
DescriptionList ,
2021-10-08 21:09:48 +00:00
Details ,
DetailsContent ,
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
HardBreak ,
Heading ,
HorizontalRule ,
Image ,
2021-08-26 21:11:25 +00:00
InlineDiff ,
2021-08-19 21:10:32 +00:00
Italic ,
Link ,
ListItem ,
OrderedList ,
2022-07-29 15:12:25 +00:00
ReferenceDefinition ,
2022-05-11 15:07:26 +00:00
Sourcemap ,
2021-08-19 21:10:32 +00:00
Strike ,
Table ,
TableCell ,
TableHeader ,
TableRow ,
2021-08-26 21:11:25 +00:00
TaskItem ,
TaskList ,
2022-07-12 21:09:53 +00:00
... HTMLMarks ,
... HTMLNodes ,
2021-08-19 21:10:32 +00:00
] ,
} ) ;
const {
builders : {
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 ,
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")' ) ;
} ) ;
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 < / d t >
< dd > A large feline inhabiting Bodmin Moor . < / d d >
< dt > Morgawr < / d t >
< dd > A sea serpent . < / d d >
< dt > Owlman < / d t >
< dd >
A giant _owl - like _ creature .
< / d d >
< / d l >
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 < / s u m m a r y >
this content will be hidden
< / d e t a i l s >
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 * *
< / s u m m a r y >
\ ` \` \` javascript
var a = 2 ;
var b = 3 ;
var c = a + d ;
console . log ( c ) ;
\ ` \` \`
this content will be _hidden _
< / d e t a i l s >
2022-03-03 03:19:02 +00:00
2021-10-08 21:09:48 +00:00
< details >
< summary > summary 2 < / s u m m a r y >
content 2
< / d e t a i l s >
` .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 < / s u m m a r y >
< details >
< summary > dream level 2 < / s u m m a r y >
< details >
< summary > dream level 3 < / s u m m a r y >
_inception _
< / d e t a i l s >
2022-03-03 03:19:02 +00:00
2021-10-08 21:09:48 +00:00
< / d e t a i l s >
2022-03-03 03:19:02 +00:00
2021-10-08 21:09:48 +00:00
< / d e t a i l s >
` .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 < / f i g c a p t i o n >
< / f i g u r e >
` .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 _
< / f i g c a p t i o n >
< / f i g u r e >
` .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 < / t h >
< th > block content < / t h >
< th > in tables < / t h >
< th > in content editor < / t h >
< / t r >
< tr >
< td >
# heading 1
< / t d >
< td >
# # heading 2
< / t d >
< td >
* * just bold * *
< / t d >
< td >
* * bold * * _italic _ \ ` code \`
< / t d >
< / t r >
< tr >
< td >
all marks in three paragraphs :
the * * quick * * _brown _ \ ` fox \`
[ jumps ] ( / h o m e ) o v e r t h e ~ ~ l a z y ~ ~ : d o g :
< / t d >
< td >
! [ some image ] ( img . jpg ) < br > image content
< / t d >
< td >
> some text \ \
> \ \
> in a multiline blockquote
< / t d >
< td >
\ ` \` \` javascript
var a = 2 ;
var b = 3 ;
var c = a + d ;
console . log ( c ) ;
\ ` \` \`
< / t d >
< / t r >
< tr >
< td >
* item 1
* item 2
* item 2
< / t d >
< td >
1. item 1
2. item 2
3. item 2
< / t d >
< td >
paragraphs separated by
-- -
a horizontal rule
< / t d >
< td >
| table | inside |
| -- -- -- - | -- -- -- -- |
| another | table |
< / t d >
< / t r >
< / t a b l e >
` .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 < / t h >
< / t r >
< tr >
< td >
> hi
there
< / t d >
< / t r >
< / t a b l e >
# 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 < / t h >
< td > cell < / t d >
< / t r >
< tr >
< td > cell < / t d >
< th > cell < / t h >
< / t r >
< / t a b l e >
` .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 < / t d >
< td > cell < / t d >
< / t r >
< tr >
< td > cell < / t d >
< td > cell < / t d >
< / t r >
< / t a b l e >
` .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 < / t h >
< th > header < / t h >
< th > header < / t h >
< / t r >
< tr >
< td colspan = "2" > cell with rowspan : 2 < / t d >
< td rowspan = "2" > cell < / t d >
< / t r >
< tr >
< td colspan = "2" > cell with rowspan : 2 < / t d >
< / t r >
< / t a b l e >
` .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-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-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-07-14 12:08:33 +00:00
mark | markdown | modifiedMarkdown | editAction
2022-06-14 06:09:22 +00:00
$ { '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 }
2022-07-27 18:12:02 +00:00
$ { 'link' } | $ { 'link https://www.gitlab.com>' } | $ { 'modified link [https://www.gitlab.com>](https://www.gitlab.com%3E)' } | $ { prependContentEditAction }
2022-06-14 06:09:22 +00:00
$ { 'link' } | $ { 'link **https://www.gitlab.com]**' } | $ { 'modified link [**https://www.gitlab.com\\]**](https://www.gitlab.com%5D)' } | $ { 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 }
2022-05-11 15:07:26 +00:00
` (
2022-06-02 00:09:20 +00:00
'preserves original $mark syntax when sourceMarkdown is available for $content' ,
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
} ) ;