2017-11-08 17:42:34 +01:00
zrequire ( 'hash_util' ) ;
2019-07-26 03:47:36 +02:00
set _global ( 'katex' , zrequire ( 'katex' , 'katex/dist/katex.min.js' ) ) ;
set _global ( 'marked' , zrequire ( 'marked' , 'third/marked/lib/marked' ) ) ;
2020-02-04 21:50:55 +01:00
2017-11-08 17:42:34 +01:00
zrequire ( 'fenced_code' ) ;
zrequire ( 'stream_data' ) ;
zrequire ( 'people' ) ;
2017-11-22 09:11:07 +01:00
zrequire ( 'user_groups' ) ;
2020-02-06 07:07:10 +01:00
const emoji _codes = zrequire ( 'emoji_codes' , 'generated/emoji/emoji_codes.json' ) ;
2017-11-08 17:42:34 +01:00
zrequire ( 'emoji' ) ;
2017-12-16 23:25:31 +01:00
zrequire ( 'message_store' ) ;
markdown: Add helper configuration for mobile.
This refactoring is the first step toward sharing
our markdown code with mobile. This focuses on
the Zulip layer, not the underlying third party `marked`
library.
In this commit we do a one-time initialization to
wire up the markdown functions, but after further
discussions with Greg, it might make more sense
to just pass in helpers on every use of markdown
(which is generally only once per sent message).
I'll address that in follow-up commits.
Even though it looks like a pretty invasive change,
you will note that we barely needed to modify the
node tests to make this pass. And we have pretty
decent test coverage here.
All of the places where we used to depend on
other Zulip modules now use helper functions that
any client (e.g. mobile) can configure themselves.
Or course, in the webapp, we configure these from
modules like people/stream_data/hash_util/etc.
Even in places where markdown used to deal directly with
data structures from other modules, we now use functions.
We may revisit this in a future commit, and we might
just pass data directly for certain things.
I decided to keep the helpers data structure completely flat,
so we don't have ugly nested names like
`helpers.emoji.get_emoji_codepoint`. Because of this,
some of the names aren't 1:1, which I think is fine.
For example, we map `user_groups.is_member_of` to
`is_member_of_user_group`.
It's likely that mobile already has different names
for their versions of these functions, so trying for
fake consistency would only help the webapp. In some
cases, I think the webapp functions have names that
could be improved, but we can clean that up in future
commits, and since the names aren't coupled to markdown
itself (i.e. only the config), we will be less
constrained.
It's worth noting that `marked` has an `options`
data structure that it uses for configuration, but
I didn't piggyback onto it, since the `marked`
options are more at the lexing/parsing layer vs.
the app-data layer stuff that our helpers mostly
help with.
Hopefully it's obvious why I just put helpers in
the top-level namespace for the module rather than
passing it around through multiple layers of the
parser.
There were a couple places in markdown where we
were doing awkward `hasOwnProperty` checks for
emoji-related stuff. Now we use the Python
principle of ask-forgiveness-not-permission and
just handle the getters returning falsy data. (It
should be `undefined`, but any falsy value is
unworkable in the places I changed, so I use
the simpler, less brittle form.)
We also break our direct dependency on
`emoji_codes.json` (with some help from the
prior commit).
In one place I rename streamName to stream_name,
fixing up an ancient naming violation that goes
way back to before this code was even extracted
away from echo.js. I didn't bother to split this
out into a separate commit, since 2 of the 4
lines would be immediately re-modified in the
subsequent commit.
Note that we still depend on `fenced_code`
via the global namespace, instead of simply
requiring it directly or injecting it. The
reason I'm postponing any action there is that
we'll have to change things once we move
markdown into a shared library. (The most
likely outcome is that we'll rename/move both files
at the same time and fix the namespace/require
details as part of that commit.)
Also the markdown code still relies on `_` being
available in the global namespace. We aren't
quite ready to share code with mobile yet, but the
underscore dependency should not be problematic,
since mobile already uses underscore to use the
webapp's shared typing_status module.
2020-02-13 13:54:11 +01:00
const markdown _config = zrequire ( 'markdown_config' ) ;
2017-11-08 17:42:34 +01:00
zrequire ( 'markdown' ) ;
2014-01-17 21:08:45 +01:00
2019-07-25 09:13:22 +02:00
set _global ( 'location' , {
origin : 'http://zulip.zulipdev.com' ,
2017-04-04 15:31:21 +02:00
} ) ;
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 : {
2018-03-11 18:55:20 +01:00
1 : { id : 1 ,
name : 'burrito' ,
source _url : '/static/generated/emoji/images/emoji/burrito.png' ,
deactivated : false ,
} ,
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" ,
] ,
] ,
2018-01-15 19:36:32 +01:00
translate _emoticons : false ,
2016-10-30 20:54:53 +01:00
} ) ;
2014-01-24 22:52:37 +01:00
2017-08-16 22:00:19 +02:00
set _global ( 'Image' , function ( ) {
2018-05-07 03:30:13 +02:00
return { } ;
2017-08-16 22:00:19 +02:00
} ) ;
emoji . initialize ( ) ;
2019-11-02 00:06:25 +01:00
const doc = "" ;
2014-01-16 20:18:55 +01:00
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
2019-11-02 00:06:25 +01:00
const 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' ,
} ) ;
2018-03-29 00:25:58 +02:00
people . add ( {
full _name : 'Bobby <h1>Tables</h1>' ,
user _id : 103 ,
email : 'bobby@zulip.com' ,
} ) ;
2018-08-19 03:39:57 +02:00
people . add ( {
full _name : 'Mark Twin' ,
user _id : 104 ,
email : 'twin1@zulip.com' ,
} ) ;
people . add ( {
full _name : 'Mark Twin' ,
user _id : 105 ,
email : 'twin2@zulip.com' ,
} ) ;
people . add ( {
full _name : 'Brother of Bobby|123' ,
user _id : 106 ,
email : 'bobby2@zulip.com' ,
} ) ;
2019-12-13 01:38:49 +01:00
people . add ( {
full _name : "& & &" ,
user _id : 107 ,
email : "ampampamp@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
2019-11-02 00:06:25 +01:00
const hamletcharacters = {
2017-11-22 09:11:07 +01:00
name : "hamletcharacters" ,
id : 1 ,
description : "Characters of Hamlet" ,
members : [ cordelia . user _id ] ,
} ;
2019-11-02 00:06:25 +01:00
const backend = {
2017-11-22 09:11:07 +01:00
name : "Backend" ,
id : 2 ,
description : "Backend team" ,
members : [ ] ,
} ;
2019-11-02 00:06:25 +01:00
const edgecase _group = {
2018-03-29 00:25:58 +02:00
name : "Bobby <h1>Tables</h1>" ,
id : 3 ,
description : "HTML Syntax to check for Markdown edge cases." ,
members : [ ] ,
} ;
2019-12-13 01:38:49 +01:00
const amp _group = {
name : "& & &" ,
id : 4 ,
description : "Check ampersand escaping" ,
members : [ ] ,
} ;
2017-11-22 09:11:07 +01:00
global . user _groups . add ( hamletcharacters ) ;
global . user _groups . add ( backend ) ;
2018-03-29 00:25:58 +02:00
global . user _groups . add ( edgecase _group ) ;
2019-12-13 01:38:49 +01:00
global . user _groups . add ( amp _group ) ;
2017-11-22 09:11:07 +01:00
2019-11-02 00:06:25 +01:00
const stream _data = global . stream _data ;
const denmark = {
2016-11-10 20:20:40 +01:00
subscribed : false ,
color : 'blue' ,
name : 'Denmark' ,
stream _id : 1 ,
2019-05-15 08:54:25 +02:00
is _muted : true ,
2016-11-10 20:20:40 +01:00
} ;
2019-11-02 00:06:25 +01:00
const social = {
2016-11-10 20:20:40 +01:00
subscribed : true ,
color : 'red' ,
name : 'social' ,
stream _id : 2 ,
2019-05-15 08:54:25 +02:00
is _muted : false ,
2016-12-03 23:17:57 +01:00
invite _only : true ,
2016-11-10 20:20:40 +01:00
} ;
2019-11-02 00:06:25 +01:00
const edgecase _stream = {
2018-03-29 00:25:58 +02:00
subscribed : true ,
color : 'green' ,
name : 'Bobby <h1>Tables</h1>' ,
stream _id : 3 ,
2019-05-15 08:54:25 +02:00
is _muted : false ,
2018-03-29 00:25:58 +02:00
} ;
2019-11-02 00:06:25 +01:00
const edgecase _stream _2 = {
2019-06-21 20:47:09 +02:00
subscribed : true ,
color : 'yellow' ,
name : 'Bobby <h1' ,
stream _id : 4 ,
is _muted : false ,
} ;
2019-12-13 01:38:49 +01:00
const amp _stream = {
subscribed : true ,
color : 'orange' ,
name : '& & &' ,
stream _id : 5 ,
is _muted : false ,
} ;
2020-02-09 22:02:55 +01:00
stream _data . add _sub ( denmark ) ;
stream _data . add _sub ( social ) ;
stream _data . add _sub ( edgecase _stream ) ;
stream _data . add _sub ( edgecase _stream _2 ) ;
2019-06-21 20:47:09 +02:00
// Note: edgecase_stream cannot be mentioned because it is caught by
// streamTopicHandler and it would be parsed as edgecase_stream_2.
2019-12-13 01:38:49 +01:00
stream _data . add _sub ( amp _stream ) ;
2016-11-10 20:20:40 +01:00
2017-06-15 23:48:29 +02:00
// Check the default behavior of fenced code blocks
// works properly before markdown is initialized.
2018-05-15 12:40:07 +02:00
run _test ( 'fenced_block_defaults' , ( ) => {
2019-11-02 00:06:25 +01:00
const input = '\n```\nfenced code\n```\n\nand then after\n' ;
2020-04-18 03:17:21 +02:00
const expected = '\n\n<div class="codehilite"><pre><span></span><code>fenced code\n</code></pre></div>\n\n\n\nand then after\n\n' ;
2019-11-02 00:06:25 +01:00
const output = fenced _code . process _fenced _code ( input ) ;
2017-06-15 23:48:29 +02:00
assert . equal ( output , expected ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-06-15 23:48:29 +02:00
markdown: Add helper configuration for mobile.
This refactoring is the first step toward sharing
our markdown code with mobile. This focuses on
the Zulip layer, not the underlying third party `marked`
library.
In this commit we do a one-time initialization to
wire up the markdown functions, but after further
discussions with Greg, it might make more sense
to just pass in helpers on every use of markdown
(which is generally only once per sent message).
I'll address that in follow-up commits.
Even though it looks like a pretty invasive change,
you will note that we barely needed to modify the
node tests to make this pass. And we have pretty
decent test coverage here.
All of the places where we used to depend on
other Zulip modules now use helper functions that
any client (e.g. mobile) can configure themselves.
Or course, in the webapp, we configure these from
modules like people/stream_data/hash_util/etc.
Even in places where markdown used to deal directly with
data structures from other modules, we now use functions.
We may revisit this in a future commit, and we might
just pass data directly for certain things.
I decided to keep the helpers data structure completely flat,
so we don't have ugly nested names like
`helpers.emoji.get_emoji_codepoint`. Because of this,
some of the names aren't 1:1, which I think is fine.
For example, we map `user_groups.is_member_of` to
`is_member_of_user_group`.
It's likely that mobile already has different names
for their versions of these functions, so trying for
fake consistency would only help the webapp. In some
cases, I think the webapp functions have names that
could be improved, but we can clean that up in future
commits, and since the names aren't coupled to markdown
itself (i.e. only the config), we will be less
constrained.
It's worth noting that `marked` has an `options`
data structure that it uses for configuration, but
I didn't piggyback onto it, since the `marked`
options are more at the lexing/parsing layer vs.
the app-data layer stuff that our helpers mostly
help with.
Hopefully it's obvious why I just put helpers in
the top-level namespace for the module rather than
passing it around through multiple layers of the
parser.
There were a couple places in markdown where we
were doing awkward `hasOwnProperty` checks for
emoji-related stuff. Now we use the Python
principle of ask-forgiveness-not-permission and
just handle the getters returning falsy data. (It
should be `undefined`, but any falsy value is
unworkable in the places I changed, so I use
the simpler, less brittle form.)
We also break our direct dependency on
`emoji_codes.json` (with some help from the
prior commit).
In one place I rename streamName to stream_name,
fixing up an ancient naming violation that goes
way back to before this code was even extracted
away from echo.js. I didn't bother to split this
out into a separate commit, since 2 of the 4
lines would be immediately re-modified in the
subsequent commit.
Note that we still depend on `fenced_code`
via the global namespace, instead of simply
requiring it directly or injecting it. The
reason I'm postponing any action there is that
we'll have to change things once we move
markdown into a shared library. (The most
likely outcome is that we'll rename/move both files
at the same time and fix the namespace/require
details as part of that commit.)
Also the markdown code still relies on `_` being
available in the global namespace. We aren't
quite ready to share code with mobile yet, but the
underscore dependency should not be problematic,
since mobile already uses underscore to use the
webapp's shared typing_status module.
2020-02-13 13:54:11 +01:00
markdown . initialize (
page _params . realm _filters ,
markdown _config . get _helpers ( )
) ;
2013-12-04 17:16:08 +01:00
2019-11-02 00:06:25 +01:00
const bugdown _data = global . read _fixture _data ( 'markdown_test_cases.json' ) ;
2014-01-17 21:08:45 +01:00
2018-05-15 12:40:07 +02:00
run _test ( 'bugdown_detection' , ( ) => {
2019-11-02 00:06:25 +01:00
const no _markup = [
2018-05-07 03:30:13 +02:00
"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**" ,
"No user mention @what there" ,
"No group mention *hamletcharacters*" ,
"We like to code\n~~~\ndef code():\n we = \"like to do\"\n~~~" ,
"This is a\nmultiline :emoji: here\n message" ,
"This is an :emoji: message" ,
"User Mention @**leo**" ,
"User Mention @**leo f**" ,
"User Mention @**leo with some name**" ,
"Group Mention @*hamletcharacters*" ,
"Stream #**Verona**" ,
"This contains !gravatar(leo@zulip.com)" ,
"And an avatar !avatar(leo@zulip.com) is here" ,
] ;
2013-12-04 17:16:08 +01:00
2019-11-02 00:06:25 +01:00
const markup = [
2018-05-07 03:30:13 +02:00
"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" ,
"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-07-29 02:51:33 +02:00
assert . equal ( markdown . contains _backend _only _syntax ( content ) , false ) ;
2013-12-04 17:16:08 +01:00
} ) ;
markup . forEach ( function ( content ) {
2017-07-29 02:51:33 +02:00
assert . equal ( markdown . contains _backend _only _syntax ( content ) , true ) ;
2013-12-04 17:16:08 +01:00
} ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2013-12-04 17:16:08 +01:00
2018-05-15 12:40:07 +02:00
run _test ( 'marked_shared' , ( ) => {
2019-11-02 00:06:25 +01:00
const tests = bugdown _data . regular _tests ;
2017-12-10 09:01:37 +01:00
2017-06-13 23:49:50 +02:00
tests . forEach ( function ( test ) {
2018-03-28 10:40:44 +02:00
// Ignore tests if specified
if ( test . ignore === true ) {
return ;
}
2019-11-02 00:06:25 +01:00
const message = { raw _content : test . input } ;
2018-03-25 23:23:22 +02:00
page _params . translate _emoticons = test . translate _emoticons || false ;
2017-06-13 23:49:50 +02:00
markdown . apply _markdown ( message ) ;
2019-11-02 00:06:25 +01:00
const output = message . content ;
const error _message = ` Failure in test: ${ test . name } ` ;
2017-07-29 03:04:17 +02:00
if ( test . marked _expected _output ) {
2018-03-28 09:17:31 +02:00
global . bugdown _assert . notEqual ( test . expected _output , output , error _message ) ;
global . bugdown _assert . equal ( test . marked _expected _output , output , error _message ) ;
2017-07-29 03:17:25 +02:00
} else if ( test . backend _only _rendering ) {
assert . equal ( markdown . contains _backend _only _syntax ( test . input ) , true ) ;
2017-06-13 23:49:50 +02:00
} else {
2018-03-28 09:17:31 +02:00
global . bugdown _assert . equal ( test . expected _output , output , error _message ) ;
2017-06-13 23:49:50 +02:00
}
} ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2014-01-17 21:08:45 +01:00
2018-05-15 12:40:07 +02:00
run _test ( 'message_flags' , ( ) => {
2019-11-02 00:06:25 +01:00
let message = { raw _content : '@**Leo**' } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2017-12-16 23:25:31 +01:00
assert ( ! message . mentioned ) ;
assert ( ! message . mentioned _me _directly ) ;
2017-01-24 01:51:58 +01:00
message = { raw _content : '@**Cordelia Lear**' } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2017-12-16 23:25:31 +01:00
assert ( message . mentioned ) ;
assert ( message . mentioned _me _directly ) ;
2017-01-24 01:51:58 +01:00
message = { raw _content : '@**all**' } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2017-12-16 23:25:31 +01:00
assert ( message . mentioned ) ;
assert ( ! message . mentioned _me _directly ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-01-24 01:51:58 +01:00
2018-05-15 12:40:07 +02:00
run _test ( 'marked' , ( ) => {
2019-11-02 00:06:25 +01:00
const test _cases = [
2017-06-13 23:49:50 +02:00
{ 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>' } ,
2018-02-17 01:59:22 +01:00
{ input : 'hello ***foo*** for you' , expected : '<p>hello <strong><em>foo</em></strong> for you</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : '__hello__' , expected : '<p>__hello__</p>' } ,
{ input : '\n```\nfenced code\n```\n\nand then after\n' ,
2020-04-18 03:17:21 +02:00
expected : '<div class="codehilite"><pre><span></span><code>fenced code\n</code></pre></div>\n\n\n<p>and then after</p>' } ,
2017-07-30 21:28:34 +02:00
{ input : '\n```\n fenced code trailing whitespace \n```\n\nand then after\n' ,
2020-04-18 03:17:21 +02:00
expected : '<div class="codehilite"><pre><span></span><code> fenced code trailing whitespace\n</code></pre></div>\n\n\n<p>and then after</p>' } ,
2017-06-13 23:49:50 +02:00
{ 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' ,
2020-04-18 03:17:21 +02:00
expected : '<div class="codehilite"><pre><span></span><code>fenced code special\n</code></pre></div>\n\n\n<p>and then after</p>' } ,
2017-06-15 23:48:29 +02:00
{ input : '\n```vb.net\nfenced code dot\n```\n\nand then after\n' ,
2020-04-18 03:17:21 +02:00
expected : '<div class="codehilite"><pre><span></span><code>fenced code dot\n</code></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' ,
2019-08-11 07:41:34 +02:00
expected : '<ol>\n<li>an</li>\n<li>ordered </li>\n<li>list</li>\n</ol>' } ,
2017-06-13 23:49:50 +02:00
{ 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>' } ,
2020-02-17 14:19:35 +01:00
{ input : 'This is a @**CordeLIA Lear** mention' ,
2017-06-13 23:49:50 +02:00
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>' } ,
2017-11-24 07:11:11 +01:00
{ input : 'These @* are not mentions' ,
expected : '<p>These @* are not mentions</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'These #* #*** are also not mentions' ,
expected : '<p>These #* #*** are also not mentions</p>' } ,
{ input : 'This is a #**Denmark** stream link' ,
2019-07-12 00:09:38 +02:00
expected : '<p>This is a <a class="stream" data-stream-id="1" href="/#narrow/stream/1-Denmark">#Denmark</a> stream link</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'This is #**Denmark** and #**social** stream links' ,
2019-07-12 00:09:38 +02:00
expected : '<p>This is <a class="stream" data-stream-id="1" href="/#narrow/stream/1-Denmark">#Denmark</a> and <a class="stream" data-stream-id="2" href="/#narrow/stream/2-social">#social</a> stream links</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'And this is a #**wrong** stream link' ,
expected : '<p>And this is a #**wrong** stream link</p>' } ,
2019-06-21 20:47:09 +02:00
{ input : 'This is a #**Denmark>some topic** stream_topic link' ,
2019-07-12 00:09:38 +02:00
expected : '<p>This is a <a class="stream-topic" data-stream-id="1" href="/#narrow/stream/1-Denmark/topic/some.20topic">#Denmark > some topic</a> stream_topic link</p>' } ,
2019-06-21 20:47:09 +02:00
{ input : 'This has two links: #**Denmark>some topic** and #**social>other topic**.' ,
2019-07-12 00:09:38 +02:00
expected : '<p>This has two links: <a class="stream-topic" data-stream-id="1" href="/#narrow/stream/1-Denmark/topic/some.20topic">#Denmark > some topic</a> and <a class="stream-topic" data-stream-id="2" href="/#narrow/stream/2-social/topic/other.20topic">#social > other topic</a>.</p>' } ,
2019-06-21 20:47:09 +02:00
{ input : 'This is not a #**Denmark>** stream_topic link' ,
expected : '<p>This is not a #**Denmark>** stream_topic link</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'mmm...:burrito:s' ,
2017-06-09 10:30:24 +02:00
expected : '<p>mmm...<img alt=":burrito:" class="emoji" src="/static/generated/emoji/images/emoji/burrito.png" title="burrito">s</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'This is an :poop: message' ,
2019-01-14 08:45:37 +01:00
expected : '<p>This is an <span aria-label="poop" class="emoji emoji-1f4a9" role="img" title="poop">:poop:</span> message</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : "\ud83d\udca9" ,
2019-01-14 08:45:37 +01:00
expected : '<p><span aria-label="poop" class="emoji emoji-1f4a9" role="img" title="poop">:poop:</span></p>' } ,
2017-09-28 19:25:20 +02:00
{ input : '\u{1f6b2}' ,
expected : '<p>\u{1f6b2}</p>' } ,
2019-02-20 10:15:33 +01:00
{ input : 'Silent mention: @_**Cordelia Lear**' ,
2019-02-15 20:58:54 +01:00
expected : '<p>Silent mention: <span class="user-mention silent" data-user-id="101">Cordelia Lear</span></p>' } ,
2019-01-08 11:30:13 +01:00
{ input : '> Mention in quote: @**Cordelia Lear**\n\nMention outside quote: @**Cordelia Lear**' ,
2019-02-15 20:58:54 +01:00
expected : '<blockquote>\n<p>Mention in quote: <span class="user-mention silent" data-user-id="101">Cordelia Lear</span></p>\n</blockquote>\n<p>Mention outside quote: <span class="user-mention" data-user-id="101">@Cordelia Lear</span></p>' } ,
2017-07-30 21:07:59 +02:00
// Test only those realm filters which don't return True for
// `contains_backend_only_syntax()`. Those which return True
// are tested separately.
2017-06-13 23:49:50 +02:00
{ input : 'This is a realm filter #1234 with text after it' ,
2020-02-29 01:37:33 +01:00
expected : '<p>This is a realm filter <a href="https://trac.zulip.net/ticket/1234" title="https://trac.zulip.net/ticket/1234">#1234</a> with text after it</p>' } ,
2017-07-30 21:07:59 +02:00
{ input : '#1234is not a realm filter.' ,
expected : '<p>#1234is not a realm filter.</p>' } ,
{ input : 'A pattern written as #1234is not a realm filter.' ,
expected : '<p>A pattern written as #1234is not a realm filter.</p>' } ,
2017-06-13 23:49:50 +02:00
{ input : 'This is a realm filter with ZGROUP_123:45 groups' ,
2020-02-29 01:37:33 +01:00
expected : '<p>This is a realm filter with <a href="https://zone_45.zulip.net/ticket/123" title="https://zone_45.zulip.net/ticket/123">ZGROUP_123:45</a> groups</p>' } ,
2017-06-13 23:49:50 +02:00
{ 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**' ,
2019-07-12 00:09:38 +02:00
expected : '<p>T<br>\n<a class="stream" data-stream-id="1" href="/#narrow/stream/1-Denmark">#Denmark</a></p>' } ,
2017-07-30 21:28:34 +02:00
{ input : 'T\n@**Cordelia Lear**' ,
2018-05-07 03:30:13 +02:00
expected : '<p>T<br>\n<span class="user-mention" data-user-id="101">@Cordelia Lear</span></p>' } ,
2018-08-19 03:39:57 +02:00
{ input : '@**Mark Twin|104** and @**Mark Twin|105** are out to confuse you.' ,
2018-12-18 19:34:45 +01:00
expected : '<p><span class="user-mention" data-user-id="104">@Mark Twin</span> and <span class="user-mention" data-user-id="105">@Mark Twin</span> are out to confuse you.</p>' } ,
2018-08-19 03:39:57 +02:00
{ input : '@**Invalid User|1234**' ,
expected : '<p>@**Invalid User|1234**</p>' } ,
2020-02-17 14:19:35 +01:00
{ input : '@**Cordelia LeAR|103** has a wrong user_id.' ,
expected : '<p>@**Cordelia LeAR|103** has a wrong user_id.</p>' } ,
2018-08-19 03:39:57 +02:00
{ input : '@**Brother of Bobby|123** is really the full name.' ,
expected : '<p><span class="user-mention" data-user-id="106">@Brother of Bobby|123</span> is really the full name.</p>' } ,
{ input : '@**Brother of Bobby|123|106**' ,
expected : '<p><span class="user-mention" data-user-id="106">@Brother of Bobby|123</span></p>' } ,
2017-11-22 09:11:07 +01:00
{ input : 'T\n@hamletcharacters' ,
expected : '<p>T<br>\n@hamletcharacters</p>' } ,
{ input : 'T\n@*hamletcharacters*' ,
expected : '<p>T<br>\n<span class="user-group-mention" data-user-group-id="1">@hamletcharacters</span></p>' } ,
2017-11-29 06:42:47 +01:00
{ input : 'T\n@*notagroup*' ,
expected : '<p>T<br>\n@*notagroup*</p>' } ,
2018-05-07 03:30:13 +02:00
{ input : 'T\n@*backend*' ,
2017-11-22 09:11:07 +01:00
expected : '<p>T<br>\n<span class="user-group-mention" data-user-group-id="2">@Backend</span></p>' } ,
{ input : '@*notagroup*' ,
expected : '<p>@*notagroup*</p>' } ,
2017-07-18 08:38:59 +02:00
{ 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>' } ,
2018-03-25 18:10:59 +02:00
// Test the emoticon conversion
{ input : ':)' ,
expected : '<p>:)</p>' } ,
{ input : ':)' ,
2019-01-14 08:45:37 +01:00
expected : '<p><span aria-label="slight smile" class="emoji emoji-1f642" role="img" title="slight smile">:slight_smile:</span></p>' ,
2018-03-25 18:10:59 +02:00
translate _emoticons : true } ,
2018-05-07 03:30:13 +02:00
// Test HTML Escape in Custom Zulip Rules
2018-03-29 00:25:58 +02:00
{ input : '@**<h1>The Rogue One</h1>**' ,
expected : '<p>@**<h1>The Rogue One</h1>**</p>' } ,
{ input : '#**<h1>The Rogue One</h1>**' ,
expected : '<p>#**<h1>The Rogue One</h1>**</p>' } ,
{ input : '!avatar(<h1>The Rogue One</h1>)' ,
expected : '<p><img alt="<h1>The Rogue One</h1>" class="message_body_gravatar" src="/avatar/<h1>The Rogue One</h1>?s=30" title="<h1>The Rogue One</h1>"></p>' } ,
{ input : ':<h1>The Rogue One</h1>:' ,
expected : '<p>:<h1>The Rogue One</h1>:</p>' } ,
{ input : '@**O\'Connell**' ,
expected : '<p>@**O'Connell**</p>' } ,
{ input : '@*Bobby <h1>Tables</h1>*' ,
expected : '<p><span class="user-group-mention" data-user-group-id="3">@Bobby <h1>Tables</h1></span></p>' } ,
2019-12-13 01:38:49 +01:00
{ input : '@*& & &amp;*' ,
expected : '<p><span class="user-group-mention" data-user-group-id="4">@& & &amp;</span></p>' } ,
2018-03-29 00:25:58 +02:00
{ input : '@**Bobby <h1>Tables</h1>**' ,
expected : '<p><span class="user-mention" data-user-id="103">@Bobby <h1>Tables</h1></span></p>' } ,
2019-12-13 01:38:49 +01:00
{ input : '@**& & &amp;**' ,
expected : '<p><span class="user-mention" data-user-id="107">@& & &amp;</span></p>' } ,
2018-03-29 00:25:58 +02:00
{ input : '#**Bobby <h1>Tables</h1>**' ,
2019-07-12 00:09:38 +02:00
expected : '<p><a class="stream-topic" data-stream-id="4" href="/#narrow/stream/4-Bobby-.3Ch1/topic/Tables.3C.2Fh1.3E">#Bobby <h1 > Tables</h1></a></p>' } ,
2019-12-13 01:38:49 +01:00
{ input : '#**& & &amp;**' ,
expected : '<p><a class="stream" data-stream-id="5" href="/#narrow/stream/5-.26-.26.20.26amp.3B">#& & &amp;</a></p>' } ,
{ input : '#**& & &amp;>& & &amp;**' ,
expected : '<p><a class="stream-topic" data-stream-id="5" href="/#narrow/stream/5-.26-.26.20.26amp.3B/topic/.26.20.26.20.26amp.3B">#& & &amp; > & & &amp;</a></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
2017-09-28 19:25:20 +02:00
// cases (U+1F6B2), to verify that we display the emoji as it was input if it
// isn't present in emoji_codes.codepoint_to_name.
delete emoji _codes . codepoint _to _name [ '1f6b2' ] ;
2017-06-13 23:49:50 +02:00
test _cases . forEach ( function ( test _case ) {
2018-03-25 18:10:59 +02:00
// Disable emoji conversion by default.
page _params . translate _emoticons = test _case . translate _emoticons || false ;
2019-11-02 00:06:25 +01:00
const input = test _case . input ;
const expected = test _case . expected ;
2017-06-13 23:49:50 +02:00
2019-11-02 00:06:25 +01:00
const message = { raw _content : input } ;
2017-06-13 23:49:50 +02:00
markdown . apply _markdown ( message ) ;
2019-11-02 00:06:25 +01:00
const output = message . content ;
2017-06-13 23:49:50 +02:00
assert . equal ( expected , output ) ;
} ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2014-01-24 21:48:56 +01:00
2018-11-15 16:41:21 +01:00
run _test ( 'topic_links' , ( ) => {
2019-11-02 00:06:25 +01:00
let message = { type : 'stream' , topic : "No links here" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 0 ) ;
2017-06-13 23:49:50 +02:00
2018-12-23 16:49:14 +01:00
message = { type : 'stream' , topic : "One #123 link here" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 1 ) ;
assert . equal ( message . topic _links [ 0 ] , "https://trac.zulip.net/ticket/123" ) ;
2017-06-13 23:49:50 +02:00
2018-12-23 16:49:14 +01:00
message = { type : 'stream' , topic : "Two #123 #456 link here" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 2 ) ;
assert . equal ( message . topic _links [ 0 ] , "https://trac.zulip.net/ticket/123" ) ;
assert . equal ( message . topic _links [ 1 ] , "https://trac.zulip.net/ticket/456" ) ;
2017-06-13 23:49:50 +02:00
2018-12-23 16:49:14 +01:00
message = { type : 'stream' , topic : "New ZBUG_123 link here" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 1 ) ;
assert . equal ( message . topic _links [ 0 ] , "https://trac2.zulip.net/ticket/123" ) ;
2017-06-13 23:49:50 +02:00
2018-12-23 16:49:14 +01:00
message = { type : 'stream' , topic : "New ZBUG_123 with #456 link here" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 2 ) ;
assert ( message . topic _links . includes ( "https://trac2.zulip.net/ticket/123" ) ) ;
assert ( message . topic _links . includes ( "https://trac.zulip.net/ticket/456" ) ) ;
2017-06-13 23:49:50 +02:00
2018-12-23 16:49:14 +01:00
message = { type : 'stream' , topic : "One ZGROUP_123:45 link here" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 1 ) ;
assert . equal ( message . topic _links [ 0 ] , "https://zone_45.zulip.net/ticket/123" ) ;
2017-06-13 23:49:50 +02:00
2019-05-25 16:10:30 +02:00
message = { type : 'stream' , topic : "Hello https://google.com" } ;
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 1 ) ;
assert . equal ( message . topic _links [ 0 ] , "https://google.com" ) ;
2019-05-25 16:10:30 +02:00
message = { type : 'stream' , topic : "#456 https://google.com https://github.com" } ;
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 3 ) ;
assert ( message . topic _links . includes ( "https://google.com" ) ) ;
assert ( message . topic _links . includes ( "https://github.com" ) ) ;
assert ( message . topic _links . includes ( "https://trac.zulip.net/ticket/456" ) ) ;
2019-05-25 16:10:30 +02:00
2017-06-13 23:49:50 +02:00
message = { type : "not-stream" } ;
2018-11-13 16:19:59 +01:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 0 ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2014-01-27 17:06:59 +01:00
2018-05-15 12:40:07 +02:00
run _test ( 'message_flags' , ( ) => {
2019-11-02 00:06:25 +01:00
let input = "/me is testing this" ;
let message = { topic : "No links here" , raw _content : input } ;
2017-06-13 23:49:50 +02:00
markdown . apply _markdown ( message ) ;
2017-08-27 18:10:36 +02:00
assert . equal ( message . is _me _message , true ) ;
2017-12-16 23:25:31 +01:00
assert ( ! message . unread ) ;
2017-06-13 23:49:50 +02:00
2018-01-21 19:27:36 +01:00
input = "/me is testing\nthis" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2018-01-21 19:27:36 +01:00
markdown . apply _markdown ( message ) ;
2018-12-29 11:07:27 +01:00
assert . equal ( message . is _me _message , true ) ;
2018-01-21 19:27:36 +01:00
2017-06-13 23:49:50 +02:00
input = "testing this @**all** @**Cordelia Lear**" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2017-06-13 23:49:50 +02:00
markdown . apply _markdown ( message ) ;
2017-08-27 18:10:36 +02:00
assert . equal ( message . is _me _message , false ) ;
2017-12-16 23:25:31 +01:00
assert . equal ( message . mentioned , true ) ;
assert . equal ( message . mentioned _me _directly , true ) ;
2017-06-13 23:49:50 +02:00
2018-04-03 17:55:57 +02:00
input = "test @**everyone**" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2018-04-03 17:55:57 +02:00
markdown . apply _markdown ( message ) ;
assert . equal ( message . is _me _message , false ) ;
assert . equal ( message . mentioned , true ) ;
assert . equal ( message . mentioned _me _directly , false ) ;
input = "test @**stream**" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2018-04-03 17:55:57 +02:00
markdown . apply _markdown ( message ) ;
assert . equal ( message . is _me _message , false ) ;
assert . equal ( message . mentioned , true ) ;
assert . equal ( message . mentioned _me _directly , false ) ;
2017-06-13 23:49:50 +02:00
input = "test @all" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2017-06-13 23:49:50 +02:00
markdown . apply _markdown ( message ) ;
2018-01-24 17:18:07 +01:00
assert . equal ( message . mentioned , false ) ;
input = "test @everyone" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2018-01-24 17:18:07 +01:00
markdown . apply _markdown ( message ) ;
assert . equal ( message . mentioned , false ) ;
2017-06-13 23:49:50 +02:00
input = "test @any" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2017-06-13 23:49:50 +02:00
markdown . apply _markdown ( message ) ;
2017-12-16 23:25:31 +01:00
assert . equal ( message . mentioned , false ) ;
2017-11-22 09:11:07 +01:00
2018-01-24 17:18:07 +01:00
input = "test @alleycat.com" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2018-01-24 17:18:07 +01:00
markdown . apply _markdown ( message ) ;
assert . equal ( message . mentioned , false ) ;
2017-11-22 09:11:07 +01:00
input = "test @*hamletcharacters*" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2017-11-22 09:11:07 +01:00
markdown . apply _markdown ( message ) ;
2017-12-16 23:25:31 +01:00
assert . equal ( message . mentioned , true ) ;
2017-11-22 09:11:07 +01:00
input = "test @*backend*" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2017-11-22 09:11:07 +01:00
markdown . apply _markdown ( message ) ;
2017-12-16 23:25:31 +01:00
assert . equal ( message . mentioned , false ) ;
2018-01-24 17:18:07 +01:00
input = "test @**invalid_user**" ;
2018-12-23 16:49:14 +01:00
message = { topic : "No links here" , raw _content : input } ;
2018-01-24 17:18:07 +01:00
markdown . apply _markdown ( message ) ;
assert . equal ( message . mentioned , false ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-06-13 23:26:27 +02:00
2018-05-15 12:40:07 +02:00
run _test ( 'backend_only_realm_filters' , ( ) => {
2019-11-02 00:06:25 +01:00
const backend _only _realm _filters = [
2017-07-30 21:07:59 +02:00
'Here is the PR-#123.' ,
'Function abc() was introduced in (PR)#123.' ,
] ;
backend _only _realm _filters . forEach ( function ( content ) {
assert . equal ( markdown . contains _backend _only _syntax ( content ) , true ) ;
} ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-07-30 21:07:59 +02:00
2018-05-15 12:40:07 +02:00
run _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
2020-02-17 14:43:59 +01:00
// to update_realm_filter_rules.
markdown . update _realm _filter _rules ( [ [ '/a(?im)a/g' ] , [ '/a(?L)a/g' ] ] ) ;
2019-11-02 00:06:25 +01:00
let actual _value = marked . InlineLexer . rules . zulip . realm _filters ;
let expected _value = [ /\/aa\/g(?![\w])/gim , /\/aa\/g(?![\w])/g ] ;
2017-06-13 23:49:50 +02:00
assert . deepEqual ( actual _value , expected _value ) ;
2019-02-11 22:54:18 +01:00
// Test case with multiple replacements.
2020-02-17 14:43:59 +01:00
markdown . update _realm _filter _rules ( [ [ '#cf(?P<contest>[0-9]+)(?P<problem>[A-Z][0-9A-Z]*)' , 'http://google.com' ] ] ) ;
2019-02-11 22:54:18 +01:00
actual _value = marked . InlineLexer . rules . zulip . realm _filters ;
expected _value = [ /#cf([0-9]+)([A-Z][0-9A-Z]*)(?![\w])/g ] ;
assert . deepEqual ( actual _value , expected _value ) ;
2019-02-12 22:30:57 +01:00
// Test incorrect syntax.
2020-04-03 17:18:04 +02:00
blueslip . expect ( 'error' , 'python_to_js_filter: Invalid regular expression: /!@#@(!#&((!&(@#((?![\\w])/: Unterminated group' ) ;
2020-02-17 14:43:59 +01:00
markdown . update _realm _filter _rules ( [ [ '!@#@(!#&((!&(@#(' , 'http://google.com' ] ] ) ;
2019-02-12 22:30:57 +01:00
actual _value = marked . InlineLexer . rules . zulip . realm _filters ;
expected _value = [ ] ;
assert . deepEqual ( actual _value , expected _value ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-06-14 11:19:00 +02:00
2018-05-15 12:40:07 +02:00
run _test ( 'katex_throws_unexpected_exceptions' , ( ) => {
2017-06-14 11:19:00 +02:00
katex . renderToString = function ( ) { throw new Error ( 'some-exception' ) ; } ;
2020-04-03 17:18:04 +02:00
blueslip . expect ( 'error' , 'Error: some-exception' ) ;
2019-11-02 00:06:25 +01:00
const message = { raw _content : '$$a$$' } ;
2017-06-14 11:19:00 +02:00
markdown . apply _markdown ( message ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2019-02-15 23:24:26 +01:00
run _test ( 'misc_helpers' , ( ) => {
const elem = $ ( '.user-mention' ) ;
markdown . set _name _in _mention _element ( elem , 'Aaron' ) ;
assert . equal ( elem . text ( ) , '@Aaron' ) ;
elem . addClass ( 'silent' ) ;
markdown . set _name _in _mention _element ( elem , 'Aaron, but silent' ) ;
assert . equal ( elem . text ( ) , 'Aaron, but silent' ) ;
} ) ;
2020-02-15 15:21:32 +01:00
run _test ( 'translate_emoticons_to_names' , ( ) => {
// Simple test
const test _text = 'Testing :)' ;
const expected = 'Testing :slight_smile:' ;
const result = markdown . translate _emoticons _to _names ( test _text ) ;
assert . equal ( expected , result ) ;
// Extensive tests.
// The following code loops over the test cases and each emoticon conversion
// to generate multiple test cases.
const testcases = [
{ name : 'only emoticon' , original : '<original>' , expected : '<converted>' } ,
{ name : 'space at start' , original : ' <original>' , expected : ' <converted>' } ,
{ name : 'space at end' , original : '<original> ' , expected : '<converted> ' } ,
{ name : 'symbol at end' , original : '<original>!' , expected : '<converted>!' } ,
{ name : 'symbol at start' , original : 'Hello,<original>' , expected : 'Hello,<converted>' } ,
{ name : 'after a word' , original : 'Hello<original>' , expected : 'Hello<original>' } ,
{ name : 'between words' , original : 'Hello<original>World' , expected : 'Hello<original>World' } ,
{ name : 'end of sentence' , original : 'End of sentence. <original>' , expected : 'End of sentence. <converted>' } ,
{ name : 'between symbols' , original : 'Hello.<original>! World.' , expected : 'Hello.<original>! World.' } ,
{ name : 'before end of sentence' , original : 'Hello <original>!' , expected : 'Hello <converted>!' } ,
] ;
for ( const [ shortcut , full _name ] of Object . entries ( emoji _codes . emoticon _conversions ) ) {
for ( const t of testcases ) {
const converted _value = full _name ;
let original = t . original ;
let expected = t . expected ;
original = original . replace ( /(<original>)/g , shortcut ) ;
expected = expected . replace ( /(<original>)/g , shortcut )
. replace ( /(<converted>)/g , converted _value ) ;
const result = markdown . translate _emoticons _to _names ( original ) ;
assert . equal ( result , expected ) ;
}
}
} ) ;