|
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/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 <http://example.com/> \"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 <http://example.com/>] foo");
MT("linkWebDouble", "[link <http://example.com/>] foo [link <http://example.com/>]");
MT("linkEmail", "[link <user@example.com>] foo");
MT("linkEmailDouble", "[link <user@example.com>] foo [link <user@example.com>]");
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 </][tag div][tag&bracket >]", "[link <http://github.com/>]");
MT("xmlModeWithMarkdownInside", "[tag&bracket <][tag div] [attribute markdown]=[string 1][tag&bracket >]", "[em *foo*]", "[link <http://github.com/>]", "[tag </div>]", "[link <http://github.com/>]", "[tag&bracket <][tag div][tag&bracket >]", "[tag&bracket </][tag div][tag&bracket >]");
MT("xmlModeLineBreakInTags", "[tag&bracket <][tag div] [attribute id]=[string \"1\"]", " [attribute class]=[string \"sth\"][tag&bracket >]xxx", "[tag&bracket </][tag div][tag&bracket >]");
MT("xmlModeCommentWithBlankLine", "[comment <!-- Hello]", "", "[comment World -->]");
MT("xmlModeCDATA", "[atom <![CDATA[ Hello]", "", "[atom FooBar]", "[atom Test ]]]]>]");
MT("xmlModePreprocessor", "[meta <?php] [meta echo '1234'; ?>]");
MT_noXml("xmlHighlightDisabled", "<div>foo</div>");
// Tests Emojis
ET("emojiDefault", "[builtin :foobar:]");
ET("emojiTable", " :--:"); })();
|