// Initiate ModeTest and set defaults var MT = ModeTest; MT.modeName = 'markdown'; MT.modeOptions = {}; MT.testMode( 'plainText', 'foo', [ null, 'foo' ] ); // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value) MT.testMode( 'codeBlocksUsing4Spaces', ' foo', [ null, ' ', 'comment', 'foo' ] ); // Code blocks using 4 spaces with internal indentation MT.testMode( 'codeBlocksUsing4SpacesIndentation', ' bar\n hello\n world\n foo\nbar', [ null, ' ', 'comment', 'bar', null, ' ', 'comment', 'hello', null, ' ', 'comment', 'world', null, ' ', 'comment', 'foo', null, 'bar' ] ); // Code blocks using 4 spaces with internal indentation MT.testMode( 'codeBlocksUsing4SpacesIndentation', ' foo\n bar\n hello\n world', [ null, ' foo', null, ' ', 'comment', 'bar', null, ' ', 'comment', 'hello', null, ' ', 'comment', 'world' ] ); // Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value) MT.testMode( 'codeBlocksUsing1Tab', '\tfoo', [ null, '\t', 'comment', 'foo' ] ); // Inline code using backticks MT.testMode( 'inlineCodeUsingBackticks', 'foo `bar`', [ null, 'foo ', 'comment', '`bar`' ] ); // Block code using single backtick (shouldn't work) MT.testMode( 'blockCodeSingleBacktick', '`\nfoo\n`', [ 'comment', '`', null, '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.testMode( 'unclosedBackticks', 'foo `bar', [ null, '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.testMode( 'doubleBackticks', '``foo ` bar``', [ 'comment', '``foo ` bar``' ] ); // Tests based on Dingus // http://daringfireball.net/projects/markdown/dingus // // Multiple backticks within an inline code block MT.testMode( 'consecutiveBackticks', '`foo```bar`', [ 'comment', '`foo```bar`' ] ); // Multiple backticks within an inline code block with a second code block MT.testMode( 'consecutiveBackticks', '`foo```bar` hello `world`', [ 'comment', '`foo```bar`', null, ' hello ', 'comment', '`world`' ] ); // Unclosed with several different groups of backticks MT.testMode( 'unclosedBackticks', '``foo ``` bar` hello', [ 'comment', '``foo ``` bar` hello' ] ); // Closed with several different groups of backticks MT.testMode( 'closedBackticks', '``foo ``` bar` hello`` world', [ 'comment', '``foo ``` bar` hello``', null, ' world' ] ); // atx headers // http://daringfireball.net/projects/markdown/syntax#header // // H1 MT.testMode( 'atxH1', '# foo', [ 'header', '# foo' ] ); // H2 MT.testMode( 'atxH2', '## foo', [ 'header', '## foo' ] ); // H3 MT.testMode( 'atxH3', '### foo', [ 'header', '### foo' ] ); // H4 MT.testMode( 'atxH4', '#### foo', [ 'header', '#### foo' ] ); // H5 MT.testMode( 'atxH5', '##### foo', [ 'header', '##### foo' ] ); // H6 MT.testMode( 'atxH6', '###### foo', [ 'header', '###### foo' ] ); // H6 - 7x '#' should still be H6, per Dingus // http://daringfireball.net/projects/markdown/dingus MT.testMode( 'atxH6NotH7', '####### foo', [ 'header', '####### foo' ] ); // 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.testMode( 'setextH1', 'foo\n=', [ null, 'foo', 'header', '=' ] ); // Check if 3+ ='s work MT.testMode( 'setextH1', 'foo\n===', [ null, 'foo', 'header', '===' ] ); // Check if single underlining - works MT.testMode( 'setextH2', 'foo\n-', [ null, 'foo', 'header', '-' ] ); // Check if 3+ -'s work MT.testMode( 'setextH2', 'foo\n---', [ null, 'foo', 'header', '---' ] ); // Single-line blockquote with trailing space MT.testMode( 'blockquoteSpace', '> foo', [ 'quote', '> foo' ] ); // Single-line blockquote MT.testMode( 'blockquoteNoSpace', '>foo', [ 'quote', '>foo' ] ); // Single-line blockquote followed by normal paragraph MT.testMode( 'blockquoteThenParagraph', '>foo\n\nbar', [ 'quote', '>foo', null, 'bar' ] ); // Multi-line blockquote (lazy mode) MT.testMode( 'multiBlockquoteLazy', '>foo\nbar', [ 'quote', '>foo', 'quote', 'bar' ] ); // Multi-line blockquote followed by normal paragraph (lazy mode) MT.testMode( 'multiBlockquoteLazyThenParagraph', '>foo\nbar\n\nhello', [ 'quote', '>foo', 'quote', 'bar', null, 'hello' ] ); // Multi-line blockquote (non-lazy mode) MT.testMode( 'multiBlockquote', '>foo\n>bar', [ 'quote', '>foo', 'quote', '>bar' ] ); // Multi-line blockquote followed by normal paragraph (non-lazy mode) MT.testMode( 'multiBlockquoteThenParagraph', '>foo\n>bar\n\nhello', [ 'quote', '>foo', 'quote', '>bar', null, 'hello' ] ); // Check list types MT.testMode( 'listAsterisk', '* foo\n* bar', [ 'string', '* foo', 'string', '* bar' ] ); MT.testMode( 'listPlus', '+ foo\n+ bar', [ 'string', '+ foo', 'string', '+ bar' ] ); MT.testMode( 'listDash', '- foo\n- bar', [ 'string', '- foo', 'string', '- bar' ] ); MT.testMode( 'listNumber', '1. foo\n2. bar', [ 'string', '1. foo', 'string', '2. bar' ] ); // Formatting in lists (*) MT.testMode( 'listAsteriskFormatting', '* *foo* bar\n\n* **foo** bar\n\n* ***foo*** bar\n\n* `foo` bar', [ 'string', '* ', 'string em', '*foo*', 'string', ' bar', 'string', '* ', 'string strong', '**foo**', 'string', ' bar', 'string', '* ', 'string strong', '**', 'string emstrong', '*foo**', 'string em', '*', 'string', ' bar', 'string', '* ', 'string comment', '`foo`', 'string', ' bar' ] ); // Formatting in lists (+) MT.testMode( 'listPlusFormatting', '+ *foo* bar\n\n+ **foo** bar\n\n+ ***foo*** bar\n\n+ `foo` bar', [ 'string', '+ ', 'string em', '*foo*', 'string', ' bar', 'string', '+ ', 'string strong', '**foo**', 'string', ' bar', 'string', '+ ', 'string strong', '**', 'string emstrong', '*foo**', 'string em', '*', 'string', ' bar', 'string', '+ ', 'string comment', '`foo`', 'string', ' bar' ] ); // Formatting in lists (-) MT.testMode( 'listDashFormatting', '- *foo* bar\n\n- **foo** bar\n\n- ***foo*** bar\n\n- `foo` bar', [ 'string', '- ', 'string em', '*foo*', 'string', ' bar', 'string', '- ', 'string strong', '**foo**', 'string', ' bar', 'string', '- ', 'string strong', '**', 'string emstrong', '*foo**', 'string em', '*', 'string', ' bar', 'string', '- ', 'string comment', '`foo`', 'string', ' bar' ] ); // Formatting in lists (1.) MT.testMode( 'listNumberFormatting', '1. *foo* bar\n\n2. **foo** bar\n\n3. ***foo*** bar\n\n4. `foo` bar', [ 'string', '1. ', 'string em', '*foo*', 'string', ' bar', 'string', '2. ', 'string strong', '**foo**', 'string', ' bar', 'string', '3. ', 'string strong', '**', 'string emstrong', '*foo**', 'string em', '*', 'string', ' bar', 'string', '4. ', 'string comment', '`foo`', 'string', ' bar' ] ); // Paragraph lists MT.testMode( 'listParagraph', '* foo\n\n* bar', [ 'string', '* foo', 'string', '* bar' ] ); // Multi-paragraph lists // // 4 spaces MT.testMode( 'listMultiParagraph', '* foo\n\n* bar\n\n hello', [ 'string', '* foo', 'string', '* bar', null, ' ', 'string', 'hello' ] ); // 4 spaces, extra blank lines (should still be list, per Dingus) MT.testMode( 'listMultiParagraphExtra', '* foo\n\n* bar\n\n\n hello', [ 'string', '* foo', 'string', '* bar', null, ' ', 'string', 'hello' ] ); // 4 spaces, plus 1 space (should still be list, per Dingus) MT.testMode( 'listMultiParagraphExtraSpace', '* foo\n\n* bar\n\n hello\n\n world', [ 'string', '* foo', 'string', '* bar', null, ' ', 'string', 'hello', null, ' ', 'string', 'world' ] ); // 1 tab MT.testMode( 'listTab', '* foo\n\n* bar\n\n\thello', [ 'string', '* foo', 'string', '* bar', null, '\t', 'string', 'hello' ] ); // No indent MT.testMode( 'listNoIndent', '* foo\n\n* bar\n\nhello', [ 'string', '* foo', 'string', '* bar', null, 'hello' ] ); // Blockquote MT.testMode( 'blockquote', '* foo\n\n* bar\n\n > hello', [ 'string', '* foo', 'string', '* bar', null, ' ', 'string quote', '> hello' ] ); // Code block MT.testMode( 'blockquoteCode', '* foo\n\n* bar\n\n > hello\n\n world', [ 'string', '* foo', 'string', '* bar', null, ' ', 'comment', '> hello', null, ' ', 'string', 'world' ] ); // Code block followed by text MT.testMode( 'blockquoteCodeText', '* foo\n\n bar\n\n hello\n\n world', [ 'string', '* foo', null, ' ', 'string', 'bar', null, ' ', 'comment', 'hello', null, ' ', 'string', 'world' ] ); // Nested list // // * MT.testMode( 'listAsteriskNested', '* foo\n\n * bar', [ 'string', '* foo', null, ' ', 'string', '* bar' ] ); // + MT.testMode( 'listPlusNested', '+ foo\n\n + bar', [ 'string', '+ foo', null, ' ', 'string', '+ bar' ] ); // - MT.testMode( 'listDashNested', '- foo\n\n - bar', [ 'string', '- foo', null, ' ', 'string', '- bar' ] ); // 1. MT.testMode( 'listNumberNested', '1. foo\n\n 2. bar', [ 'string', '1. foo', null, ' ', 'string', '2. bar' ] ); // Mixed MT.testMode( 'listMixed', '* foo\n\n + bar\n\n - hello\n\n 1. world', [ 'string', '* foo', null, ' ', 'string', '+ bar', null, ' ', 'string', '- hello', null, ' ', 'string', '1. world' ] ); // Blockquote MT.testMode( 'listBlockquote', '* foo\n\n + bar\n\n > hello', [ 'string', '* foo', null, ' ', 'string', '+ bar', null, ' ', 'quote string', '> hello' ] ); // Code MT.testMode( 'listCode', '* foo\n\n + bar\n\n hello', [ 'string', '* foo', null, ' ', 'string', '+ bar', null, ' ', 'comment', 'hello' ] ); // Code with internal indentation MT.testMode( 'listCodeIndentation', '* foo\n\n bar\n hello\n world\n foo\n bar', [ 'string', '* foo', null, ' ', 'comment', 'bar', null, ' ', 'comment', 'hello', null, ' ', 'comment', 'world', null, ' ', 'comment', 'foo', null, ' ', 'string', 'bar' ] ); // Code followed by text MT.testMode( 'listCodeText', '* foo\n\n bar\n\nhello', [ 'string', '* foo', null, ' ', 'comment', 'bar', null, 'hello' ] ); // Following tests directly from official Markdown documentation // http://daringfireball.net/projects/markdown/syntax#hr MT.testMode( 'hrSpace', '* * *', [ 'hr', '* * *' ] ); MT.testMode( 'hr', '***', [ 'hr', '***' ] ); MT.testMode( 'hrLong', '*****', [ 'hr', '*****' ] ); MT.testMode( 'hrSpaceDash', '- - -', [ 'hr', '- - -' ] ); MT.testMode( 'hrDashLong', '---------------------------------------', [ 'hr', '---------------------------------------' ] ); // Inline link with title MT.testMode( 'linkTitle', '[foo](http://example.com/ "bar") hello', [ 'link', '[foo]', 'string', '(http://example.com/ "bar")', null, ' hello' ] ); // Inline link without title MT.testMode( 'linkNoTitle', '[foo](http://example.com/) bar', [ 'link', '[foo]', 'string', '(http://example.com/)', null, ' bar' ] ); // Inline link with Em MT.testMode( 'linkEm', '[*foo*](http://example.com/) bar', [ 'link', '[', 'link em', '*foo*', 'link', ']', 'string', '(http://example.com/)', null, ' bar' ] ); // Inline link with Strong MT.testMode( 'linkStrong', '[**foo**](http://example.com/) bar', [ 'link', '[', 'link strong', '**foo**', 'link', ']', 'string', '(http://example.com/)', null, ' bar' ] ); // Inline link with EmStrong MT.testMode( 'linkEmStrong', '[***foo***](http://example.com/) bar', [ 'link', '[', 'link strong', '**', 'link emstrong', '*foo**', 'link em', '*', 'link', ']', 'string', '(http://example.com/)', null, ' bar' ] ); // Image with title MT.testMode( 'imageTitle', '![foo](http://example.com/ "bar") hello', [ 'tag', '![foo]', 'string', '(http://example.com/ "bar")', null, ' hello' ] ); // Image without title MT.testMode( 'imageNoTitle', '![foo](http://example.com/) bar', [ 'tag', '![foo]', 'string', '(http://example.com/)', null, ' bar' ] ); // Image with asterisks MT.testMode( 'imageAsterisks', '![*foo*](http://example.com/) bar', [ 'tag', '![*foo*]', 'string', '(http://example.com/)', null, ' 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.testMode( 'notALink', '[foo] (bar)', [ null, '[foo] (bar)' ] ); // Reference-style links MT.testMode( 'linkReference', '[foo][bar] hello', [ 'link', '[foo]', 'string', '[bar]', null, ' hello' ] ); // Reference-style links with Em MT.testMode( 'linkReferenceEm', '[*foo*][bar] hello', [ 'link', '[', 'link em', '*foo*', 'link', ']', 'string', '[bar]', null, ' hello' ] ); // Reference-style links with Strong MT.testMode( 'linkReferenceStrong', '[**foo**][bar] hello', [ 'link', '[', 'link strong', '**foo**', 'link', ']', 'string', '[bar]', null, ' hello' ] ); // Reference-style links with EmStrong MT.testMode( 'linkReferenceEmStrong', '[***foo***][bar] hello', [ 'link', '[', 'link strong', '**', 'link emstrong', '*foo**', 'link em', '*', 'link', ']', 'string', '[bar]', null, ' hello' ] ); // Reference-style links with optional space separator (per docuentation) // "You can optionally use a space to separate the sets of brackets" MT.testMode( 'linkReferenceSpace', '[foo] [bar] hello', [ 'link', '[foo]', null, ' ', 'string', '[bar]', null, ' hello' ] ); // Should only allow a single space ("...use *a* space...") MT.testMode( 'linkReferenceDoubleSpace', '[foo] [bar] hello', [ null, '[foo] [bar] hello' ] ); // Reference-style links with implicit link name MT.testMode( 'linkImplicit', '[foo][] hello', [ 'link', '[foo]', 'string', '[]', null, ' 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) // // No title MT.testMode( 'labelNoTitle', '[foo]: http://example.com/', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/' ] ); // Space in ID and title MT.testMode( 'labelSpaceTitle', '[foo bar]: http://example.com/ "hello"', [ 'link', '[foo bar]:', null, ' ', 'string', 'http://example.com/ "hello"' ] ); // Double title MT.testMode( 'labelDoubleTitle', '[foo bar]: http://example.com/ "hello" "world"', [ 'link', '[foo bar]:', null, ' ', 'string', 'http://example.com/ "hello"', null, ' "world"' ] ); // Double quotes around title MT.testMode( 'labelTitleDoubleQuotes', '[foo]: http://example.com/ "bar"', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/ "bar"' ] ); // Single quotes around title MT.testMode( 'labelTitleSingleQuotes', '[foo]: http://example.com/ \'bar\'', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/ \'bar\'' ] ); // Parentheses around title MT.testMode( 'labelTitleParenthese', '[foo]: http://example.com/ (bar)', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/ (bar)' ] ); // Invalid title MT.testMode( 'labelTitleInvalid', '[foo]: http://example.com/ bar', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/', null, ' bar' ] ); // Angle brackets around URL MT.testMode( 'labelLinkAngleBrackets', '[foo]: "bar"', [ 'link', '[foo]:', null, ' ', 'string', ' "bar"' ] ); // Title on next line per documentation (double quotes) MT.testMode( 'labelTitleNextDoubleQuotes', '[foo]: http://example.com/\n"bar" hello', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/', 'string', '"bar"', null, ' hello' ] ); // Title on next line per documentation (single quotes) MT.testMode( 'labelTitleNextSingleQuotes', '[foo]: http://example.com/\n\'bar\' hello', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/', 'string', '\'bar\'', null, ' hello' ] ); // Title on next line per documentation (parentheses) MT.testMode( 'labelTitleNextParenthese', '[foo]: http://example.com/\n(bar) hello', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/', 'string', '(bar)', null, ' hello' ] ); // Title on next line per documentation (mixed) MT.testMode( 'labelTitleNextMixed', '[foo]: http://example.com/\n(bar" hello', [ 'link', '[foo]:', null, ' ', 'string', 'http://example.com/', null, '(bar" hello' ] ); // Automatic links MT.testMode( 'linkWeb', ' foo', [ 'link', '', null, ' foo' ] ); // Automatic email links MT.testMode( 'linkEmail', ' foo', [ 'link', '', null, ' foo' ] ); // Single asterisk MT.testMode( 'emAsterisk', '*foo* bar', [ 'em', '*foo*', null, ' bar' ] ); // Single underscore MT.testMode( 'emUnderscore', '_foo_ bar', [ 'em', '_foo_', null, ' bar' ] ); // Emphasis characters within a word MT.testMode( 'emInWordAsterisk', 'foo*bar*hello', [ null, 'foo', 'em', '*bar*', null, 'hello' ] ); MT.testMode( 'emInWordUnderscore', 'foo_bar_hello', [ null, 'foo', 'em', '_bar_', null, 'hello' ] ); // Per documentation: "...surround an * or _ with spaces, it’ll be // treated as a literal asterisk or underscore." // // Inside EM MT.testMode( 'emEscapedBySpaceIn', 'foo _bar _ hello_ world', [ null, 'foo ', 'em', '_bar _ hello_', null, ' world' ] ); // Outside EM MT.testMode( 'emEscapedBySpaceOut', 'foo _ bar_hello_world', [ null, 'foo _ bar', 'em', '_hello_', null, '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.testMode( 'emIncompleteAsterisk', 'foo *bar', [ null, 'foo ', 'em', '*bar' ] ); MT.testMode( 'emIncompleteUnderscore', 'foo _bar', [ null, 'foo ', 'em', '_bar' ] ); // Double asterisk MT.testMode( 'strongAsterisk', '**foo** bar', [ 'strong', '**foo**', null, ' bar' ] ); // Double underscore MT.testMode( 'strongUnderscore', '__foo__ bar', [ 'strong', '__foo__', null, ' bar' ] ); // Triple asterisk MT.testMode( 'emStrongAsterisk', '*foo**bar*hello** world', [ 'em', '*foo', 'emstrong', '**bar*', 'strong', 'hello**', null, ' world' ] ); // Triple underscore MT.testMode( 'emStrongUnderscore', '_foo__bar_hello__ world', [ 'em', '_foo', 'emstrong', '__bar_', 'strong', 'hello__', null, ' world' ] ); // Triple mixed // "...same character must be used to open and close an emphasis span."" MT.testMode( 'emStrongMixed', '_foo**bar*hello__ world', [ 'em', '_foo', 'emstrong', '**bar*hello__ world' ] ); MT.testMode( 'emStrongMixed', '*foo__bar_hello** world', [ 'em', '*foo', 'emstrong', '__bar_hello** world' ] ); // These characters should be escaped: // \ backslash // ` backtick // * asterisk // _ underscore // {} curly braces // [] square brackets // () parentheses // # hash mark // + plus sign // - minus sign (hyphen) // . dot // ! exclamation mark // // Backtick (code) MT.testMode( 'escapeBacktick', 'foo \\`bar\\`', [ null, 'foo \\`bar\\`' ] ); MT.testMode( 'doubleEscapeBacktick', 'foo \\\\`bar\\\\`', [ null, 'foo \\\\', 'comment', '`bar\\\\`' ] ); // Asterisk (em) MT.testMode( 'escapeAsterisk', 'foo \\*bar\\*', [ null, 'foo \\*bar\\*' ] ); MT.testMode( 'doubleEscapeAsterisk', 'foo \\\\*bar\\\\*', [ null, 'foo \\\\', 'em', '*bar\\\\*' ] ); // Underscore (em) MT.testMode( 'escapeUnderscore', 'foo \\_bar\\_', [ null, 'foo \\_bar\\_' ] ); MT.testMode( 'doubleEscapeUnderscore', 'foo \\\\_bar\\\\_', [ null, 'foo \\\\', 'em', '_bar\\\\_' ] ); // Hash mark (headers) MT.testMode( 'escapeHash', '\\# foo', [ null, '\\# foo' ] ); MT.testMode( 'doubleEscapeHash', '\\\\# foo', [ null, '\\\\# foo' ] );