2014-01-04 01:18:30 +01:00
/*global Dict */
2014-01-17 21:08:45 +01:00
var path = require ( 'path' ) ;
var fs = require ( 'fs' ) ;
2017-04-04 15:31:21 +02:00
set _global ( 'window' , {
location : {
origin : 'http://zulip.zulipdev.com' ,
} ,
} ) ;
2016-10-30 20:54:53 +01:00
set _global ( 'page_params' , {
2017-04-24 21:59:07 +02:00
realm _users : [ ] ,
2016-10-30 20:54:53 +01:00
realm _emoji : {
2016-12-28 05:07:10 +01:00
burrito : { display _url : '/static/generated/emoji/images/emoji/burrito.png' ,
source _url : '/static/generated/emoji/images/emoji/burrito.png' } ,
2016-10-30 20:54:53 +01:00
} ,
realm _filters : [
[
"#(?P<id>[0-9]{2,8})" ,
2016-12-03 23:17:57 +01:00
"https://trac.zulip.net/ticket/%(id)s" ,
2016-10-30 20:54:53 +01:00
] ,
[
"ZBUG_(?P<id>[0-9]{2,8})" ,
2016-12-03 23:17:57 +01:00
"https://trac2.zulip.net/ticket/%(id)s" ,
2016-10-30 20:54:53 +01:00
] ,
[
"ZGROUP_(?P<id>[0-9]{2,8}):(?P<zone>[0-9]{1,8})" ,
2016-12-03 23:17:57 +01:00
"https://zone_%(zone)s.zulip.net/ticket/%(id)s" ,
] ,
] ,
2016-10-30 20:54:53 +01:00
} ) ;
2014-01-24 22:52:37 +01:00
2017-06-14 11:19:00 +02:00
set _global ( 'blueslip' , { } ) ;
2013-12-04 17:16:08 +01:00
add _dependencies ( {
2014-01-04 01:18:30 +01:00
marked : 'third/marked/lib/marked.js' ,
2017-01-26 00:41:53 +01:00
emoji _codes : 'generated/emoji/emoji_codes.js' ,
2014-01-27 23:21:48 +01:00
emoji : 'js/emoji.js' ,
2014-01-30 22:42:19 +01:00
people : 'js/people.js' ,
2016-11-10 20:20:40 +01:00
stream _data : 'js/stream_data.js' ,
2017-03-19 00:43:14 +01:00
hash _util : 'js/hash_util' ,
2016-12-03 23:17:57 +01:00
fenced _code : 'js/fenced_code.js' ,
2017-03-20 16:56:39 +01:00
katex : 'node_modules/katex/dist/katex.min.js' ,
2017-03-20 18:54:00 +01:00
util : 'js/util.js' ,
2013-12-04 17:16:08 +01:00
} ) ;
2014-01-16 20:18:55 +01:00
var doc = "" ;
set _global ( 'document' , doc ) ;
2017-06-14 20:30:12 +02:00
set _global ( '$' , global . make _zjquery ( ) ) ;
2013-12-04 17:16:08 +01:00
2014-01-27 18:21:58 +01:00
set _global ( 'feature_flags' , { local _echo : true } ) ;
2016-10-30 20:58:28 +01:00
var people = global . people ;
2017-01-24 01:51:58 +01:00
var cordelia = {
2016-10-30 20:58:28 +01:00
full _name : 'Cordelia Lear' ,
user _id : 101 ,
2016-12-03 23:17:57 +01:00
email : 'cordelia@zulip.com' ,
2017-01-24 01:51:58 +01:00
} ;
people . add ( cordelia ) ;
2014-01-04 01:18:30 +01:00
2017-01-24 00:44:08 +01:00
people . add ( {
full _name : 'Leo' ,
user _id : 102 ,
email : 'leo@zulip.com' ,
} ) ;
2017-01-24 01:51:58 +01:00
people . initialize _current _user ( cordelia . user _id ) ;
2017-01-24 00:44:08 +01:00
2016-11-10 20:20:40 +01:00
var stream _data = global . stream _data ;
var denmark = {
subscribed : false ,
color : 'blue' ,
name : 'Denmark' ,
stream _id : 1 ,
2016-12-03 23:17:57 +01:00
in _home _view : false ,
2016-11-10 20:20:40 +01:00
} ;
var social = {
subscribed : true ,
color : 'red' ,
name : 'social' ,
stream _id : 2 ,
in _home _view : true ,
2016-12-03 23:17:57 +01:00
invite _only : true ,
2016-11-10 20:20:40 +01:00
} ;
stream _data . add _sub ( 'Denmark' , denmark ) ;
stream _data . add _sub ( 'social' , social ) ;
2017-06-15 23:48:29 +02:00
// Check the default behavior of fenced code blocks
// works properly before markdown is initialized.
( function test _fenced _block _defaults ( ) {
var input = '\n```\nfenced code\n```\n\nand then after\n' ;
var expected = '\n\n<div class="codehilite"><pre><span></span>fenced code\n</pre></div>\n\n\n\nand then after\n\n' ;
var output = fenced _code . process _fenced _code ( input ) ;
assert . equal ( output , expected ) ;
} ( ) ) ;
2017-05-09 18:01:43 +02:00
var markdown = require ( 'js/markdown.js' ) ;
markdown . initialize ( ) ;
2013-12-04 17:16:08 +01:00
2015-10-13 23:34:50 +02:00
var bugdown _data = JSON . parse ( fs . readFileSync ( path . join ( _ _dirname , '../../zerver/fixtures/bugdown-data.json' ) , 'utf8' , 'r' ) ) ;
2014-01-17 21:08:45 +01:00
2013-12-04 17:16:08 +01:00
( function test _bugdown _detection ( ) {
var no _markup = [
"This is a plaintext message" ,
"This is a plaintext: message" ,
"This is a :plaintext message" ,
"This is a :plaintext message: message" ,
"Contains a not an image.jpeg/ok file" ,
"Contains a not an http://www.google.com/ok/image.png/stop file" ,
"No png to be found here, a png" ,
"No user mention **leo**" ,
2014-01-03 22:51:00 +01:00
"No user mention @what there" ,
2014-01-04 00:21:06 +01:00
"We like to code\n~~~\ndef code():\n we = \"like to do\"\n~~~" ,
"This is a\nmultiline :emoji: here\n message" ,
2014-01-04 01:18:30 +01:00
"This is an :emoji: message" ,
"User Mention @**leo**" ,
"User Mention @**leo f**" ,
2016-10-19 16:49:53 +02:00
"User Mention @**leo with some name**" ,
2016-11-10 20:20:40 +01:00
"Stream #**Verona**" ,
2016-10-20 21:42:55 +02:00
"This contains !gravatar(leo@zulip.com)" ,
2016-12-03 23:17:57 +01:00
"And an avatar !avatar(leo@zulip.com) is here" ,
2013-12-04 17:16:08 +01:00
] ;
var markup = [
"Contains a https://zulip.com/image.png file" ,
"Contains a https://zulip.com/image.jpg file" ,
"https://zulip.com/image.jpg" ,
"also https://zulip.com/image.jpg" ,
"https://zulip.com/image.jpg too" ,
"Contains a zulip.com/foo.jpeg file" ,
"Contains a https://zulip.com/image.png file" ,
"twitter url https://twitter.com/jacobian/status/407886996565016579" ,
"https://twitter.com/jacobian/status/407886996565016579" ,
"then https://twitter.com/jacobian/status/407886996565016579" ,
"twitter url http://twitter.com/jacobian/status/407886996565016579" ,
2016-12-03 23:17:57 +01:00
"youtube url https://www.youtube.com/watch?v=HHZ8iqswiCw&feature=youtu.be&a" ,
2013-12-04 17:16:08 +01:00
] ;
no _markup . forEach ( function ( content ) {
2017-05-09 18:01:43 +02:00
assert . equal ( markdown . contains _bugdown ( content ) , false ) ;
2013-12-04 17:16:08 +01:00
} ) ;
markup . forEach ( function ( content ) {
2017-05-09 18:01:43 +02:00
assert . equal ( markdown . contains _bugdown ( content ) , true ) ;
2013-12-04 17:16:08 +01:00
} ) ;
} ( ) ) ;
2014-01-17 21:08:45 +01:00
( function test _marked _shared ( ) {
2017-06-13 23:49:50 +02:00
var tests = bugdown _data . regular _tests ;
tests . forEach ( function ( test ) {
var message = { raw _content : test . input } ;
markdown . apply _markdown ( message ) ;
var output = message . content ;
if ( test . bugdown _matches _marked ) {
assert . equal ( test . expected _output , output ) ;
} else {
assert . notEqual ( test . expected _output , output ) ;
}
} ) ;
2014-01-17 21:08:45 +01:00
} ( ) ) ;
2017-01-24 01:51:58 +01:00
( function test _message _flags ( ) {
var message = { raw _content : '@**Leo**' } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2017-01-24 01:51:58 +01:00
assert ( ! _ . contains ( message . flags , 'mentioned' ) ) ;
message = { raw _content : '@**Cordelia Lear**' } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2017-01-24 01:51:58 +01:00
assert ( _ . contains ( message . flags , 'mentioned' ) ) ;
message = { raw _content : '@**all**' } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2017-01-24 01:51:58 +01:00
assert ( _ . contains ( message . flags , 'mentioned' ) ) ;
} ( ) ) ;
2013-12-04 17:16:08 +01:00
( function test _marked ( ) {
2017-06-13 23:49:50 +02:00
var test _cases = [
{ input : 'hello' , expected : '<p>hello</p>' } ,
{ input : 'hello there' , expected : '<p>hello there</p>' } ,
{ input : 'hello **bold** for you' , expected : '<p>hello <strong>bold</strong> for you</p>' } ,
{ input : '__hello__' , expected : '<p>__hello__</p>' } ,
{ input : '\n```\nfenced code\n```\n\nand then after\n' ,
expected : '<div class="codehilite"><pre><span></span>fenced code\n</pre></div>\n\n\n<p>and then after</p>' } ,
{ input : '\n```\n fenced code trailing whitespace \n```\n\nand then after\n' ,
expected : '<div class="codehilite"><pre><span></span> fenced code trailing whitespace\n</pre></div>\n\n\n<p>and then after</p>' } ,
{ input : '* a\n* list \n* here' ,
expected : '<ul>\n<li>a</li>\n<li>list </li>\n<li>here</li>\n</ul>' } ,
2017-06-15 23:48:29 +02:00
{ input : '\n```c#\nfenced code special\n```\n\nand then after\n' ,
expected : '<div class="codehilite"><pre><span></span>fenced code special\n</pre></div>\n\n\n<p>and then after</p>' } ,
{ input : '\n```vb.net\nfenced code dot\n```\n\nand then after\n' ,
expected : '<div class="codehilite"><pre><span></span>fenced code dot\n</pre></div>\n\n\n<p>and then after</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'Some text first\n* a\n* list \n* here\n\nand then after' ,
expected : '<p>Some text first</p>\n<ul>\n<li>a</li>\n<li>list </li>\n<li>here</li>\n</ul>\n<p>and then after</p>' } ,
{ input : '1. an\n2. ordered \n3. list' ,
expected : '<p>1. an</p>\n<p>2. ordered </p>\n<p>3. list</p>' } ,
{ input : '\n~~~quote\nquote this for me\n~~~\nthanks\n' ,
expected : '<blockquote>\n<p>quote this for me</p>\n</blockquote>\n<p>thanks</p>' } ,
{ input : 'This is a @**Cordelia Lear** mention' ,
expected : '<p>This is a <span class="user-mention" data-user-id="101">@Cordelia Lear</span> mention</p>' } ,
{ input : 'These @ @**** are not mentions' ,
expected : '<p>These @ @<em>**</em> are not mentions</p>' } ,
{ input : 'These # #**** are not mentions' ,
expected : '<p>These # #<em>**</em> are not mentions</p>' } ,
{ input : 'These @* @*** are not mentions' ,
expected : '<p>These @* @*** are not mentions</p>' } ,
{ input : 'These #* #*** are also not mentions' ,
expected : '<p>These #* #*** are also not mentions</p>' } ,
{ input : 'This is a #**Denmark** stream link' ,
expected : '<p>This is a <a class="stream" data-stream-id="1" href="http://zulip.zulipdev.com/#narrow/stream/Denmark">#Denmark</a> stream link</p>' } ,
{ input : 'This is #**Denmark** and #**social** stream links' ,
expected : '<p>This is <a class="stream" data-stream-id="1" href="http://zulip.zulipdev.com/#narrow/stream/Denmark">#Denmark</a> and <a class="stream" data-stream-id="2" href="http://zulip.zulipdev.com/#narrow/stream/social">#social</a> stream links</p>' } ,
{ input : 'And this is a #**wrong** stream link' ,
expected : '<p>And this is a #**wrong** stream link</p>' } ,
{ input : 'mmm...:burrito:s' ,
expected : '<p>mmm...<img alt=":burrito:" class="emoji" src="/static/generated/emoji/images/emoji/burrito.png" title=":burrito:">s</p>' } ,
{ input : 'This is an :poop: message' ,
expected : '<p>This is an <img alt=":poop:" class="emoji" src="/static/generated/emoji/images/emoji/unicode/1f4a9.png" title=":poop:"> message</p>' } ,
{ input : "\ud83d\udca9" ,
2017-06-20 15:52:14 +02:00
expected : '<p><img alt=":poop:" class="emoji" src="/static/generated/emoji/images/emoji/unicode/1f4a9.png" title=":poop:"></p>' } ,
2017-06-13 23:49:50 +02:00
{ input : '\u{1f937}' ,
expected : '<p>\u{1f937}</p>' } ,
{ input : 'This is a realm filter #1234 with text after it' ,
expected : '<p>This is a realm filter <a href="https://trac.zulip.net/ticket/1234" target="_blank" title="https://trac.zulip.net/ticket/1234">#1234</a> with text after it</p>' } ,
{ input : 'This is a realm filter with ZGROUP_123:45 groups' ,
expected : '<p>This is a realm filter with <a href="https://zone_45.zulip.net/ticket/123" target="_blank" title="https://zone_45.zulip.net/ticket/123">ZGROUP_123:45</a> groups</p>' } ,
{ input : 'This is an !avatar(cordelia@zulip.com) of Cordelia Lear' ,
expected : '<p>This is an <img alt="cordelia@zulip.com" class="message_body_gravatar" src="/avatar/cordelia@zulip.com?s=30" title="cordelia@zulip.com"> of Cordelia Lear</p>' } ,
{ input : 'This is a !gravatar(cordelia@zulip.com) of Cordelia Lear' ,
expected : '<p>This is a <img alt="cordelia@zulip.com" class="message_body_gravatar" src="/avatar/cordelia@zulip.com?s=30" title="cordelia@zulip.com"> of Cordelia Lear</p>' } ,
{ input : 'Test *italic*' ,
expected : '<p>Test <em>italic</em></p>' } ,
2017-07-18 08:38:59 +02:00
{ input : 'T\n#**Denmark**' ,
2017-07-18 10:33:19 +02:00
expected : '<p>T<br>\n<a class="stream" data-stream-id="1" href="http://zulip.zulipdev.com/#narrow/stream/Denmark">#Denmark</a></p>' } ,
2017-07-18 08:38:59 +02:00
{ input : 'T\n@**Cordelia Lear**' ,
expected : '<p>T<br>\n<span class="user-mention" data-user-id="101">@Cordelia Lear</span></p>' } ,
{ input : 'This is a realm filter `hello` with text after it' ,
expected : '<p>This is a realm filter <code>hello</code> with text after it</p>' } ,
2017-06-13 23:49:50 +02:00
] ;
// We remove one of the unicode emoji we put as input in one of the test
// cases (U+1F937), to verify that we display the emoji as it was input if it
// isn't present in emoji.emojis_by_unicode.
delete emoji . emojis _by _unicode [ '1f937' ] ;
test _cases . forEach ( function ( test _case ) {
var input = test _case . input ;
var expected = test _case . expected ;
var message = { raw _content : input } ;
markdown . apply _markdown ( message ) ;
var output = message . content ;
assert . equal ( expected , output ) ;
} ) ;
2014-01-24 21:48:56 +01:00
} ( ) ) ;
( function test _subject _links ( ) {
2017-06-13 23:49:50 +02:00
var message = { type : 'stream' , subject : "No links here" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , [ ] ) ;
message = { type : 'stream' , subject : "One #123 link here" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , 1 ) ;
assert . equal ( message . subject _links [ 0 ] , "https://trac.zulip.net/ticket/123" ) ;
message = { type : 'stream' , subject : "Two #123 #456 link here" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , 2 ) ;
assert . equal ( message . subject _links [ 0 ] , "https://trac.zulip.net/ticket/123" ) ;
assert . equal ( message . subject _links [ 1 ] , "https://trac.zulip.net/ticket/456" ) ;
message = { type : 'stream' , subject : "New ZBUG_123 link here" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , 1 ) ;
assert . equal ( message . subject _links [ 0 ] , "https://trac2.zulip.net/ticket/123" ) ;
message = { type : 'stream' , subject : "New ZBUG_123 with #456 link here" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , 2 ) ;
assert ( message . subject _links . indexOf ( "https://trac2.zulip.net/ticket/123" ) !== - 1 ) ;
assert ( message . subject _links . indexOf ( "https://trac.zulip.net/ticket/456" ) !== - 1 ) ;
message = { type : 'stream' , subject : "One ZGROUP_123:45 link here" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , 1 ) ;
assert . equal ( message . subject _links [ 0 ] , "https://zone_45.zulip.net/ticket/123" ) ;
message = { type : "not-stream" } ;
markdown . add _subject _links ( message ) ;
assert . equal ( message . subject _links . length , 0 ) ;
2013-12-04 17:16:08 +01:00
} ( ) ) ;
2014-01-27 17:06:59 +01:00
( function test _message _flags ( ) {
2017-06-13 23:49:50 +02:00
var input = "/me is testing this" ;
var message = { subject : "No links here" , raw _content : input } ;
message . flags = [ 'read' ] ;
markdown . apply _markdown ( message ) ;
markdown . add _message _flags ( message ) ;
assert . equal ( message . flags . length , 2 ) ;
assert ( message . flags . indexOf ( 'read' ) !== - 1 ) ;
assert ( message . flags . indexOf ( 'is_me_message' ) !== - 1 ) ;
input = "testing this @**all** @**Cordelia Lear**" ;
message = { subject : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
markdown . add _message _flags ( message ) ;
assert . equal ( message . flags . length , 1 ) ;
assert ( message . flags . indexOf ( 'mentioned' ) !== - 1 ) ;
input = "test @all" ;
message = { subject : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
markdown . add _message _flags ( message ) ;
assert . equal ( message . flags . length , 1 ) ;
assert ( message . flags . indexOf ( 'mentioned' ) !== - 1 ) ;
input = "test @any" ;
message = { subject : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
markdown . add _message _flags ( message ) ;
assert . equal ( message . flags . length , 0 ) ;
assert ( message . flags . indexOf ( 'mentioned' ) === - 1 ) ;
2014-01-27 17:06:59 +01:00
} ( ) ) ;
2017-06-13 23:26:27 +02:00
( function test _python _to _js _filter ( ) {
2017-06-13 23:49:50 +02:00
// The only way to reach python_to_js_filter is indirectly, hence the call
// to set_realm_filters.
markdown . set _realm _filters ( [ [ '/a(?im)a/g' ] , [ '/a(?L)a/g' ] ] ) ;
var actual _value = ( marked . InlineLexer . rules . zulip . realm _filters ) ;
var expected _value = [ /\/aa\/g/gim , /\/aa\/g/g ] ;
assert . deepEqual ( actual _value , expected _value ) ;
2017-06-13 23:26:27 +02:00
} ( ) ) ;
2017-06-14 11:19:00 +02:00
( function test _katex _throws _unexpected _exceptions ( ) {
katex . renderToString = function ( ) { throw new Error ( 'some-exception' ) ; } ;
var blueslip _error _called = false ;
blueslip . error = function ( ex ) {
assert . equal ( ex . message , 'some-exception' ) ;
blueslip _error _called = true ;
} ;
var message = { raw _content : '$$a$$' } ;
markdown . apply _markdown ( message ) ;
assert ( blueslip _error _called ) ;
} ( ) ) ;