2013-06-20 13:58:54 -04:00
/*global sanitizeHtml:true */
2013-06-18 18:03:36 -04:00
2013-06-21 11:36:33 -04:00
module ( "Discourse.Markdown" , {
setup : function ( ) {
Discourse . SiteSettings . traditional _markdown _linebreaks = false ;
}
} ) ;
2013-06-18 18:03:36 -04:00
var cooked = function ( input , expected , text ) {
2013-08-08 18:14:12 -04:00
var result = Discourse . Markdown . cook ( input , { mentionLookup : false , sanitize : true } ) ;
equal ( result , expected , text ) ;
2013-06-18 18:03:36 -04:00
} ;
var cookedOptions = function ( input , opts , expected , text ) {
equal ( Discourse . Markdown . cook ( input , opts ) , expected , text ) ;
2013-06-21 14:06:20 -04:00
} ;
2013-06-18 18:03:36 -04:00
test ( "basic cooking" , function ( ) {
cooked ( "hello" , "<p>hello</p>" , "surrounds text with paragraphs" ) ;
2013-08-27 12:24:17 -04:00
cooked ( "**evil**" , "<p><strong>evil</strong></p>" , "it bolds text." ) ;
2013-08-28 11:14:06 -04:00
cooked ( "__bold__" , "<p><strong>bold</strong></p>" , "it bolds text." ) ;
2013-08-27 12:24:17 -04:00
cooked ( "*trout*" , "<p><em>trout</em></p>" , "it italicizes text." ) ;
cooked ( "_trout_" , "<p><em>trout</em></p>" , "it italicizes text." ) ;
2013-08-24 13:06:07 -04:00
cooked ( "***hello***" , "<p><strong><em>hello</em></strong></p>" , "it can do bold and italics at once." ) ;
2013-08-27 12:24:17 -04:00
cooked ( "word_with_underscores" , "<p>word_with_underscores</p>" , "it doesn't do intraword italics" ) ;
2013-08-30 10:56:41 -04:00
cooked ( "common/_special_font_face.html.erb" , "<p>common/_special_font_face.html.erb</p>" , "it doesn't intraword with a slash" ) ;
2013-08-27 12:24:17 -04:00
cooked ( "hello \\*evil\\*" , "<p>hello *evil*</p>" , "it supports escaping of asterisks" ) ;
cooked ( "hello \\_evil\\_" , "<p>hello _evil_</p>" , "it supports escaping of italics" ) ;
2013-08-28 11:14:06 -04:00
cooked ( "brussel sproutes are *awful*." , "<p>brussel sproutes are <em>awful</em>.</p>" , "it doesn't swallow periods." ) ;
2013-06-21 11:36:33 -04:00
} ) ;
2013-08-26 15:21:23 -04:00
test ( "Traditional Line Breaks" , function ( ) {
2013-06-21 11:36:33 -04:00
var input = "1\n2\n3" ;
2013-08-08 18:14:12 -04:00
cooked ( input , "<p>1<br>2<br>3</p>" , "automatically handles trivial newlines" ) ;
2013-06-21 11:36:33 -04:00
var traditionalOutput = "<p>1\n2\n3</p>" ;
cookedOptions ( input ,
{ traditional _markdown _linebreaks : true } ,
traditionalOutput ,
2013-06-21 14:06:20 -04:00
"It supports traditional markdown via an option" ) ;
2013-06-21 11:36:33 -04:00
Discourse . SiteSettings . traditional _markdown _linebreaks = true ;
2013-06-21 14:06:20 -04:00
cooked ( input , traditionalOutput , "It supports traditional markdown via a Site Setting" ) ;
2013-08-26 15:21:23 -04:00
} ) ;
2013-06-21 11:36:33 -04:00
2013-08-26 15:21:23 -04:00
test ( "Line Breaks" , function ( ) {
cooked ( "[] first choice\n[] second choice" ,
"<p>[] first choice<br>[] second choice</p>" ,
"it handles new lines correctly with [] options" ) ;
2013-06-18 18:03:36 -04:00
} ) ;
test ( "Links" , function ( ) {
2013-08-08 18:14:12 -04:00
2013-08-22 18:03:01 -04:00
cooked ( "EvilTrout: http://eviltrout.com" ,
'<p>EvilTrout: <a href="http://eviltrout.com">http://eviltrout.com</a></p>' ,
"autolinks a URL" ) ;
2013-06-18 18:03:36 -04:00
cooked ( "Youtube: http://www.youtube.com/watch?v=1MrpeBRkM5A" ,
'<p>Youtube: <a href="http://www.youtube.com/watch?v=1MrpeBRkM5A">http://www.youtube.com/watch?v=1MrpeBRkM5A</a></p>' ,
"allows links to contain query params" ) ;
cooked ( "Derpy: http://derp.com?__test=1" ,
2013-08-08 18:14:12 -04:00
'<p>Derpy: <a href="http://derp.com?__test=1">http://derp.com?__test=1</a></p>' ,
"works with double underscores in urls" ) ;
cooked ( "Derpy: http://derp.com?_test_=1" ,
'<p>Derpy: <a href="http://derp.com?_test_=1">http://derp.com?_test_=1</a></p>' ,
"works with underscores in urls" ) ;
2013-06-18 18:03:36 -04:00
cooked ( "Atwood: www.codinghorror.com" ,
'<p>Atwood: <a href="http://www.codinghorror.com">www.codinghorror.com</a></p>' ,
"autolinks something that begins with www" ) ;
cooked ( "Atwood: http://www.codinghorror.com" ,
'<p>Atwood: <a href="http://www.codinghorror.com">http://www.codinghorror.com</a></p>' ,
"autolinks a URL with http://www" ) ;
2013-08-21 22:49:55 -04:00
cooked ( "EvilTrout: http://eviltrout.com hello" ,
'<p>EvilTrout: <a href="http://eviltrout.com">http://eviltrout.com</a> hello</p>' ,
"autolinks with trailing text" ) ;
2013-06-18 18:03:36 -04:00
cooked ( "here is [an example](http://twitter.com)" ,
'<p>here is <a href="http://twitter.com">an example</a></p>' ,
"supports markdown style links" ) ;
cooked ( "Batman: http://en.wikipedia.org/wiki/The_Dark_Knight_(film)" ,
'<p>Batman: <a href="http://en.wikipedia.org/wiki/The_Dark_Knight_(film)">http://en.wikipedia.org/wiki/The_Dark_Knight_(film)</a></p>' ,
"autolinks a URL with parentheses (like Wikipedia)" ) ;
2013-08-08 18:14:12 -04:00
cooked ( "Here's a tweet:\nhttps://twitter.com/evil_trout/status/345954894420787200" ,
"<p>Here's a tweet:<br><a href=\"https://twitter.com/evil_trout/status/345954894420787200\" class=\"onebox\">https://twitter.com/evil_trout/status/345954894420787200</a></p>" ,
"It doesn't strip the new line." ) ;
2013-08-26 15:21:23 -04:00
cooked ( "1. View @eviltrout's profile here: http://meta.discourse.org/users/eviltrout/activity<br>next line." ,
2013-08-22 18:03:01 -04:00
"<ol><li>View <span class=\"mention\">@eviltrout</span>'s profile here: <a href=\"http://meta.discourse.org/users/eviltrout/activity\">http://meta.discourse.org/users/eviltrout/activity</a><br>next line.</li></ol>" ,
"allows autolinking within a list without inserting a paragraph." ) ;
2013-08-08 18:14:12 -04:00
cooked ( "[3]: http://eviltrout.com" , "" , "It doesn't autolink markdown link references" ) ;
cooked ( "http://discourse.org and http://discourse.org/another_url and http://www.imdb.com/name/nm2225369" ,
"<p><a href=\"http://discourse.org\">http://discourse.org</a> and " +
"<a href=\"http://discourse.org/another_url\">http://discourse.org/another_url</a> and " +
"<a href=\"http://www.imdb.com/name/nm2225369\">http://www.imdb.com/name/nm2225369</a></p>" ,
'allows multiple links on one line' ) ;
2013-08-28 13:06:41 -04:00
cooked ( "* [Evil Trout][1]\n [1]: http://eviltrout.com" ,
"<ul><li><a href=\"http://eviltrout.com\">Evil Trout</a><br></li></ul>" ,
"allows markdown link references in a list" ) ;
2013-06-18 18:03:36 -04:00
} ) ;
2013-08-29 15:18:27 -04:00
test ( "simple quotes" , function ( ) {
cooked ( "> nice!" , "<blockquote><p>nice!</p></blockquote>" , "it supports simple quotes" ) ;
cooked ( " > nice!" , "<blockquote><p>nice!</p></blockquote>" , "it allows quotes with preceeding spaces" ) ;
cooked ( "> level 1\n> > level 2" ,
"<blockquote><p>level 1</p><blockquote><p>level 2</p></blockquote></blockquote>" ,
"it allows nesting of blockquotes" ) ;
cooked ( "> level 1\n> > level 2" ,
"<blockquote><p>level 1</p><blockquote><p>level 2</p></blockquote></blockquote>" ,
"it allows nesting of blockquotes with spaces" ) ;
2013-09-05 17:03:35 -04:00
cooked ( "- hello\n\n > world\n > eviltrout" ,
"<ul><li>hello</li></ul>\n\n<blockquote><p>world<br>eviltrout</p></blockquote>" ,
"it allows quotes within a list." ) ;
cooked ( " > indent 1\n > indent 2" , "<blockquote><p>indent 1<br>indent 2</p></blockquote>" , "allow multiple spaces to indent" ) ;
2013-08-29 15:18:27 -04:00
} ) ;
2013-06-18 18:03:36 -04:00
test ( "Quotes" , function ( ) {
2013-08-21 22:36:02 -04:00
cookedOptions ( "[quote=\"eviltrout, post: 1\"]\na quote\n\nsecond line\n[/quote]" ,
{ topicId : 2 } ,
2013-08-26 15:21:23 -04:00
"<p><aside class=\"quote\" data-post=\"1\"><div class=\"title\"><div class=\"quote-controls\"></div>eviltrout said:</div><blockquote>" +
2013-08-29 11:38:51 -04:00
"a quote<br/><br/>second line</blockquote></aside></p>" ,
2013-08-21 22:36:02 -04:00
"works with multiple lines" ) ;
2013-06-18 18:03:36 -04:00
cookedOptions ( "1[quote=\"bob, post:1\"]my quote[/quote]2" ,
{ topicId : 2 , lookupAvatar : function ( name ) { return "" + name ; } } ,
2013-08-26 15:21:23 -04:00
"<p>1</p>\n\n<p><aside class=\"quote\" data-post=\"1\"><div class=\"title\"><div class=\"quote-controls\"></div>bob" +
2013-08-21 22:36:02 -04:00
"bob said:</div><blockquote>my quote</blockquote></aside></p>\n\n<p>2</p>" ,
2013-06-18 18:03:36 -04:00
"handles quotes properly" ) ;
cookedOptions ( "1[quote=\"bob, post:1\"]my quote[/quote]2" ,
{ topicId : 2 , lookupAvatar : function ( name ) { } } ,
2013-08-21 22:36:02 -04:00
"<p>1</p>\n\n<p><aside class=\"quote\" data-post=\"1\"><div class=\"title\"><div class=\"quote-controls\"></div>bob said:" +
"</div><blockquote>my quote</blockquote></aside></p>\n\n<p>2</p>" ,
2013-06-18 18:03:36 -04:00
"includes no avatar if none is found" ) ;
2013-06-26 18:41:48 -04:00
} ) ;
2013-06-18 18:03:36 -04:00
test ( "Mentions" , function ( ) {
2013-08-24 15:00:18 -04:00
var alwaysTrue = { mentionLookup : ( function ( ) { return true ; } ) } ;
cookedOptions ( "Hello @sam" , alwaysTrue ,
2013-08-08 18:14:12 -04:00
"<p>Hello <a class=\"mention\" href=\"/users/sam\">@sam</a></p>" ,
2013-06-18 18:03:36 -04:00
"translates mentions to links" ) ;
2013-08-08 18:14:12 -04:00
cooked ( "Hello @EvilTrout" , "<p>Hello <span class=\"mention\">@EvilTrout</span></p>" , "adds a mention class" ) ;
2013-06-18 18:03:36 -04:00
cooked ( "robin@email.host" , "<p>robin@email.host</p>" , "won't add mention class to an email address" ) ;
cooked ( "hanzo55@yahoo.com" , "<p>hanzo55@yahoo.com</p>" , "won't be affected by email addresses that have a number before the @ symbol" ) ;
2013-08-08 18:14:12 -04:00
cooked ( "@EvilTrout yo" , "<p><span class=\"mention\">@EvilTrout</span> yo</p>" , "it handles mentions at the beginning of a string" ) ;
cooked ( "yo\n@EvilTrout" , "<p>yo<br><span class=\"mention\">@EvilTrout</span></p>" , "it handles mentions at the beginning of a new line" ) ;
2013-06-18 18:03:36 -04:00
cooked ( "`evil` @EvilTrout `trout`" ,
2013-08-08 18:14:12 -04:00
"<p><code>evil</code> <span class=\"mention\">@EvilTrout</span> <code>trout</code></p>" ,
2013-06-18 18:03:36 -04:00
"deals correctly with multiple <code> blocks" ) ;
2013-08-08 18:14:12 -04:00
cooked ( "```\na @test\n```" , "<p><pre><code class=\"lang-auto\">a @test</code></pre></p>" , "should not do mentions within a code block." ) ;
2013-06-18 18:03:36 -04:00
2013-08-21 16:10:16 -04:00
cooked ( "> foo bar baz @eviltrout" ,
"<blockquote><p>foo bar baz <span class=\"mention\">@eviltrout</span></p></blockquote>" ,
"handles mentions in simple quotes" ) ;
cooked ( "> foo bar baz @eviltrout ohmagerd\nlook at this" ,
2013-08-26 15:21:23 -04:00
"<blockquote><p>foo bar baz <span class=\"mention\">@eviltrout</span> ohmagerd<br>look at this</p></blockquote>" ,
2013-08-21 16:10:16 -04:00
"does mentions properly with trailing text within a simple quote" ) ;
2013-08-22 12:18:03 -04:00
cooked ( "`code` is okay before @mention" ,
"<p><code>code</code> is okay before <span class=\"mention\">@mention</span></p>" ,
"Does not mention in an inline code block" ) ;
cooked ( "@mention is okay before `code`" ,
"<p><span class=\"mention\">@mention</span> is okay before <code>code</code></p>" ,
"Does not mention in an inline code block" ) ;
cooked ( "don't `@mention`" ,
"<p>don't <code>@mention</code></p>" ,
"Does not mention in an inline code block" ) ;
2013-08-22 15:54:41 -04:00
cooked ( "Yes `@this` should be code @eviltrout" ,
"<p>Yes <code>@this</code> should be code <span class=\"mention\">@eviltrout</span></p>" ,
"Does not mention in an inline code block" ) ;
2013-08-22 16:13:02 -04:00
cooked ( "@eviltrout and `@eviltrout`" ,
"<p><span class=\"mention\">@eviltrout</span> and <code>@eviltrout</code></p>" ,
"you can have a mention in an inline code block following a real mention." ) ;
2013-08-22 17:50:07 -04:00
cooked ( "1. this is a list\n\n2. this is an @eviltrout mention\n" ,
2013-08-26 15:21:23 -04:00
"<ol><li><p>this is a list</p></li><li><p>this is an <span class=\"mention\">@eviltrout</span> mention</p></li></ol>" ,
2013-08-22 17:50:07 -04:00
"it mentions properly in a list." ) ;
2013-08-24 15:00:18 -04:00
cookedOptions ( "@eviltrout" , alwaysTrue ,
"<p><a class=\"mention\" href=\"/users/eviltrout\">@eviltrout</a></p>" ,
"it doesn't onebox mentions" ) ;
2013-06-18 18:03:36 -04:00
} ) ;
test ( "Oneboxing" , function ( ) {
var matches = function ( input , regexp ) {
return Discourse . Markdown . cook ( input , { mentionLookup : false } ) . match ( regexp ) ;
} ;
ok ( ! matches ( "- http://www.textfiles.com/bbs/MINDVOX/FORUMS/ethics\n\n- http://drupal.org" , /onebox/ ) ,
2013-08-08 18:14:12 -04:00
"doesn't onebox a link within a list" ) ;
2013-06-18 18:03:36 -04:00
ok ( matches ( "http://test.com" , /onebox/ ) , "adds a onebox class to a link on its own line" ) ;
ok ( matches ( "http://test.com\nhttp://test2.com" , /onebox[\s\S]+onebox/m ) , "supports multiple links" ) ;
ok ( ! matches ( "http://test.com bob" , /onebox/ ) , "doesn't onebox links that have trailing text" ) ;
2013-09-06 16:46:55 -04:00
ok ( ! matches ( "[Tom Cruise](http://www.tomcruise.com/)" , "onebox" ) , "Markdown links with labels are not oneboxed" ) ;
ok ( matches ( "[http://www.tomcruise.com/](http://www.tomcruise.com/)" ,
"onebox" ) ,
"Markdown links where the label is the same as the url are oneboxed" ) ;
2013-06-18 18:03:36 -04:00
cooked ( "http://en.wikipedia.org/wiki/Homicide:_Life_on_the_Street" ,
2013-08-08 18:14:12 -04:00
"<p><a href=\"http://en.wikipedia.org/wiki/Homicide:_Life_on_the_Street\" class=\"onebox\"" +
2013-06-18 18:03:36 -04:00
">http://en.wikipedia.org/wiki/Homicide:_Life_on_the_Street</a></p>" ,
"works with links that have underscores in them" ) ;
} ) ;
2013-08-08 18:14:12 -04:00
test ( "Code Blocks" , function ( ) {
2013-08-22 13:46:51 -04:00
cooked ( "```\na\nb\nc\n\nd\n```" ,
"<p><pre><code class=\"lang-auto\">a\nb\nc\n\nd</code></pre></p>" ,
"it treats new lines properly" ) ;
2013-08-08 18:14:12 -04:00
cooked ( "```\ntest\n```" ,
"<p><pre><code class=\"lang-auto\">test</code></pre></p>" ,
"it supports basic code blocks" ) ;
cooked ( "```json\n{hello: 'world'}\n```\ntrailing" ,
2013-08-26 15:21:23 -04:00
"<p><pre><code class=\"json\">{hello: 'world'}</code></pre></p>\n\n<p>trailing</p>" ,
2013-08-08 18:14:12 -04:00
"It does not truncate text after a code block." ) ;
cooked ( "```json\nline 1\n\nline 2\n\n\nline3\n```" ,
"<p><pre><code class=\"json\">line 1\n\nline 2\n\n\nline3</code></pre></p>" ,
"it maintains new lines inside a code block." ) ;
cooked ( "hello\nworld\n```json\nline 1\n\nline 2\n\n\nline3\n```" ,
"<p>hello<br>world<br></p>\n\n<p><pre><code class=\"json\">line 1\n\nline 2\n\n\nline3</code></pre></p>" ,
"it maintains new lines inside a code block with leading content." ) ;
cooked ( "```text\n<header>hello</header>\n```" ,
"<p><pre><code class=\"text\"><header>hello</header></code></pre></p>" ,
"it escapes code in the code block" ) ;
cooked ( "```ruby\n# cool\n```" ,
"<p><pre><code class=\"ruby\"># cool</code></pre></p>" ,
"it supports changing the language" ) ;
cooked ( " ```\n hello\n ```" ,
"<pre><code>```\nhello\n```</code></pre>" ,
"only detect ``` at the begining of lines" ) ;
2013-08-24 13:24:27 -04:00
cooked ( "```ruby\ndef self.parse(text)\n\n text\nend\n```" ,
"<p><pre><code class=\"ruby\">def self.parse(text)\n\n text\nend</code></pre></p>" ,
"it allows leading spaces on lines in a code block." ) ;
2013-08-26 16:53:10 -04:00
cooked ( "```ruby\nhello `eviltrout`\n```" ,
"<p><pre><code class=\"ruby\">hello `eviltrout`</code></pre></p>" ,
"it allows code with backticks in it" ) ;
2013-08-29 14:42:31 -04:00
cooked ( "```[quote=\"sam, post:1, topic:9441, full:true\"]This is `<not>` a bug.[/quote]```" ,
"<p><pre><code class=\"lang-auto\">[quote="sam, post:1, topic:9441, full:true"]This is `<not>` a bug.[/quote]</code></pre></p>" ,
"it allows code with backticks in it" ) ;
2013-08-08 18:14:12 -04:00
} ) ;
2013-06-19 15:06:23 -04:00
test ( "SanitizeHTML" , function ( ) {
equal ( sanitizeHtml ( "<div><script>alert('hi');</script></div>" ) , "<div></div>" ) ;
equal ( sanitizeHtml ( "<div><p class=\"funky\" wrong='1'>hello</p></div>" ) , "<div><p class=\"funky\">hello</p></div>" ) ;
2013-08-08 18:14:12 -04:00
cooked ( "hello<script>alert(42)</script>" , "<p>hello</p>" , "it sanitizes while cooking" ) ;
cooked ( "<a href='http://disneyland.disney.go.com/'>disney</a> <a href='http://reddit.com'>reddit</a>" ,
"<p><a href=\"http://disneyland.disney.go.com/\">disney</a> <a href=\"http://reddit.com\">reddit</a></p>" ,
"we can embed proper links" ) ;
2013-06-19 15:06:23 -04:00
2013-06-24 16:25:08 -04:00
} ) ;
2013-06-26 18:41:48 -04:00
test ( "URLs in BBCode tags" , function ( ) {
cooked ( "[img]http://eviltrout.com/eviltrout.png[/img][img]http://samsaffron.com/samsaffron.png[/img]" ,
"<p><img src=\"http://eviltrout.com/eviltrout.png\"><img src=\"http://samsaffron.com/samsaffron.png\"></p>" ,
"images are properly parsed" ) ;
cooked ( "[url]http://discourse.org[/url]" ,
"<p><a href=\"http://discourse.org\">http://discourse.org</a></p>" ,
"links are properly parsed" ) ;
cooked ( "[url=http://discourse.org]discourse[/url]" ,
"<p><a href=\"http://discourse.org\">discourse</a></p>" ,
"named links are properly parsed" ) ;
} ) ;