// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE ;(function () { var config = { tabSize: 4, indentUnit: 2 } var mode = CodeMirror.getMode(config, 'markdown') function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)) } var modeHighlightFormatting = CodeMirror.getMode(config, { name: 'markdown', highlightFormatting: true }) function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)) } var modeMT_noXml = CodeMirror.getMode(config, { name: 'markdown', xml: false }) function MT_noXml(name) { test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1)) } var modeMT_noFencedHighlight = CodeMirror.getMode(config, { name: 'markdown', fencedCodeBlockHighlighting: false }) function MT_noFencedHighlight(name) { test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1)) } var modeAtxNoSpace = CodeMirror.getMode(config, { name: 'markdown', allowAtxHeaderWithoutSpace: true }) function AtxNoSpaceTest(name) { test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1)) } var modeOverrideClasses = CodeMirror.getMode(config, { name: 'markdown', strikethrough: true, emoji: true, tokenTypeOverrides: { header: 'override-header', code: 'override-code', quote: 'override-quote', list1: 'override-list1', list2: 'override-list2', list3: 'override-list3', hr: 'override-hr', image: 'override-image', imageAltText: 'override-image-alt-text', imageMarker: 'override-image-marker', linkInline: 'override-link-inline', linkEmail: 'override-link-email', linkText: 'override-link-text', linkHref: 'override-link-href', em: 'override-em', strong: 'override-strong', strikethrough: 'override-strikethrough', emoji: 'override-emoji', }, }) function TokenTypeOverrideTest(name) { test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1)) } var modeFormattingOverride = CodeMirror.getMode(config, { name: 'markdown', highlightFormatting: true, tokenTypeOverrides: { formatting: 'override-formatting', }, }) function FormatTokenTypeOverrideTest(name) { test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1)) } var modeET = CodeMirror.getMode(config, { name: 'markdown', emoji: true }) function ET(name) { test.mode(name, modeET, Array.prototype.slice.call(arguments, 1)) } FT('formatting_emAsterisk', '[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]') FT('formatting_emUnderscore', '[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]') FT('formatting_strongAsterisk', '[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]') FT('formatting_strongUnderscore', '[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]') FT('formatting_codeBackticks', '[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]') FT('formatting_doubleBackticks', '[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]') FT('formatting_atxHeader', '[header&header-1&formatting&formatting-header&formatting-header-1 # ][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]') FT('formatting_setextHeader', '[header&header-1 foo]', '[header&header-1&formatting&formatting-header&formatting-header-1 =]') FT('formatting_blockquote', '[quote"e-1&formatting&formatting-quote&formatting-quote-1 > ][quote"e-1 foo]') FT('formatting_list', '[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]') FT('formatting_list', '[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]') FT( 'formatting_link', '[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url (][string&url http://example.com/][string&formatting&formatting-link-string&url )]' ) FT( 'formatting_linkReference', '[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url [][string&url bar][string&formatting&formatting-link-string&url ]]]', '[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string&url http://example.com/]' ) FT('formatting_linkWeb', '[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]') FT('formatting_linkEmail', '[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]') FT('formatting_escape', '[formatting-escape \\*]') FT( 'formatting_image', '[formatting&formatting-image&image&image-marker !][formatting&formatting-image&image&image-alt-text&link [[][image&image-alt-text&link alt text][formatting&formatting-image&image&image-alt-text&link ]]][formatting&formatting-link-string&string&url (][url&string http://link.to/image.jpg][formatting&formatting-link-string&string&url )]' ) FT('codeBlock', '[comment&formatting&formatting-code-block ```css]', '[tag foo]', '[comment&formatting&formatting-code-block ```]') MT('plainText', 'foo') // Don't style single trailing space MT('trailingSpace1', 'foo ') // Two or more trailing spaces should be styled with line break character MT('trailingSpace2', 'foo[trailing-space-a ][trailing-space-new-line ]') MT('trailingSpace3', 'foo[trailing-space-a ][trailing-space-b ][trailing-space-new-line ]') MT('trailingSpace4', 'foo[trailing-space-a ][trailing-space-b ][trailing-space-a ][trailing-space-new-line ]') // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value) MT('codeBlocksUsing4Spaces', ' [comment foo]') // Code blocks using 4 spaces with internal indentation MT('codeBlocksUsing4SpacesIndentation', ' [comment bar]', ' [comment hello]', ' [comment world]', ' [comment foo]', 'bar') // Code blocks should end even after extra indented lines MT('codeBlocksWithTrailingIndentedLine', ' [comment foo]', ' [comment bar]', ' [comment baz]', ' ', 'hello') // Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value) MT('codeBlocksUsing1Tab', '\t[comment foo]') // No code blocks directly after paragraph // http://spec.commonmark.org/0.19/#example-65 MT('noCodeBlocksAfterParagraph', 'Foo', ' Bar') MT('codeBlocksAfterATX', '[header&header-1 # foo]', ' [comment code]') MT('codeBlocksAfterSetext', '[header&header-2 foo]', '[header&header-2 ---]', ' [comment code]') MT('codeBlocksAfterFencedCode', '[comment ```]', '[comment foo]', '[comment ```]', ' [comment code]') // Inline code using backticks MT('inlineCodeUsingBackticks', 'foo [comment `bar`]') // Block code using single backtick (shouldn't work) MT('blockCodeSingleBacktick', '[comment `]', '[comment foo]', '[comment `]') // Unclosed backticks // Instead of simply marking as CODE, it would be nice to have an // incomplete flag for CODE, that is styled slightly different. MT('unclosedBackticks', 'foo [comment `bar]') // Per documentation: "To include a literal backtick character within a // code span, you can use multiple backticks as the opening and closing // delimiters" MT('doubleBackticks', '[comment ``foo ` bar``]') // Tests based on Dingus // http://daringfireball.net/projects/markdown/dingus // // Multiple backticks within an inline code block MT('consecutiveBackticks', '[comment `foo```bar`]') // Multiple backticks within an inline code block with a second code block MT('consecutiveBackticks', '[comment `foo```bar`] hello [comment `world`]') // Unclosed with several different groups of backticks MT('unclosedBackticks', '[comment ``foo ``` bar` hello]') // Closed with several different groups of backticks MT('closedBackticks', '[comment ``foo ``` bar` hello``] world') // info string cannot contain backtick, thus should result in inline code MT('closingFencedMarksOnSameLine', '[comment ``` code ```] foo') // atx headers // http://daringfireball.net/projects/markdown/syntax#header MT('atxH1', '[header&header-1 # foo]') MT('atxH2', '[header&header-2 ## foo]') MT('atxH3', '[header&header-3 ### foo]') MT('atxH4', '[header&header-4 #### foo]') MT('atxH5', '[header&header-5 ##### foo]') MT('atxH6', '[header&header-6 ###### foo]') // http://spec.commonmark.org/0.19/#example-24 MT('noAtxH7', '####### foo') // http://spec.commonmark.org/0.19/#example-25 MT('noAtxH1WithoutSpace', '#5 bolt') // CommonMark requires a space after # but most parsers don't AtxNoSpaceTest('atxNoSpaceAllowed_H1NoSpace', '[header&header-1 #foo]') AtxNoSpaceTest('atxNoSpaceAllowed_H4NoSpace', '[header&header-4 ####foo]') AtxNoSpaceTest('atxNoSpaceAllowed_H1Space', '[header&header-1 # foo]') // Inline styles should be parsed inside headers MT('atxH1inline', '[header&header-1 # foo ][header&header-1&em *bar*]') MT('atxIndentedTooMuch', '[header&header-1 # foo]', ' [comment # bar]') // disable atx inside blockquote until we implement proper blockquote inner mode // TODO: fix to be CommonMark-compliant MT('atxNestedInsideBlockquote', '[quote"e-1 > # foo]') MT('atxAfterBlockquote', '[quote"e-1 > foo]', '[header&header-1 # bar]') // Setext headers - H1, H2 // Per documentation, "Any number of underlining =’s or -’s will work." // http://daringfireball.net/projects/markdown/syntax#header // Ideally, the text would be marked as `header` as well, but this is // not really feasible at the moment. So, instead, we're testing against // what works today, to avoid any regressions. // // Check if single underlining = works MT('setextH1', '[header&header-1 foo]', '[header&header-1 =]') // Check if 3+ ='s work MT('setextH1', '[header&header-1 foo]', '[header&header-1 ===]') // Check if single underlining - should not be interpreted // as it might lead to an empty list: // https://spec.commonmark.org/0.28/#setext-heading-underline MT('setextH2Single', 'foo', '-') // Check if 3+ -'s work MT('setextH2', '[header&header-2 foo]', '[header&header-2 ---]') // http://spec.commonmark.org/0.19/#example-45 MT('setextH2AllowSpaces', '[header&header-2 foo]', ' [header&header-2 ---- ]') // http://spec.commonmark.org/0.19/#example-44 MT('noSetextAfterIndentedCodeBlock', ' [comment foo]', '[hr ---]') MT('setextAfterFencedCode', '[comment ```]', '[comment foo]', '[comment ```]', '[header&header-2 bar]', '[header&header-2 ---]') MT('setextAfterATX', '[header&header-1 # foo]', '[header&header-2 bar]', '[header&header-2 ---]') // http://spec.commonmark.org/0.19/#example-51 MT('noSetextAfterQuote', '[quote"e-1 > foo]', '[hr ---]', '', '[quote"e-1 > foo]', '[quote"e-1 bar]', '[hr ---]') MT('noSetextAfterList', '[variable-2 - foo]', '[hr ---]') MT('noSetextAfterList_listContinuation', '[variable-2 - foo]', 'bar', '[hr ---]') MT('setextAfterList_afterIndentedCode', '[variable-2 - foo]', '', ' [comment bar]', '[header&header-2 baz]', '[header&header-2 ---]') MT('setextAfterList_afterFencedCodeBlocks', '[variable-2 - foo]', '', ' [comment ```]', ' [comment bar]', ' [comment ```]', '[header&header-2 baz]', '[header&header-2 ---]') MT('setextAfterList_afterHeader', '[variable-2 - foo]', ' [variable-2&header&header-1 # bar]', '[header&header-2 baz]', '[header&header-2 ---]') MT('setextAfterList_afterHr', '[variable-2 - foo]', '', ' [hr ---]', '[header&header-2 bar]', '[header&header-2 ---]') MT('setext_nestedInlineMarkup', '[header&header-1 foo ][em&header&header-1 *bar*]', '[header&header-1 =]') MT('setext_linkDef', "[link [[aaa]]:] [string&url http://google.com 'title']", '[hr ---]') // currently, looks max one line ahead, thus won't catch valid CommonMark // markup MT('setext_oneLineLookahead', 'foo', '[header&header-1 bar]', '[header&header-1 =]') // ensure we regard space after a single dash as a list MT('setext_emptyList', 'foo', '[variable-2 - ]', 'foo') // Single-line blockquote with trailing space MT('blockquoteSpace', '[quote"e-1 > foo]') // Single-line blockquote MT('blockquoteNoSpace', '[quote"e-1 >foo]') // No blank line before blockquote MT('blockquoteNoBlankLine', 'foo', '[quote"e-1 > bar]') MT('blockquoteNested', '[quote"e-1 > foo]', '[quote"e-1 >][quote"e-2 > foo]', '[quote"e-1 >][quote"e-2 >][quote"e-3 > foo]') // ensure quote-level is inferred correctly even if indented MT('blockquoteNestedIndented', ' [quote"e-1 > foo]', ' [quote"e-1 >][quote"e-2 > foo]', ' [quote"e-1 >][quote"e-2 >][quote"e-3 > foo]') // ensure quote-level is inferred correctly even if indented MT('blockquoteIndentedTooMuch', 'foo', ' > bar') // Single-line blockquote followed by normal paragraph MT('blockquoteThenParagraph', '[quote"e-1 >foo]', '', 'bar') // Multi-line blockquote (lazy mode) MT('multiBlockquoteLazy', '[quote"e-1 >foo]', '[quote"e-1 bar]') // Multi-line blockquote followed by normal paragraph (lazy mode) MT('multiBlockquoteLazyThenParagraph', '[quote"e-1 >foo]', '[quote"e-1 bar]', '', 'hello') // Multi-line blockquote (non-lazy mode) MT('multiBlockquote', '[quote"e-1 >foo]', '[quote"e-1 >bar]') // Multi-line blockquote followed by normal paragraph (non-lazy mode) MT('multiBlockquoteThenParagraph', '[quote"e-1 >foo]', '[quote"e-1 >bar]', '', 'hello') // disallow lists inside blockquote for now because it causes problems outside blockquote // TODO: fix to be CommonMark-compliant MT('listNestedInBlockquote', '[quote"e-1 > - foo]') // disallow fenced blocks inside blockquote because it causes problems outside blockquote // TODO: fix to be CommonMark-compliant MT( 'fencedBlockNestedInBlockquote', '[quote"e-1 > ```]', '[quote"e-1 > code]', '[quote"e-1 > ```]', // ensure we still allow inline code '[quote"e-1 > ][quote"e-1&comment `code`]' ) // Header with leading space after continued blockquote (#3287, negative indentation) MT('headerAfterContinuedBlockquote', '[quote"e-1 > foo]', '[quote"e-1 bar]', '', ' [header&header-1 # hello]') // Check list types MT('listAsterisk', 'foo', 'bar', '', '[variable-2 * foo]', '[variable-2 * bar]') MT('listPlus', 'foo', 'bar', '', '[variable-2 + foo]', '[variable-2 + bar]') MT('listDash', 'foo', 'bar', '', '[variable-2 - foo]', '[variable-2 - bar]') MT('listNumber', 'foo', 'bar', '', '[variable-2 1. foo]', '[variable-2 2. bar]') MT('listFromParagraph', 'foo', '[variable-2 1. bar]', '[variable-2 2. hello]') // List after hr MT('listAfterHr', '[hr ---]', '[variable-2 - bar]') // List after header MT('listAfterHeader', '[header&header-1 # foo]', '[variable-2 - bar]') // hr after list MT('hrAfterList', '[variable-2 - foo]', '[hr -----]') MT('hrAfterFencedCode', '[comment ```]', '[comment code]', '[comment ```]', '[hr ---]') // allow hr inside lists // (require prev line to be empty or hr, TODO: non-CommonMark-compliant) MT('hrInsideList', '[variable-2 - foo]', '', ' [hr ---]', ' [hr ---]', '', ' [comment ---]') MT('consecutiveHr', '[hr ---]', '[hr ---]', '[hr ---]') // Formatting in lists (*) MT( 'listAsteriskFormatting', '[variable-2 * ][variable-2&em *foo*][variable-2 bar]', '[variable-2 * ][variable-2&strong **foo**][variable-2 bar]', '[variable-2 * ][variable-2&em&strong ***foo***][variable-2 bar]', '[variable-2 * ][variable-2&comment `foo`][variable-2 bar]' ) // Formatting in lists (+) MT( 'listPlusFormatting', '[variable-2 + ][variable-2&em *foo*][variable-2 bar]', '[variable-2 + ][variable-2&strong **foo**][variable-2 bar]', '[variable-2 + ][variable-2&em&strong ***foo***][variable-2 bar]', '[variable-2 + ][variable-2&comment `foo`][variable-2 bar]' ) // Formatting in lists (-) MT( 'listDashFormatting', '[variable-2 - ][variable-2&em *foo*][variable-2 bar]', '[variable-2 - ][variable-2&strong **foo**][variable-2 bar]', '[variable-2 - ][variable-2&em&strong ***foo***][variable-2 bar]', '[variable-2 - ][variable-2&comment `foo`][variable-2 bar]' ) // Formatting in lists (1.) MT( 'listNumberFormatting', '[variable-2 1. ][variable-2&em *foo*][variable-2 bar]', '[variable-2 2. ][variable-2&strong **foo**][variable-2 bar]', '[variable-2 3. ][variable-2&em&strong ***foo***][variable-2 bar]', '[variable-2 4. ][variable-2&comment `foo`][variable-2 bar]' ) // Paragraph lists MT('listParagraph', '[variable-2 * foo]', '', '[variable-2 * bar]') // Multi-paragraph lists // // 4 spaces MT('listMultiParagraph', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2 hello]') // 4 spaces, extra blank lines (should still be list, per Dingus) MT('listMultiParagraphExtra', '[variable-2 * foo]', '', '[variable-2 * bar]', '', '', ' [variable-2 hello]') // 4 spaces, plus 1 space (should still be list, per Dingus) MT('listMultiParagraphExtraSpace', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2 hello]', '', ' [variable-2 world]') // 1 tab MT('listTab', '[variable-2 * foo]', '', '[variable-2 * bar]', '', '\t[variable-2 hello]') // No indent MT('listNoIndent', '[variable-2 * foo]', '', '[variable-2 * bar]', '', 'hello') MT( 'listCommonMarkIndentationCode', '[variable-2 * Code blocks also affect]', ' [variable-3 * The next level starts where the contents start.]', ' [variable-3 * Anything less than that will keep the item on the same level.]', ' [variable-3 * Each list item can indent the first level further and further.]', ' [variable-3 * For the most part, this makes sense while writing a list.]', ' [keyword * This means two items with same indentation can be different levels.]', ' [keyword * Each level has an indent requirement that can change between items.]', ' [keyword * A list item that meets this will be part of the next level.]', ' [variable-3 * Otherwise, it will be part of the level where it does meet this.]', ' [variable-2 * World]' ) // should handle nested and un-nested lists MT( 'listCommonMark_MixedIndents', '[variable-2 * list1]', ' [variable-2 list1]', ' [variable-2&header&header-1 # heading still part of list1]', ' [variable-2 text after heading still part of list1]', '', ' [comment indented codeblock]', ' [variable-2 list1 after code block]', ' [variable-3 * list2]', // amount of spaces on empty lines between lists doesn't matter ' ', // extra empty lines irrelevant '', '', ' [variable-3 indented text part of list2]', ' [keyword * list3]', '', ' [variable-3 text at level of list2]', '', ' [variable-2 de-indented text part of list1 again]', '', ' [variable-2&comment ```]', ' [comment code]', ' [variable-2&comment ```]', '', ' [variable-2 text after fenced code]' ) // should correctly parse numbered list content indentation MT( 'listCommonMark_NumberedListIndent', '[variable-2 1000. list with base indent of 6]', '', ' [variable-2 text must be indented 6 spaces at minimum]', '', ' [variable-2 9-spaces indented text still part of list]', '', ' [comment indented codeblock starts at 10 spaces]', '', ' [comment text indented by 5 spaces no longer belong to list]' ) // should consider tab as 4 spaces MT('listCommonMark_TabIndented', '[variable-2 * list]', '\t[variable-3 * list2]', '', '\t\t[variable-3 part of list2]') MT('listAfterBlockquote', '[quote"e-1 > foo]', '[variable-2 - bar]') // shouldn't create sublist if it's indented more than allowed MT('nestedListIndentedTooMuch', '[variable-2 - foo]', ' [variable-2 - bar]') MT('listIndentedTooMuchAfterParagraph', 'foo', ' - bar') // Blockquote MT('blockquote', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [variable-2"e"e-1 > hello]') // Code block MT('blockquoteCode', '[variable-2 * foo]', '', '[variable-2 * bar]', '', ' [comment > hello]', '', ' [variable-2 world]') // Code block followed by text MT('blockquoteCodeText', '[variable-2 * foo]', '', ' [variable-2 bar]', '', ' [comment hello]', '', ' [variable-2 world]') // Nested list MT('listAsteriskNested', '[variable-2 * foo]', '', ' [variable-3 * bar]') MT('listPlusNested', '[variable-2 + foo]', '', ' [variable-3 + bar]') MT('listDashNested', '[variable-2 - foo]', '', ' [variable-3 - bar]') MT('listNumberNested', '[variable-2 1. foo]', '', ' [variable-3 2. bar]') MT('listMixed', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [keyword - hello]', '', ' [variable-2 1. world]') MT('listBlockquote', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [quote"e-1&variable-3 > hello]') MT('listCode', '[variable-2 * foo]', '', ' [variable-3 + bar]', '', ' [comment hello]') // Code with internal indentation MT('listCodeIndentation', '[variable-2 * foo]', '', ' [comment bar]', ' [comment hello]', ' [comment world]', ' [comment foo]', ' [variable-2 bar]') // List nesting edge cases MT('listNested', '[variable-2 * foo]', '', ' [variable-3 * bar]', '', ' [variable-3 hello]') MT('listNested', '[variable-2 * foo]', '', ' [variable-3 * bar]', '', ' [keyword * foo]') // Code followed by text MT('listCodeText', '[variable-2 * foo]', '', ' [comment bar]', '', 'hello') // Following tests directly from official Markdown documentation // http://daringfireball.net/projects/markdown/syntax#hr MT('hrSpace', '[hr * * *]') MT('hr', '[hr ***]') MT('hrLong', '[hr *****]') MT('hrSpaceDash', '[hr - - -]') MT('hrDashLong', '[hr ---------------------------------------]') //Images MT('Images', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)]') //Images with highlight alt text MT('imageEm', '[image&image-marker !][image&image-alt-text&link [[][image-alt-text&em&image&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]') MT('imageStrong', '[image&image-marker !][image&image-alt-text&link [[][image-alt-text&strong&image&link **alt text**][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]') MT('imageEmStrong', '[image&image-marker !][image&image-alt-text&link [[][image&image-alt-text&em&strong&link ***alt text***][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]') // Inline link with title MT('linkTitle', '[link [[foo]]][string&url (http://example.com/ "bar")] hello') // Inline link without title MT('linkNoTitle', '[link [[foo]]][string&url (http://example.com/)] bar') // Inline link with image MT('linkImage', '[link [[][link&image&image-marker !][link&image&image-alt-text&link [[alt text]]][string&url (http://link.to/image.jpg)][link ]]][string&url (http://example.com/)] bar') // Inline link with Em MT('linkEm', '[link [[][link&em *foo*][link ]]][string&url (http://example.com/)] bar') // Inline link with Strong MT('linkStrong', '[link [[][link&strong **foo**][link ]]][string&url (http://example.com/)] bar') // Inline link with EmStrong MT('linkEmStrong', '[link [[][link&em&strong ***foo***][link ]]][string&url (http://example.com/)] bar') MT('multilineLink', '[link [[foo]', '[link bar]]][string&url (https://foo#_a)]', 'should not be italics') // Image with title MT('imageTitle', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/ "bar")] hello') // Image without title MT('imageNoTitle', '[image&image-marker !][image&image-alt-text&link [[alt text]]][string&url (http://example.com/)] bar') // Image with asterisks MT('imageAsterisks', '[image&image-marker !][image&image-alt-text&link [[ ][image&image-alt-text&em&link *alt text*][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)] bar') // Not a link. Should be normal text due to square brackets being used // regularly in text, especially in quoted material, and no space is allowed // between square brackets and parentheses (per Dingus). MT('notALink', '[link [[foo]]] (bar)') // Reference-style links MT('linkReference', '[link [[foo]]][string&url [[bar]]] hello') // Reference-style links with Em MT('linkReferenceEm', '[link [[][link&em *foo*][link ]]][string&url [[bar]]] hello') // Reference-style links with Strong MT('linkReferenceStrong', '[link [[][link&strong **foo**][link ]]][string&url [[bar]]] hello') // Reference-style links with EmStrong MT('linkReferenceEmStrong', '[link [[][link&em&strong ***foo***][link ]]][string&url [[bar]]] hello') // Reference-style links with optional space separator (per documentation) // "You can optionally use a space to separate the sets of brackets" MT('linkReferenceSpace', '[link [[foo]]] [string&url [[bar]]] hello') // Should only allow a single space ("...use *a* space...") MT('linkReferenceDoubleSpace', '[link [[foo]]] [link [[bar]]] hello') // Reference-style links with implicit link name MT('linkImplicit', '[link [[foo]]][string&url [[]]] hello') // @todo It would be nice if, at some point, the document was actually // checked to see if the referenced link exists // Link label, for reference-style links (taken from documentation) MT('labelNoTitle', '[link [[foo]]:] [string&url http://example.com/]') MT('labelIndented', ' [link [[foo]]:] [string&url http://example.com/]') MT('labelSpaceTitle', '[link [[foo bar]]:] [string&url http://example.com/ "hello"]') MT('labelDoubleTitle', '[link [[foo bar]]:] [string&url http://example.com/ "hello"] "world"') MT('labelTitleDoubleQuotes', '[link [[foo]]:] [string&url http://example.com/ "bar"]') MT('labelTitleSingleQuotes', "[link [[foo]]:] [string&url http://example.com/ 'bar']") MT('labelTitleParentheses', '[link [[foo]]:] [string&url http://example.com/ (bar)]') MT('labelTitleInvalid', '[link [[foo]]:] [string&url http://example.com/] bar') MT('labelLinkAngleBrackets', '[link [[foo]]:] [string&url "bar"]') MT('labelTitleNextDoubleQuotes', '[link [[foo]]:] [string&url http://example.com/]', '[string "bar"] hello') MT('labelTitleNextSingleQuotes', '[link [[foo]]:] [string&url http://example.com/]', "[string 'bar'] hello") MT('labelTitleNextParentheses', '[link [[foo]]:] [string&url http://example.com/]', '[string (bar)] hello') MT('labelTitleNextMixed', '[link [[foo]]:] [string&url http://example.com/]', '(bar" hello') MT('labelEscape', '[link [[foo \\]] ]]:] [string&url http://example.com/]') MT('labelEscapeColon', '[link [[foo \\]]: bar]]:] [string&url http://example.com/]') MT('labelEscapeEnd', '\\[[foo\\]]: http://example.com/') MT('linkWeb', '[link ] foo') MT('linkWebDouble', '[link ] foo [link ]') MT('linkEmail', '[link ] foo') MT('linkEmailDouble', '[link ] foo [link ]') MT('emAsterisk', '[em *foo*] bar') MT('emUnderscore', '[em _foo_] bar') MT('emInWordAsterisk', 'foo[em *bar*]hello') MT('emInWordUnderscore', 'foo_bar_hello') // Per documentation: "...surround an * or _ with spaces, it’ll be // treated as a literal asterisk or underscore." MT('emEscapedBySpaceIn', 'foo [em _bar _ hello_] world') MT('emEscapedBySpaceOut', 'foo _ bar [em _hello_] world') MT('emEscapedByNewline', 'foo', '_ bar [em _hello_] world') // Unclosed emphasis characters // Instead of simply marking as EM / STRONG, it would be nice to have an // incomplete flag for EM and STRONG, that is styled slightly different. MT('emIncompleteAsterisk', 'foo [em *bar]') MT('emIncompleteUnderscore', 'foo [em _bar]') MT('strongAsterisk', '[strong **foo**] bar') MT('strongUnderscore', '[strong __foo__] bar') MT('emStrongAsterisk', '[em *foo][em&strong **bar*][strong hello**] world') MT('emStrongUnderscore', '[em _foo ][em&strong __bar_][strong hello__] world') // "...same character must be used to open and close an emphasis span."" MT('emStrongMixed', '[em _foo][em&strong **bar*hello__ world]') MT('emStrongMixed', '[em *foo ][em&strong __bar_hello** world]') MT('linkWithNestedParens', '[link [[foo]]][string&url (bar(baz))]') // These characters should be escaped: // \ backslash // ` backtick // * asterisk // _ underscore // {} curly braces // [] square brackets // () parentheses // # hash mark // + plus sign // - minus sign (hyphen) // . dot // ! exclamation mark MT('escapeBacktick', 'foo \\`bar\\`') MT('doubleEscapeBacktick', 'foo \\\\[comment `bar\\\\`]') MT('escapeAsterisk', 'foo \\*bar\\*') MT('doubleEscapeAsterisk', 'foo \\\\[em *bar\\\\*]') MT('escapeUnderscore', 'foo \\_bar\\_') MT('doubleEscapeUnderscore', 'foo \\\\[em _bar\\\\_]') MT('escapeHash', '\\# foo') MT('doubleEscapeHash', '\\\\# foo') MT('escapeNewline', '\\', '[em *foo*]') // Class override tests TokenTypeOverrideTest('overrideHeader1', '[override-header&override-header-1 # Foo]') TokenTypeOverrideTest('overrideHeader2', '[override-header&override-header-2 ## Foo]') TokenTypeOverrideTest('overrideHeader3', '[override-header&override-header-3 ### Foo]') TokenTypeOverrideTest('overrideHeader4', '[override-header&override-header-4 #### Foo]') TokenTypeOverrideTest('overrideHeader5', '[override-header&override-header-5 ##### Foo]') TokenTypeOverrideTest('overrideHeader6', '[override-header&override-header-6 ###### Foo]') TokenTypeOverrideTest('overrideCode', '[override-code `foo`]') TokenTypeOverrideTest('overrideCodeBlock', '[override-code ```]', '[override-code foo]', '[override-code ```]') TokenTypeOverrideTest('overrideQuote', '[override-quote&override-quote-1 > foo]', '[override-quote&override-quote-1 > bar]') TokenTypeOverrideTest( 'overrideQuoteNested', '[override-quote&override-quote-1 > foo]', '[override-quote&override-quote-1 >][override-quote&override-quote-2 > bar]', '[override-quote&override-quote-1 >][override-quote&override-quote-2 >][override-quote&override-quote-3 > baz]' ) TokenTypeOverrideTest( 'overrideLists', '[override-list1 - foo]', '', ' [override-list2 + bar]', '', ' [override-list3 * baz]', '', ' [override-list1 1. qux]', '', ' [override-list2 - quux]' ) TokenTypeOverrideTest('overrideHr', '[override-hr * * *]') TokenTypeOverrideTest('overrideImage', '[override-image&override-image-marker !][override-image&override-image-alt-text&link [[alt text]]][override-link-href&url (http://link.to/image.jpg)]') TokenTypeOverrideTest('overrideLinkText', '[override-link-text [[foo]]][override-link-href&url (http://example.com)]') TokenTypeOverrideTest('overrideLinkEmailAndInline', '[override-link-email <][override-link-inline foo@example.com>]') TokenTypeOverrideTest('overrideEm', '[override-em *foo*]') TokenTypeOverrideTest('overrideStrong', '[override-strong **foo**]') TokenTypeOverrideTest('overrideStrikethrough', '[override-strikethrough ~~foo~~]') TokenTypeOverrideTest('overrideEmoji', '[override-emoji :foo:]') FormatTokenTypeOverrideTest('overrideFormatting', '[override-formatting-escape \\*]') // Tests to make sure GFM-specific things aren't getting through MT('taskList', '[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]') MT('fencedCodeBlocks', '[comment ```]', '[comment foo]', '', '[comment bar]', '[comment ```]', 'baz') MT('fencedCodeBlocks_invalidClosingFence_trailingText', '[comment ```]', '[comment foo]', '[comment ``` must not have trailing text]', '[comment baz]') MT('fencedCodeBlocks_invalidClosingFence_trailingTabs', '[comment ```]', '[comment foo]', '[comment ```\t]', '[comment baz]') MT( 'fencedCodeBlocks_validClosingFence', '[comment ```]', '[comment foo]', // may have trailing spaces '[comment ``` ]', 'baz' ) MT('fencedCodeBlocksInList_closingFenceIndented', '[variable-2 - list]', ' [variable-2&comment ```]', ' [comment foo]', ' [variable-2&comment ```]', ' [variable-2 baz]') MT('fencedCodeBlocksInList_closingFenceIndentedTooMuch', '[variable-2 - list]', ' [variable-2&comment ```]', ' [comment foo]', ' [comment ```]', ' [comment baz]') MT('fencedCodeBlockModeSwitching', '[comment ```javascript]', '[variable foo]', '', '[comment ```]', 'bar') MT_noFencedHighlight('fencedCodeBlock_noHighlight', '[comment ```javascript]', '[comment foo]', '[comment ```]') MT('fencedCodeBlockModeSwitchingObjc', '[comment ```objective-c]', '[keyword @property] [variable NSString] [operator *] [variable foo];', '[comment ```]', 'bar') MT('fencedCodeBlocksMultipleChars', '[comment `````]', '[comment foo]', '[comment ```]', '[comment foo]', '[comment `````]', 'bar') MT('fencedCodeBlocksTildes', '[comment ~~~]', '[comment foo]', '[comment ~~~]', 'bar') MT('fencedCodeBlocksTildesMultipleChars', '[comment ~~~~~]', '[comment ~~~]', '[comment foo]', '[comment ~~~~~]', 'bar') MT('fencedCodeBlocksMultipleChars', '[comment `````]', '[comment foo]', '[comment ```]', '[comment foo]', '[comment `````]', 'bar') MT('fencedCodeBlocksMixed', '[comment ~~~]', '[comment ```]', '[comment foo]', '[comment ~~~]', 'bar') MT('fencedCodeBlocksAfterBlockquote', '[quote"e-1 > foo]', '[comment ```]', '[comment bar]', '[comment ```]') // fencedCode indented too much should act as simple indentedCode // (hence has no highlight formatting) FT('tooMuchIndentedFencedCode', ' [comment ```]', ' [comment code]', ' [comment ```]') MT( 'autoTerminateFencedCodeWhenLeavingList', '[variable-2 - list1]', ' [variable-3 - list2]', ' [variable-3&comment ```]', ' [comment code]', ' [variable-3 - list2]', ' [variable-2&comment ```]', ' [comment code]', '[quote"e-1 > foo]' ) // Tests that require XML mode MT( 'xmlMode', '[tag&bracket <][tag div][tag&bracket >]', ' *foo*', ' [tag&bracket <][tag http://github.com][tag&bracket />]', '[tag&bracket ]', '[link ]' ) MT( 'xmlModeWithMarkdownInside', '[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]', '[em *foo*]', '[link ]', '[tag ]', '[link ]', '[tag&bracket <][tag div][tag&bracket >]', '[tag&bracket ]' ) MT('xmlModeLineBreakInTags', '[tag&bracket <][tag div] [attribute id]=[string "1"]', ' [attribute class]=[string "sth"][tag&bracket >]xxx', '[tag&bracket ]') MT('xmlModeCommentWithBlankLine', '[comment ]') MT('xmlModeCDATA', '[atom ]') MT('xmlModePreprocessor', "[meta ]") MT_noXml('xmlHighlightDisabled', '
foo
') // Tests Emojis ET('emojiDefault', '[builtin :foobar:]') ET('emojiTable', ' :--:') })()