2020-08-01 03:43:15 +02:00
"use strict" ;
2020-11-30 23:46:45 +01:00
const { strict : assert } = require ( "assert" ) ;
2021-06-08 04:14:11 +02:00
const markdown _test _cases = require ( "../../zerver/tests/fixtures/markdown_test_cases" ) ;
2023-02-22 23:04:10 +01:00
const markdown _assert = require ( "./lib/markdown_assert" ) ;
2023-12-07 11:41:37 +01:00
const { mock _esm , set _global , zrequire } = require ( "./lib/namespace" ) ;
2023-02-22 23:04:10 +01:00
const { run _test } = require ( "./lib/test" ) ;
const { page _params , user _settings } = require ( "./lib/zpage_params" ) ;
2020-12-01 00:02:16 +01:00
2021-03-30 12:51:54 +02:00
const example _realm _linkifiers = [
{
pattern : "#(?P<id>[0-9]{2,8})" ,
linkifier: Support URL templates for linkifiers.
This swaps out url_format_string from all of our APIs and replaces it
with url_template. Note that the documentation changes in the following
commits will be squashed with this commit.
We change the "url_format" key to "url_template" for the
realm_linkifiers events in event_schema, along with updating
LinkifierDict. "url_template" is the name chosen to normalize
mixed usages of "url_format_string" and "url_format" throughout
the backend.
The markdown processor is updated to stop handling the format string
interpolation and delegate the task template expansion to the uri_template
library instead.
This change affects many test cases. We mostly just replace "%(name)s"
with "{name}", "url_format_string" with "url_template" to make sure that
they still pass. There are some test cases dedicated for testing "%"
escaping, which aren't relevant anymore and are subject to removal.
But for now we keep most of them as-is, and make sure that "%" is always
escaped since we do not use it for variable substitution any more.
Since url_format_string is not populated anymore, a migration is created
to remove this field entirely, and make url_template non-nullable since
we will always populate it. Note that it is possible to have
url_template being null after migration 0422 and before 0424, but
in practice, url_template will not be None after backfilling and the
backend now is always setting url_template.
With the removal of url_format_string, RealmFilter model will now be cleaned
with URL template checks, and the old checks for escapes are removed.
We also modified RealmFilter.clean to skip the validation when the
url_template is invalid. This avoids raising mulitple ValidationError's
when calling full_clean on a linkifier. But we might eventually want to
have a more centric approach to data validation instead of having
the same validation in both the clean method and the validator.
Fixes #23124.
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-10-05 20:55:31 +02:00
url _template : "https://trac.example.com/ticket/{id}" ,
2021-03-30 12:51:54 +02:00
id : 1 ,
} ,
{
pattern : "ZBUG_(?P<id>[0-9]{2,8})" ,
linkifier: Support URL templates for linkifiers.
This swaps out url_format_string from all of our APIs and replaces it
with url_template. Note that the documentation changes in the following
commits will be squashed with this commit.
We change the "url_format" key to "url_template" for the
realm_linkifiers events in event_schema, along with updating
LinkifierDict. "url_template" is the name chosen to normalize
mixed usages of "url_format_string" and "url_format" throughout
the backend.
The markdown processor is updated to stop handling the format string
interpolation and delegate the task template expansion to the uri_template
library instead.
This change affects many test cases. We mostly just replace "%(name)s"
with "{name}", "url_format_string" with "url_template" to make sure that
they still pass. There are some test cases dedicated for testing "%"
escaping, which aren't relevant anymore and are subject to removal.
But for now we keep most of them as-is, and make sure that "%" is always
escaped since we do not use it for variable substitution any more.
Since url_format_string is not populated anymore, a migration is created
to remove this field entirely, and make url_template non-nullable since
we will always populate it. Note that it is possible to have
url_template being null after migration 0422 and before 0424, but
in practice, url_template will not be None after backfilling and the
backend now is always setting url_template.
With the removal of url_format_string, RealmFilter model will now be cleaned
with URL template checks, and the old checks for escapes are removed.
We also modified RealmFilter.clean to skip the validation when the
url_template is invalid. This avoids raising mulitple ValidationError's
when calling full_clean on a linkifier. But we might eventually want to
have a more centric approach to data validation instead of having
the same validation in both the clean method and the validator.
Fixes #23124.
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-10-05 20:55:31 +02:00
url _template : "https://trac2.zulip.net/ticket/{id}" ,
2021-03-30 12:51:54 +02:00
id : 2 ,
} ,
{
pattern : "ZGROUP_(?P<id>[0-9]{2,8}):(?P<zone>[0-9]{1,8})" ,
linkifier: Support URL templates for linkifiers.
This swaps out url_format_string from all of our APIs and replaces it
with url_template. Note that the documentation changes in the following
commits will be squashed with this commit.
We change the "url_format" key to "url_template" for the
realm_linkifiers events in event_schema, along with updating
LinkifierDict. "url_template" is the name chosen to normalize
mixed usages of "url_format_string" and "url_format" throughout
the backend.
The markdown processor is updated to stop handling the format string
interpolation and delegate the task template expansion to the uri_template
library instead.
This change affects many test cases. We mostly just replace "%(name)s"
with "{name}", "url_format_string" with "url_template" to make sure that
they still pass. There are some test cases dedicated for testing "%"
escaping, which aren't relevant anymore and are subject to removal.
But for now we keep most of them as-is, and make sure that "%" is always
escaped since we do not use it for variable substitution any more.
Since url_format_string is not populated anymore, a migration is created
to remove this field entirely, and make url_template non-nullable since
we will always populate it. Note that it is possible to have
url_template being null after migration 0422 and before 0424, but
in practice, url_template will not be None after backfilling and the
backend now is always setting url_template.
With the removal of url_format_string, RealmFilter model will now be cleaned
with URL template checks, and the old checks for escapes are removed.
We also modified RealmFilter.clean to skip the validation when the
url_template is invalid. This avoids raising mulitple ValidationError's
when calling full_clean on a linkifier. But we might eventually want to
have a more centric approach to data validation instead of having
the same validation in both the clean method and the validator.
Fixes #23124.
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-10-05 20:55:31 +02:00
url _template : "https://zone_{zone}.zulip.net/ticket/{id}" ,
2021-03-30 12:51:54 +02:00
id : 3 ,
} ,
linkifier: Support URL templates for linkifiers.
This swaps out url_format_string from all of our APIs and replaces it
with url_template. Note that the documentation changes in the following
commits will be squashed with this commit.
We change the "url_format" key to "url_template" for the
realm_linkifiers events in event_schema, along with updating
LinkifierDict. "url_template" is the name chosen to normalize
mixed usages of "url_format_string" and "url_format" throughout
the backend.
The markdown processor is updated to stop handling the format string
interpolation and delegate the task template expansion to the uri_template
library instead.
This change affects many test cases. We mostly just replace "%(name)s"
with "{name}", "url_format_string" with "url_template" to make sure that
they still pass. There are some test cases dedicated for testing "%"
escaping, which aren't relevant anymore and are subject to removal.
But for now we keep most of them as-is, and make sure that "%" is always
escaped since we do not use it for variable substitution any more.
Since url_format_string is not populated anymore, a migration is created
to remove this field entirely, and make url_template non-nullable since
we will always populate it. Note that it is possible to have
url_template being null after migration 0422 and before 0424, but
in practice, url_template will not be None after backfilling and the
backend now is always setting url_template.
With the removal of url_format_string, RealmFilter model will now be cleaned
with URL template checks, and the old checks for escapes are removed.
We also modified RealmFilter.clean to skip the validation when the
url_template is invalid. This avoids raising mulitple ValidationError's
when calling full_clean on a linkifier. But we might eventually want to
have a more centric approach to data validation instead of having
the same validation in both the clean method and the validator.
Fixes #23124.
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-10-05 20:55:31 +02:00
{
// For example, this linkifier matches:
// FOO_abcde;e;zulip;luxembourg;foo;23;testing
// which expands to:
// https://zone_e.zulip.net/ticket/luxembourg/abcde?name=foo&chapter=23#testi
// This exercises different URL template supported syntax.
pattern :
"FOO_(?P<id>[a-f]{5});(?P<zone>[a-f]);(?P<domain>[a-z]+);(?P<location>[a-z]+);(?P<name>[a-z]{2,8});(?P<chapter>[0-9]{2,3});(?P<fragment>[a-z]{2,8})" ,
url _template :
"https://zone_{zone}{.domain}.net/ticket{/location}{/id}{?name,chapter}{#fragment:5}" ,
id : 4 ,
} ,
2021-03-25 22:35:45 +01:00
] ;
2021-07-28 16:00:58 +02:00
user _settings . translate _emoticons = false ;
2020-07-15 01:29:15 +02:00
2021-05-20 01:15:35 +02:00
set _global ( "document" , { compatMode : "CSS1Compat" } ) ;
2020-12-01 23:21:38 +01:00
2023-12-07 11:41:37 +01:00
mock _esm ( "../src/settings_data" , {
user _can _access _all _other _users : ( ) => false ,
} ) ;
2022-03-25 14:47:51 +01:00
const emoji = zrequire ( "emoji" ) ;
2023-02-22 23:03:47 +01:00
const emoji _codes = zrequire ( "../../static/generated/emoji/emoji_codes.json" ) ;
2021-05-19 06:18:23 +02:00
const linkifiers = zrequire ( "linkifiers" ) ;
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
const pygments _data = zrequire ( "../generated/pygments_data.json" ) ;
2023-02-22 23:03:47 +01:00
const fenced _code = zrequire ( "../shared/src/fenced_code" ) ;
2020-07-15 01:29:15 +02:00
const markdown _config = zrequire ( "markdown_config" ) ;
2021-02-10 04:53:22 +01:00
const markdown = zrequire ( "markdown" ) ;
2020-08-20 21:24:06 +02:00
const people = zrequire ( "people" ) ;
2021-02-10 04:53:22 +01:00
const stream _data = zrequire ( "stream_data" ) ;
const user _groups = zrequire ( "user_groups" ) ;
2020-07-15 01:29:15 +02:00
2020-07-24 01:21:48 +02:00
const emoji _params = {
2016-10-30 20:54:53 +01:00
realm _emoji : {
2020-07-15 00:34:28 +02:00
1 : {
id : 1 ,
2020-07-15 01:29:15 +02:00
name : "burrito" ,
source _url : "/static/generated/emoji/images/emoji/burrito.png" ,
2018-03-11 18:55:20 +01:00
deactivated : false ,
} ,
2016-10-30 20:54:53 +01:00
} ,
2020-07-25 23:59:49 +02:00
emoji _codes ,
2020-07-24 01:21:48 +02:00
} ;
emoji . initialize ( emoji _params ) ;
2020-09-26 06:02:29 +02:00
fenced _code . initialize ( pygments _data ) ;
2017-08-16 22:00:19 +02:00
2019-11-02 00:06:25 +01:00
const cordelia = {
2021-04-11 16:26:54 +02:00
full _name : "Cordelia, Lear's daughter" ,
2016-10-30 20:58:28 +01:00
user _id : 101 ,
2020-07-15 01:29:15 +02:00
email : "cordelia@zulip.com" ,
2017-01-24 01:51:58 +01:00
} ;
2020-05-26 22:34:15 +02:00
people . add _active _user ( cordelia ) ;
2014-01-04 01:18:30 +01:00
2020-05-26 22:34:15 +02:00
people . add _active _user ( {
2020-07-15 01:29:15 +02:00
full _name : "Leo" ,
2017-01-24 00:44:08 +01:00
user _id : 102 ,
2020-07-15 01:29:15 +02:00
email : "leo@zulip.com" ,
2017-01-24 00:44:08 +01:00
} ) ;
2020-05-26 22:34:15 +02:00
people . add _active _user ( {
2020-07-15 01:29:15 +02:00
full _name : "Bobby <h1>Tables</h1>" ,
2018-03-29 00:25:58 +02:00
user _id : 103 ,
2020-07-15 01:29:15 +02:00
email : "bobby@zulip.com" ,
2018-03-29 00:25:58 +02:00
} ) ;
2020-05-26 22:34:15 +02:00
people . add _active _user ( {
2020-07-15 01:29:15 +02:00
full _name : "Mark Twin" ,
2018-08-19 03:39:57 +02:00
user _id : 104 ,
2020-07-15 01:29:15 +02:00
email : "twin1@zulip.com" ,
2018-08-19 03:39:57 +02:00
} ) ;
2020-05-26 22:34:15 +02:00
people . add _active _user ( {
2020-07-15 01:29:15 +02:00
full _name : "Mark Twin" ,
2018-08-19 03:39:57 +02:00
user _id : 105 ,
2020-07-15 01:29:15 +02:00
email : "twin2@zulip.com" ,
2018-08-19 03:39:57 +02:00
} ) ;
2020-05-26 22:34:15 +02:00
people . add _active _user ( {
2020-07-15 01:29:15 +02:00
full _name : "Brother of Bobby|123" ,
2018-08-19 03:39:57 +02:00
user _id : 106 ,
2020-07-15 01:29:15 +02:00
email : "bobby2@zulip.com" ,
2018-08-19 03:39:57 +02:00
} ) ;
2020-05-26 22:34:15 +02:00
people . add _active _user ( {
2019-12-13 01:38:49 +01:00
full _name : "& & &" ,
user _id : 107 ,
email : "ampampamp@zulip.com" ,
} ) ;
2023-12-07 11:41:37 +01:00
people . add _inaccessible _user ( 108 ) ;
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 ,
2021-05-10 07:02:14 +02:00
description : "HTML syntax to check for Markdown edge cases." ,
2018-03-29 00:25:58 +02:00
members : [ ] ,
} ;
2019-12-13 01:38:49 +01:00
const amp _group = {
name : "& & &" ,
id : 4 ,
description : "Check ampersand escaping" ,
members : [ ] ,
} ;
2020-12-01 00:57:57 +01:00
user _groups . add ( hamletcharacters ) ;
user _groups . add ( backend ) ;
user _groups . add ( edgecase _group ) ;
user _groups . add ( amp _group ) ;
2017-11-22 09:11:07 +01:00
2019-11-02 00:06:25 +01:00
const denmark = {
2016-11-10 20:20:40 +01:00
subscribed : false ,
2020-07-15 01:29:15 +02:00
color : "blue" ,
name : "Denmark" ,
2016-11-10 20:20:40 +01:00
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 ,
2020-07-15 01:29:15 +02:00
color : "red" ,
name : "social" ,
2016-11-10 20:20:40 +01:00
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 ,
2020-07-15 01:29:15 +02:00
color : "green" ,
name : "Bobby <h1>Tables</h1>" ,
2018-03-29 00:25:58 +02:00
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 ,
2020-07-15 01:29:15 +02:00
color : "yellow" ,
name : "Bobby <h1" ,
2019-06-21 20:47:09 +02:00
stream _id : 4 ,
is _muted : false ,
} ;
2019-12-13 01:38:49 +01:00
const amp _stream = {
subscribed : true ,
2020-07-15 01:29:15 +02:00
color : "orange" ,
name : "& & &" ,
2019-12-13 01:38:49 +01:00
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
2021-05-19 06:18:23 +02:00
markdown . initialize ( markdown _config . get _helpers ( ) ) ;
linkifiers . initialize ( example _realm _linkifiers ) ;
2013-12-04 17:16:08 +01:00
2021-03-16 12:31:25 +01:00
function test ( label , f ) {
2022-07-10 01:06:33 +02:00
run _test ( label , ( helpers ) => {
2021-04-03 19:07:13 +02:00
page _params . realm _users = [ ] ;
2021-05-19 06:18:23 +02:00
linkifiers . update _linkifier _rules ( example _realm _linkifiers ) ;
2022-07-10 01:06:33 +02:00
f ( helpers ) ;
2021-03-16 12:31:25 +01:00
} ) ;
}
test ( "markdown_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*" ,
2020-07-15 00:34:28 +02:00
'We like to code\n~~~\ndef code():\n we = "like to do"\n~~~' ,
2018-05-07 03:30:13 +02:00
"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**" ,
] ;
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" ,
2020-10-23 02:43:28 +02:00
"Twitter URL https://twitter.com/jacobian/status/407886996565016579" ,
2018-05-07 03:30:13 +02:00
"https://twitter.com/jacobian/status/407886996565016579" ,
"then https://twitter.com/jacobian/status/407886996565016579" ,
2020-10-23 02:43:28 +02:00
"Twitter URL http://twitter.com/jacobian/status/407886996565016579" ,
"YouTube URL https://www.youtube.com/watch?v=HHZ8iqswiCw&feature=youtu.be&a" ,
2018-05-07 03:30:13 +02:00
] ;
2013-12-04 17:16:08 +01:00
2021-01-22 22:29:08 +01:00
for ( const content of no _markup ) {
2017-07-29 02:51:33 +02:00
assert . equal ( markdown . contains _backend _only _syntax ( content ) , false ) ;
2021-01-22 22:29:08 +01:00
}
2013-12-04 17:16:08 +01:00
2021-01-22 22:29:08 +01:00
for ( const content of markup ) {
2017-07-29 02:51:33 +02:00
assert . equal ( markdown . contains _backend _only _syntax ( content ) , true ) ;
2021-01-22 22:29:08 +01:00
}
2018-05-15 12:40:07 +02:00
} ) ;
2013-12-04 17:16:08 +01:00
2021-03-16 12:31:25 +01:00
test ( "marked_shared" , ( ) => {
2020-12-01 00:26:16 +01:00
const tests = markdown _test _cases . regular _tests ;
2017-12-10 09:01:37 +01:00
2021-01-22 22:29:08 +01:00
for ( const test of tests ) {
2018-03-28 10:40:44 +02:00
// Ignore tests if specified
2022-04-09 23:44:38 +02:00
/* istanbul ignore if */
2018-03-28 10:40:44 +02:00
if ( test . ignore === true ) {
2021-01-22 22:29:08 +01:00
continue ;
2018-03-28 10:40:44 +02:00
}
2019-11-02 00:06:25 +01:00
const message = { raw _content : test . input } ;
2021-07-28 16:00:58 +02:00
user _settings . 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 ) {
2020-12-01 00:21:17 +01:00
markdown _assert . notEqual ( test . expected _output , output , error _message ) ;
markdown _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 {
2020-12-01 00:21:17 +01:00
markdown _assert . equal ( test . expected _output , output , error _message ) ;
2017-06-13 23:49:50 +02:00
}
2021-01-22 22:29:08 +01:00
}
2018-05-15 12:40:07 +02:00
} ) ;
2014-01-17 21:08:45 +01:00
2021-03-16 12:31:25 +01:00
test ( "message_flags" , ( ) => {
2020-07-15 01:29:15 +02:00
let message = { raw _content : "@**Leo**" } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2022-03-24 07:01:47 +01:00
assert . ok ( ! message . flags . includes ( "mentioned" ) ) ;
2017-01-24 01:51:58 +01:00
2021-04-11 16:26:54 +02:00
message = { raw _content : "@**Cordelia, Lear's daughter**" } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2022-03-24 07:01:47 +01:00
assert . ok ( message . flags . includes ( "mentioned" ) ) ;
2017-01-24 01:51:58 +01:00
2020-07-15 01:29:15 +02:00
message = { raw _content : "@**all**" } ;
2017-05-09 18:01:43 +02:00
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . ok ( message . flags . includes ( "stream_wildcard_mentioned" ) ) ;
assert . ok ( ! message . flags . includes ( "topic_wildcard_mentioned" ) ) ;
message = { raw _content : "@**topic**" } ;
markdown . apply _markdown ( message ) ;
assert . ok ( ! message . flags . includes ( "stream_wildcard_mentioned" ) ) ;
assert . ok ( message . flags . includes ( "topic_wildcard_mentioned" ) ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-01-24 01:51:58 +01:00
2021-03-16 12:31:25 +01:00
test ( "marked" , ( ) => {
2019-11-02 00:06:25 +01:00
const test _cases = [
2020-07-15 01:29:15 +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>" } ,
2020-07-15 00:34:28 +02:00
{
input : "hello ***foo*** for you" ,
expected : "<p>hello <strong><em>foo</em></strong> for you</p>" ,
} ,
2020-07-15 01:29:15 +02:00
{ input : "__hello__" , expected : "<p>__hello__</p>" } ,
2020-07-15 00:34:28 +02:00
{
input : "\n```\nfenced code\n```\n\nand then after\n" ,
expected :
2020-10-19 04:49:17 +02:00
'<div class="codehilite"><pre><span></span><code>fenced code\n</code></pre></div>\n<p>and then after</p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
2021-05-09 22:29:53 +02:00
input : "\n```\n fenced code trailing whitespace \n```\n\nand then after\n" ,
2020-07-15 00:34:28 +02:00
expected :
2020-10-19 04:49:17 +02:00
'<div class="codehilite"><pre><span></span><code> fenced code trailing whitespace\n</code></pre></div>\n<p>and then after</p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "* a\n* list \n* here" ,
expected : "<ul>\n<li>a</li>\n<li>list </li>\n<li>here</li>\n</ul>" ,
} ,
{
input : "\n```c#\nfenced code special\n```\n\nand then after\n" ,
expected :
2020-10-19 04:49:17 +02:00
'<div class="codehilite" data-code-language="C#"><pre><span></span><code>fenced code special\n</code></pre></div>\n<p>and then after</p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "\n```vb.net\nfenced code dot\n```\n\nand then after\n" ,
expected :
2020-10-19 04:49:17 +02:00
'<div class="codehilite" data-code-language="VB.net"><pre><span></span><code>fenced code dot\n</code></pre></div>\n<p>and then after</p>' ,
2020-07-15 00:34:28 +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 : "<ol>\n<li>an</li>\n<li>ordered </li>\n<li>list</li>\n</ol>" ,
} ,
{
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>" ,
} ,
{
2021-04-11 16:26:54 +02:00
input : "This is a @**CordeLIA, Lear's daughter** mention" ,
2020-07-15 00:34:28 +02:00
expected :
2021-04-11 16:26:54 +02:00
'<p>This is a <span class="user-mention" data-user-id="101">@Cordelia, Lear's daughter</span> mention</p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
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="/#narrow/stream/1-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="/#narrow/stream/1-Denmark">#Denmark</a> and <a class="stream" data-stream-id="2" href="/#narrow/stream/2-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 : "This is a #**Denmark>some topic** stream_topic link" ,
expected :
2020-10-07 13:17:55 +02:00
'<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>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "This has two links: #**Denmark>some topic** and #**social>other topic**." ,
expected :
2020-10-07 13:17:55 +02:00
'<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>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "This is not a #**Denmark>** stream_topic link" ,
expected : "<p>This is not a #**Denmark>** stream_topic 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 <span aria-label="poop" class="emoji emoji-1f4a9" role="img" title="poop">:poop:</span> message</p>' ,
} ,
{
2020-10-07 10:24:09 +02:00
input : "\uD83D\uDCA9" ,
2020-07-15 00:34:28 +02:00
expected :
'<p><span aria-label="poop" class="emoji emoji-1f4a9" role="img" title="poop">:poop:</span></p>' ,
} ,
{
2021-04-11 16:26:54 +02:00
input : "Silent mention: @_**Cordelia, Lear's daughter**" ,
2020-07-15 00:34:28 +02:00
expected :
2021-04-11 16:26:54 +02:00
'<p>Silent mention: <span class="user-mention silent" data-user-id="101">Cordelia, Lear's daughter</span></p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
2021-05-09 22:29:53 +02:00
input : "> Mention in quote: @**Cordelia, Lear's daughter**\n\nMention outside quote: @**Cordelia, Lear's daughter**" ,
2020-07-15 00:34:28 +02:00
expected :
2021-04-11 16:26:54 +02:00
'<blockquote>\n<p>Mention in quote: <span class="user-mention silent" data-user-id="101">Cordelia, Lear's daughter</span></p>\n</blockquote>\n<p>Mention outside quote: <span class="user-mention" data-user-id="101">@Cordelia, Lear's daughter</span></p>' ,
2020-07-15 00:34:28 +02:00
} ,
2021-05-19 21:22:58 +02:00
{
2023-07-11 14:42:31 +02:00
input : "Stream Wildcard mention: @**all**\nStream Wildcard silent mention: @_**all**" ,
2021-05-19 21:22:58 +02:00
expected :
2023-07-11 14:42:31 +02:00
'<p>Stream Wildcard mention: <span class="user-mention" data-user-id="*">@all</span><br>\nStream Wildcard silent mention: <span class="user-mention silent" data-user-id="*">all</span></p>' ,
2021-05-19 21:22:58 +02:00
} ,
{
2023-07-11 14:42:31 +02:00
input : "> Stream Wildcard mention in quote: @**all**\n\n> Another stream wildcard mention in quote: @_**all**" ,
2021-05-19 21:22:58 +02:00
expected :
2023-07-11 14:42:31 +02:00
'<blockquote>\n<p>Stream Wildcard mention in quote: <span class="user-mention silent" data-user-id="*">all</span></p>\n</blockquote>\n<blockquote>\n<p>Another stream wildcard mention in quote: <span class="user-mention silent" data-user-id="*">all</span></p>\n</blockquote>' ,
2021-05-19 21:22:58 +02:00
} ,
{
2023-07-11 14:42:31 +02:00
input : "```quote\nStream Wildcard mention in quote: @**all**\n```\n\n```quote\nAnother stream wildcard mention in quote: @_**all**\n```" ,
2021-05-19 21:22:58 +02:00
expected :
2023-07-11 14:42:31 +02:00
'<blockquote>\n<p>Stream Wildcard mention in quote: <span class="user-mention silent" data-user-id="*">all</span></p>\n</blockquote>\n<blockquote>\n<p>Another stream wildcard mention in quote: <span class="user-mention silent" data-user-id="*">all</span></p>\n</blockquote>' ,
2021-05-19 21:22:58 +02:00
} ,
2023-06-06 09:23:01 +02:00
{
input : "Topic Wildcard mention: @**topic**\nTopic Wildcard silent mention: @_**topic**" ,
expected :
'<p>Topic Wildcard mention: <span class="topic-mention">@topic</span><br>\nTopic Wildcard silent mention: <span class="topic-mention silent">topic</span></p>' ,
} ,
{
input : "> Topic Wildcard mention in quote: @**topic**\n\n> Another topic wildcard mention in quote: @_**topic**" ,
expected :
'<blockquote>\n<p>Topic Wildcard mention in quote: <span class="topic-mention silent">topic</span></p>\n</blockquote>\n<blockquote>\n<p>Another topic wildcard mention in quote: <span class="topic-mention silent">topic</span></p>\n</blockquote>' ,
} ,
{
input : "```quote\nTopic Wildcard mention in quote: @**topic**\n```\n\n```quote\nAnother topic wildcard mention in quote: @_**topic**\n```" ,
expected :
'<blockquote>\n<p>Topic Wildcard mention in quote: <span class="topic-mention silent">topic</span></p>\n</blockquote>\n<blockquote>\n<p>Another topic wildcard mention in quote: <span class="topic-mention silent">topic</span></p>\n</blockquote>' ,
} ,
2021-05-19 21:34:58 +02:00
{
input : "User group mention: @*backend*\nUser group silent mention: @_*hamletcharacters*" ,
expected :
'<p>User group mention: <span class="user-group-mention" data-user-group-id="2">@Backend</span><br>\nUser group silent mention: <span class="user-group-mention silent" data-user-group-id="1">hamletcharacters</span></p>' ,
} ,
2021-05-19 21:38:28 +02:00
{
input : "> User group mention in quote: @*backend*\n\n> Another user group mention in quote: @*hamletcharacters*" ,
expected :
'<blockquote>\n<p>User group mention in quote: <span class="user-group-mention silent" data-user-group-id="2">Backend</span></p>\n</blockquote>\n<blockquote>\n<p>Another user group mention in quote: <span class="user-group-mention silent" data-user-group-id="1">hamletcharacters</span></p>\n</blockquote>' ,
} ,
{
input : "```quote\nUser group mention in quote: @*backend*\n```\n\n```quote\nAnother user group mention in quote: @*hamletcharacters*\n```" ,
expected :
'<blockquote>\n<p>User group mention in quote: <span class="user-group-mention silent" data-user-group-id="2">Backend</span></p>\n</blockquote>\n<blockquote>\n<p>Another user group mention in quote: <span class="user-group-mention silent" data-user-group-id="1">hamletcharacters</span></p>\n</blockquote>' ,
} ,
2023-11-16 23:59:18 +01:00
// Test only those linkifiers which don't return True for
// `contains_backend_only_syntax()`. Those which return True
// are tested separately.
2020-07-15 00:34:28 +02:00
{
2021-03-13 18:15:14 +01:00
input : "This is a linkifier #1234 with text after it" ,
2020-07-15 00:34:28 +02:00
expected :
2021-03-13 18:15:14 +01:00
'<p>This is a linkifier <a href="https://trac.example.com/ticket/1234" title="https://trac.example.com/ticket/1234">#1234</a> with text after it</p>' ,
2020-07-15 00:34:28 +02:00
} ,
linkifier: Support URL templates for linkifiers.
This swaps out url_format_string from all of our APIs and replaces it
with url_template. Note that the documentation changes in the following
commits will be squashed with this commit.
We change the "url_format" key to "url_template" for the
realm_linkifiers events in event_schema, along with updating
LinkifierDict. "url_template" is the name chosen to normalize
mixed usages of "url_format_string" and "url_format" throughout
the backend.
The markdown processor is updated to stop handling the format string
interpolation and delegate the task template expansion to the uri_template
library instead.
This change affects many test cases. We mostly just replace "%(name)s"
with "{name}", "url_format_string" with "url_template" to make sure that
they still pass. There are some test cases dedicated for testing "%"
escaping, which aren't relevant anymore and are subject to removal.
But for now we keep most of them as-is, and make sure that "%" is always
escaped since we do not use it for variable substitution any more.
Since url_format_string is not populated anymore, a migration is created
to remove this field entirely, and make url_template non-nullable since
we will always populate it. Note that it is possible to have
url_template being null after migration 0422 and before 0424, but
in practice, url_template will not be None after backfilling and the
backend now is always setting url_template.
With the removal of url_format_string, RealmFilter model will now be cleaned
with URL template checks, and the old checks for escapes are removed.
We also modified RealmFilter.clean to skip the validation when the
url_template is invalid. This avoids raising mulitple ValidationError's
when calling full_clean on a linkifier. But we might eventually want to
have a more centric approach to data validation instead of having
the same validation in both the clean method and the validator.
Fixes #23124.
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-10-05 20:55:31 +02:00
{
input : "This is a complicated linkifier FOO_abcde;e;zulip;luxembourg;foo;23;testing with text after it" ,
expected :
'<p>This is a complicated linkifier <a href="https://zone_e.zulip.net/ticket/luxembourg/abcde?name=foo&chapter=23#testi" title="https://zone_e.zulip.net/ticket/luxembourg/abcde?name=foo&chapter=23#testi">FOO_abcde;e;zulip;luxembourg;foo;23;testing</a> with text after it</p>' ,
} ,
2021-03-13 18:15:14 +01:00
{ input : "#1234is not a linkifier." , expected : "<p>#1234is not a linkifier.</p>" } ,
2020-07-15 00:34:28 +02:00
{
2021-03-13 18:15:14 +01:00
input : "A pattern written as #1234is not a linkifier." ,
expected : "<p>A pattern written as #1234is not a linkifier.</p>" ,
2020-07-15 00:34:28 +02:00
} ,
{
2021-03-13 18:15:14 +01:00
input : "This is a linkifier with ZGROUP_123:45 groups" ,
2020-07-15 00:34:28 +02:00
expected :
2021-03-13 18:15:14 +01:00
'<p>This is a linkifier with <a href="https://zone_45.zulip.net/ticket/123" title="https://zone_45.zulip.net/ticket/123">ZGROUP_123:45</a> groups</p>' ,
2020-07-15 00:34:28 +02:00
} ,
{ input : "Test *italic*" , expected : "<p>Test <em>italic</em></p>" } ,
{
input : "T\n#**Denmark**" ,
expected :
'<p>T<br>\n<a class="stream" data-stream-id="1" href="/#narrow/stream/1-Denmark">#Denmark</a></p>' ,
} ,
{
2021-04-11 16:26:54 +02:00
input : "T\n@**Cordelia, Lear's daughter**" ,
2020-07-15 00:34:28 +02:00
expected :
2021-04-11 16:26:54 +02:00
'<p>T<br>\n<span class="user-mention" data-user-id="101">@Cordelia, Lear's daughter</span></p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "@**Mark Twin|104** and @**Mark Twin|105** are out to confuse you." ,
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>' ,
} ,
{ input : "@**Invalid User|1234**" , expected : "<p>@**Invalid User|1234**</p>" } ,
{
2021-04-11 16:26:54 +02:00
input : "@**Cordelia, Lear's daughter|103** has a wrong user_id." ,
expected : "<p>@**Cordelia, Lear's daughter|103** has a wrong user_id.</p>" ,
2020-07-15 00:34:28 +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>' ,
} ,
2021-03-23 20:22:04 +01:00
{
input : "@**|106** valid user id." ,
expected :
'<p><span class="user-mention" data-user-id="106">@Brother of Bobby|123</span> valid user id.</p>' ,
} ,
{
input : "@**|123|106** comes under user|id case." ,
expected : "<p>@**|123|106** comes under user|id case.</p>" ,
} ,
2023-12-07 11:41:37 +01:00
{
input : "@**|108** mention inaccessible user using ID." ,
expected : "<p>@**|108** mention inaccessible user using ID.</p>" ,
} ,
{
input : "@**Unknown user|108** mention inaccessible user using name and ID." ,
expected : "<p>@**Unknown user|108** mention inaccessible user using name and ID.</p>" ,
} ,
2021-03-23 20:22:04 +01:00
{ input : "@**|1234** invalid id." , expected : "<p>@**|1234** invalid id.</p>" } ,
2020-07-15 00:34:28 +02: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>' ,
} ,
{ input : "T\n@*notagroup*" , expected : "<p>T<br>\n@*notagroup*</p>" } ,
{
input : "T\n@*backend*" ,
expected :
'<p>T<br>\n<span class="user-group-mention" data-user-group-id="2">@Backend</span></p>' ,
} ,
{ input : "@*notagroup*" , expected : "<p>@*notagroup*</p>" } ,
{
2021-03-13 18:15:14 +01:00
input : "This is a linkifier `hello` with text after it" ,
expected : "<p>This is a linkifier <code>hello</code> with text after it</p>" ,
2020-07-15 00:34:28 +02:00
} ,
2018-03-25 18:10:59 +02:00
// Test the emoticon conversion
2020-07-15 00:34:28 +02:00
{ input : ":)" , expected : "<p>:)</p>" } ,
{
input : ":)" ,
expected :
2020-06-30 21:16:29 +02:00
'<p><span aria-label="smile" class="emoji emoji-1f642" role="img" title="smile">:smile:</span></p>' ,
2020-07-15 00:34:28 +02:00
translate _emoticons : true ,
} ,
2020-10-23 02:43:28 +02:00
// Test HTML escaping in custom Zulip rules
2020-07-15 00:34:28 +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 : ":<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>' ,
} ,
{
input : "@*& & &amp;*" ,
expected :
'<p><span class="user-group-mention" data-user-group-id="4">@& & &amp;</span></p>' ,
} ,
{
input : "@**Bobby <h1>Tables</h1>**" ,
expected :
'<p><span class="user-mention" data-user-id="103">@Bobby <h1>Tables</h1></span></p>' ,
} ,
{
input : "@**& & &amp;**" ,
expected :
'<p><span class="user-mention" data-user-id="107">@& & &amp;</span></p>' ,
} ,
{
input : "#**Bobby <h1>Tables</h1>**" ,
expected :
2020-10-07 13:17:55 +02:00
'<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>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "#**& & &amp;**" ,
expected :
2022-02-28 20:54:06 +01:00
'<p><a class="stream" data-stream-id="5" href="/#narrow/stream/5-.26-.26-.26amp.3B">#& & &amp;</a></p>' ,
2020-07-15 00:34:28 +02:00
} ,
{
input : "#**& & &amp;>& & &amp;**" ,
expected :
2022-02-28 20:54:06 +01:00
'<p><a class="stream-topic" data-stream-id="5" href="/#narrow/stream/5-.26-.26-.26amp.3B/topic/.26.20.26.20.26amp.3B">#& & &amp; > & & &amp;</a></p>' ,
2020-07-15 00:34:28 +02:00
} ,
2017-06-13 23:49:50 +02:00
] ;
2021-01-22 22:29:08 +01:00
for ( const test _case of test _cases ) {
2018-03-25 18:10:59 +02:00
// Disable emoji conversion by default.
2021-07-28 16:00:58 +02:00
user _settings . translate _emoticons = test _case . translate _emoticons || false ;
2018-03-25 18:10:59 +02:00
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 ;
2020-07-28 17:00:59 +02:00
assert . equal ( output , expected ) ;
2021-01-22 22:29:08 +01:00
}
2018-05-15 12:40:07 +02:00
} ) ;
2014-01-24 21:48:56 +01:00
2021-03-16 12:31:25 +01:00
test ( "topic_links" , ( ) => {
2020-07-15 01:29:15 +02: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
2020-07-15 01:29:15 +02: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 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://trac.example.com/ticket/123" ,
text : "#123" ,
} ) ;
2017-06-13 23:49:50 +02:00
2020-07-15 01:29:15 +02: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 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://trac.example.com/ticket/123" ,
text : "#123" ,
} ) ;
assert . deepEqual ( message . topic _links [ 1 ] , {
url : "https://trac.example.com/ticket/456" ,
text : "#456" ,
} ) ;
2017-06-13 23:49:50 +02:00
2020-07-15 01:29:15 +02: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 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://trac2.zulip.net/ticket/123" ,
text : "ZBUG_123" ,
} ) ;
2017-06-13 23:49:50 +02:00
2020-07-15 01:29:15 +02: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 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://trac2.zulip.net/ticket/123" ,
text : "ZBUG_123" ,
} ) ;
assert . deepEqual ( message . topic _links [ 1 ] , {
url : "https://trac.example.com/ticket/456" ,
text : "#456" ,
} ) ;
2017-06-13 23:49:50 +02:00
2020-07-15 01:29:15 +02: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 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://zone_45.zulip.net/ticket/123" ,
text : "ZGROUP_123:45" ,
} ) ;
2017-06-13 23:49:50 +02:00
2020-07-15 01:29:15 +02:00
message = { type : "stream" , topic : "Hello https://google.com" } ;
2019-05-25 16:10:30 +02:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 1 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://google.com" ,
text : "https://google.com" ,
} ) ;
2019-05-25 16:10:30 +02:00
2020-07-15 01:29:15 +02:00
message = { type : "stream" , topic : "#456 https://google.com https://github.com" } ;
2019-05-25 16:10:30 +02:00
markdown . add _topic _links ( message ) ;
2020-02-14 13:39:04 +01:00
assert . equal ( message . topic _links . length , 3 ) ;
2021-01-26 07:32:29 +01:00
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://trac.example.com/ticket/456" ,
text : "#456" ,
} ) ;
assert . deepEqual ( message . topic _links [ 1 ] , {
url : "https://google.com" ,
text : "https://google.com" ,
} ) ;
assert . deepEqual ( message . topic _links [ 2 ] , {
url : "https://github.com" ,
text : "https://github.com" ,
} ) ;
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 ) ;
linkifier: Support URL templates for linkifiers.
This swaps out url_format_string from all of our APIs and replaces it
with url_template. Note that the documentation changes in the following
commits will be squashed with this commit.
We change the "url_format" key to "url_template" for the
realm_linkifiers events in event_schema, along with updating
LinkifierDict. "url_template" is the name chosen to normalize
mixed usages of "url_format_string" and "url_format" throughout
the backend.
The markdown processor is updated to stop handling the format string
interpolation and delegate the task template expansion to the uri_template
library instead.
This change affects many test cases. We mostly just replace "%(name)s"
with "{name}", "url_format_string" with "url_template" to make sure that
they still pass. There are some test cases dedicated for testing "%"
escaping, which aren't relevant anymore and are subject to removal.
But for now we keep most of them as-is, and make sure that "%" is always
escaped since we do not use it for variable substitution any more.
Since url_format_string is not populated anymore, a migration is created
to remove this field entirely, and make url_template non-nullable since
we will always populate it. Note that it is possible to have
url_template being null after migration 0422 and before 0424, but
in practice, url_template will not be None after backfilling and the
backend now is always setting url_template.
With the removal of url_format_string, RealmFilter model will now be cleaned
with URL template checks, and the old checks for escapes are removed.
We also modified RealmFilter.clean to skip the validation when the
url_template is invalid. This avoids raising mulitple ValidationError's
when calling full_clean on a linkifier. But we might eventually want to
have a more centric approach to data validation instead of having
the same validation in both the clean method and the validator.
Fixes #23124.
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-10-05 20:55:31 +02:00
message = { type : "stream" , topic : "FOO_abcde;e;zulip;luxembourg;foo;23;testing" } ;
markdown . add _topic _links ( message ) ;
assert . equal ( message . topic _links . length , 1 ) ;
assert . deepEqual ( message . topic _links [ 0 ] , {
url : "https://zone_e.zulip.net/ticket/luxembourg/abcde?name=foo&chapter=23#testi" ,
text : "FOO_abcde;e;zulip;luxembourg;foo;23;testing" ,
} ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2014-01-27 17:06:59 +01:00
2021-03-16 12:31:25 +01:00
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 ) ;
2021-06-10 08:32:54 +02:00
assert . ok ( ! 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
2021-04-11 16:26:54 +02:00
input = "testing this @**all** @**Cordelia, Lear's daughter**" ;
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 ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , true ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , true ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2023-06-07 19:19:33 +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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , true ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2018-04-03 17:55:57 +02:00
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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , true ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2018-04-03 17:55:57 +02:00
2023-06-07 19:19:33 +02:00
input = "test @**topic**" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
assert . equal ( message . is _me _message , false ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , true ) ;
2023-06-07 19:19:33 +02:00
assert . equal ( message . flags . includes ( "mentioned" ) , 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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2018-01-24 17:18:07 +01:00
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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2017-06-13 23:49:50 +02:00
2023-06-07 19:19:33 +02:00
input = "test @topic" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2023-06-07 19:19:33 +02:00
assert . equal ( message . flags . includes ( "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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2018-01-24 17:18:07 +01:00
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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "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 ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2021-05-19 21:22:58 +02:00
input = "test @_**all**" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2021-05-19 21:22:58 +02:00
input = "> test @**all**" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2021-05-19 21:34:58 +02:00
2023-06-07 19:19:33 +02:00
input = "test @_**topic**" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2023-06-07 19:19:33 +02:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
input = "> test @**topic**" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2023-06-07 19:19:33 +02:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2021-05-19 21:34:58 +02:00
input = "test @_*hamletcharacters*" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2021-05-19 21:38:28 +02:00
input = "> test @*hamletcharacters*" ;
message = { topic : "No links here" , raw _content : input } ;
markdown . apply _markdown ( message ) ;
2023-11-03 15:20:44 +01:00
assert . equal ( message . flags . includes ( "stream_wildcard_mentioned" ) , false ) ;
assert . equal ( message . flags . includes ( "topic_wildcard_mentioned" ) , false ) ;
2022-03-24 07:01:47 +01:00
assert . equal ( message . flags . includes ( "mentioned" ) , false ) ;
2018-05-15 12:40:07 +02:00
} ) ;
2017-06-13 23:26:27 +02:00
2023-11-16 23:59:18 +01:00
test ( "backend_only_linkifiers" , ( ) => {
const backend _only _linkifiers = [
"Here is the PR-#123." ,
"Function abc() was introduced in (PR)#123." ,
] ;
for ( const content of backend _only _linkifiers ) {
assert . equal ( markdown . contains _backend _only _syntax ( content ) , true ) ;
}
} ) ;
2021-03-16 12:31:25 +01:00
test ( "translate_emoticons_to_names" , ( ) => {
2022-04-04 18:05:02 +02:00
const get _emoticon _translations = emoji . get _emoticon _translations ;
function translate _emoticons _to _names ( src ) {
return markdown . translate _emoticons _to _names ( { src , get _emoticon _translations } ) ;
}
2020-02-15 15:21:32 +01:00
// Simple test
2020-07-15 01:29:15 +02:00
const test _text = "Testing :)" ;
2020-06-30 21:16:29 +02:00
const expected = "Testing :smile:" ;
2022-04-04 18:05:02 +02:00
const result = translate _emoticons _to _names ( test _text ) ;
2020-07-28 17:00:59 +02:00
assert . equal ( result , expected ) ;
2020-02-15 15:21:32 +01:00
// Extensive tests.
// The following code loops over the test cases and each emoticon conversion
// to generate multiple test cases.
for ( const [ shortcut , full _name ] of Object . entries ( emoji _codes . emoticon _conversions ) ) {
2022-03-02 03:45:35 +01:00
for ( const { original , expected } of [
{ name : ` only emoticon ` , original : shortcut , expected : full _name } ,
{ name : ` space at start ` , original : ` ${ shortcut } ` , expected : ` ${ full _name } ` } ,
{ name : ` space at end ` , original : ` ${ shortcut } ` , expected : ` ${ full _name } ` } ,
{ name : ` symbol at end ` , original : ` ${ shortcut } ! ` , expected : ` ${ full _name } ! ` } ,
{
name : ` symbol at start ` ,
original : ` Hello, ${ shortcut } ` ,
expected : ` Hello, ${ full _name } ` ,
} ,
{ name : ` after a word ` , original : ` Hello ${ shortcut } ` , expected : ` Hello ${ shortcut } ` } ,
{
name : ` between words ` ,
original : ` Hello ${ shortcut } World ` ,
expected : ` Hello ${ shortcut } World ` ,
} ,
{
name : ` end of sentence ` ,
original : ` End of sentence. ${ shortcut } ` ,
expected : ` End of sentence. ${ full _name } ` ,
} ,
{
name : ` between symbols ` ,
original : ` Hello. ${ shortcut } ! World. ` ,
expected : ` Hello. ${ shortcut } ! World. ` ,
} ,
{
name : ` before end of sentence ` ,
original : ` Hello ${ shortcut } ! ` ,
expected : ` Hello ${ full _name } ! ` ,
} ,
] ) {
2022-04-04 18:05:02 +02:00
const result = translate _emoticons _to _names ( original ) ;
2020-02-15 15:21:32 +01:00
assert . equal ( result , expected ) ;
}
}
} ) ;
2020-07-25 17:50:34 +02:00
2022-03-31 14:16:20 +02:00
test ( "parse_non_message" , ( ) => {
assert . equal ( markdown . parse _non _message ( "type `/day`" ) , "<p>type <code>/day</code></p>" ) ;
} ) ;
2022-07-10 01:06:33 +02:00
test ( "missing unicode emojis" , ( { override } ) => {
2020-10-07 10:24:09 +02:00
const message = { raw _content : "\u{1F6B2}" } ;
2020-07-25 17:50:34 +02:00
markdown . apply _markdown ( message ) ;
assert . equal (
message . content ,
'<p><span aria-label="bike" class="emoji emoji-1f6b2" role="img" title="bike">:bike:</span></p>' ,
) ;
2022-07-10 01:06:33 +02:00
// Now simulate that we don't know this emoji name.
override ( emoji _codes . codepoint _to _name , "1f6b2" , undefined ) ;
2022-03-29 16:28:28 +02:00
markdown . initialize ( markdown _config . get _helpers ( ) ) ;
2021-02-14 08:44:04 +01:00
markdown . apply _markdown ( message ) ;
2020-10-07 10:24:09 +02:00
assert . equal ( message . content , "<p>\u{1F6B2}</p>" ) ;
2020-07-25 17:50:34 +02:00
} ) ;
2021-05-18 17:32:04 +02:00
2022-07-09 23:25:05 +02:00
test ( "katex_throws_unexpected_exceptions" , ( { override _rewire } ) => {
2021-05-18 17:32:04 +02:00
const message = { raw _content : "$$a$$" } ;
2022-07-09 23:25:05 +02:00
override _rewire ( markdown , "katex" , {
2022-11-17 23:33:43 +01:00
renderToString ( ) {
2022-07-09 23:25:05 +02:00
throw new Error ( "some-exception" ) ;
2021-05-18 17:32:04 +02:00
} ,
2022-07-09 23:25:05 +02:00
} ) ;
assert . throws ( ( ) => markdown . apply _markdown ( message ) , {
name : "Error" ,
message : "some-exception\nPlease report this to https://zulip.com/development-community/" ,
} ) ;
2021-05-18 17:32:04 +02:00
} ) ;