mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	These functions were just shims that were
used in the somewhat painful migration from
subject_* to topic_*.
The commit 4572be8c27
fixed it so that the client never needs to
deal with "subject_links".
So now we just go back to simpler code:
    message.topic_links = links
    links = message.topic_links
		
	
		
			
				
	
	
		
			646 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			646 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| zrequire('hash_util');
 | |
| set_global('katex', zrequire('katex', 'katex/dist/katex.min.js'));
 | |
| set_global('marked', zrequire('marked', 'third/marked/lib/marked'));
 | |
| set_global('i18n', global.stub_i18n);
 | |
| 
 | |
| zrequire('fenced_code');
 | |
| zrequire('stream_data');
 | |
| zrequire('people');
 | |
| zrequire('user_groups');
 | |
| const emoji_codes = zrequire('emoji_codes', 'generated/emoji/emoji_codes.json');
 | |
| zrequire('emoji');
 | |
| zrequire('message_store');
 | |
| zrequire('markdown');
 | |
| 
 | |
| set_global('location', {
 | |
|     origin: 'http://zulip.zulipdev.com',
 | |
| });
 | |
| 
 | |
| set_global('page_params', {
 | |
|     realm_users: [],
 | |
|     realm_emoji: {
 | |
|         1: {id: 1,
 | |
|             name: 'burrito',
 | |
|             source_url: '/static/generated/emoji/images/emoji/burrito.png',
 | |
|             deactivated: false,
 | |
|         },
 | |
|     },
 | |
|     realm_filters: [
 | |
|         [
 | |
|             "#(?P<id>[0-9]{2,8})",
 | |
|             "https://trac.zulip.net/ticket/%(id)s",
 | |
|         ],
 | |
|         [
 | |
|             "ZBUG_(?P<id>[0-9]{2,8})",
 | |
|             "https://trac2.zulip.net/ticket/%(id)s",
 | |
|         ],
 | |
|         [
 | |
|             "ZGROUP_(?P<id>[0-9]{2,8}):(?P<zone>[0-9]{1,8})",
 | |
|             "https://zone_%(zone)s.zulip.net/ticket/%(id)s",
 | |
|         ],
 | |
|     ],
 | |
|     translate_emoticons: false,
 | |
| });
 | |
| 
 | |
| set_global('blueslip', global.make_zblueslip());
 | |
| 
 | |
| set_global('Image', function () {
 | |
|     return {};
 | |
| });
 | |
| emoji.initialize();
 | |
| 
 | |
| const doc = "";
 | |
| set_global('document', doc);
 | |
| 
 | |
| set_global('$', global.make_zjquery());
 | |
| 
 | |
| set_global('feature_flags', {local_echo: true});
 | |
| 
 | |
| const people = global.people;
 | |
| 
 | |
| const cordelia = {
 | |
|     full_name: 'Cordelia Lear',
 | |
|     user_id: 101,
 | |
|     email: 'cordelia@zulip.com',
 | |
| };
 | |
| people.add(cordelia);
 | |
| 
 | |
| people.add({
 | |
|     full_name: 'Leo',
 | |
|     user_id: 102,
 | |
|     email: 'leo@zulip.com',
 | |
| });
 | |
| 
 | |
| people.add({
 | |
|     full_name: 'Bobby <h1>Tables</h1>',
 | |
|     user_id: 103,
 | |
|     email: 'bobby@zulip.com',
 | |
| });
 | |
| 
 | |
| 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',
 | |
| });
 | |
| 
 | |
| people.add({
 | |
|     full_name: "& & &",
 | |
|     user_id: 107,
 | |
|     email: "ampampamp@zulip.com",
 | |
| });
 | |
| 
 | |
| people.initialize_current_user(cordelia.user_id);
 | |
| 
 | |
| const hamletcharacters = {
 | |
|     name: "hamletcharacters",
 | |
|     id: 1,
 | |
|     description: "Characters of Hamlet",
 | |
|     members: [cordelia.user_id],
 | |
| };
 | |
| 
 | |
| const backend = {
 | |
|     name: "Backend",
 | |
|     id: 2,
 | |
|     description: "Backend team",
 | |
|     members: [],
 | |
| };
 | |
| 
 | |
| const edgecase_group = {
 | |
|     name: "Bobby <h1>Tables</h1>",
 | |
|     id: 3,
 | |
|     description: "HTML Syntax to check for Markdown edge cases.",
 | |
|     members: [],
 | |
| };
 | |
| 
 | |
| const amp_group = {
 | |
|     name: "& & &",
 | |
|     id: 4,
 | |
|     description: "Check ampersand escaping",
 | |
|     members: [],
 | |
| };
 | |
| 
 | |
| global.user_groups.add(hamletcharacters);
 | |
| global.user_groups.add(backend);
 | |
| global.user_groups.add(edgecase_group);
 | |
| global.user_groups.add(amp_group);
 | |
| 
 | |
| const stream_data = global.stream_data;
 | |
| const denmark = {
 | |
|     subscribed: false,
 | |
|     color: 'blue',
 | |
|     name: 'Denmark',
 | |
|     stream_id: 1,
 | |
|     is_muted: true,
 | |
| };
 | |
| const social = {
 | |
|     subscribed: true,
 | |
|     color: 'red',
 | |
|     name: 'social',
 | |
|     stream_id: 2,
 | |
|     is_muted: false,
 | |
|     invite_only: true,
 | |
| };
 | |
| const edgecase_stream = {
 | |
|     subscribed: true,
 | |
|     color: 'green',
 | |
|     name: 'Bobby <h1>Tables</h1>',
 | |
|     stream_id: 3,
 | |
|     is_muted: false,
 | |
| };
 | |
| const edgecase_stream_2 = {
 | |
|     subscribed: true,
 | |
|     color: 'yellow',
 | |
|     name: 'Bobby <h1',
 | |
|     stream_id: 4,
 | |
|     is_muted: false,
 | |
| };
 | |
| const amp_stream = {
 | |
|     subscribed: true,
 | |
|     color: 'orange',
 | |
|     name: '& & &',
 | |
|     stream_id: 5,
 | |
|     is_muted: false,
 | |
| };
 | |
| stream_data.add_sub(denmark);
 | |
| stream_data.add_sub(social);
 | |
| stream_data.add_sub(edgecase_stream);
 | |
| stream_data.add_sub(edgecase_stream_2);
 | |
| // Note: edgecase_stream cannot be mentioned because it is caught by
 | |
| // streamTopicHandler and it would be parsed as edgecase_stream_2.
 | |
| stream_data.add_sub(amp_stream);
 | |
| 
 | |
| // Check the default behavior of fenced code blocks
 | |
| // works properly before markdown is initialized.
 | |
| run_test('fenced_block_defaults', () => {
 | |
|     const input = '\n```\nfenced code\n```\n\nand then after\n';
 | |
|     const expected = '\n\n<div class="codehilite"><pre><span></span>fenced code\n</pre></div>\n\n\n\nand then after\n\n';
 | |
|     const output = fenced_code.process_fenced_code(input);
 | |
|     assert.equal(output, expected);
 | |
| });
 | |
| 
 | |
| markdown.initialize();
 | |
| 
 | |
| const bugdown_data = global.read_fixture_data('markdown_test_cases.json');
 | |
| 
 | |
| run_test('bugdown_detection', () => {
 | |
|     const no_markup = [
 | |
|         "This is a plaintext message",
 | |
|         "This is a plaintext: message",
 | |
|         "This is a :plaintext message",
 | |
|         "This is a :plaintext message: message",
 | |
|         "Contains a not an image.jpeg/ok file",
 | |
|         "Contains a not an http://www.google.com/ok/image.png/stop file",
 | |
|         "No png to be found here, a png",
 | |
|         "No user mention **leo**",
 | |
|         "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",
 | |
|     ];
 | |
| 
 | |
|     const markup = [
 | |
|         "Contains a https://zulip.com/image.png file",
 | |
|         "Contains a https://zulip.com/image.jpg file",
 | |
|         "https://zulip.com/image.jpg",
 | |
|         "also https://zulip.com/image.jpg",
 | |
|         "https://zulip.com/image.jpg too",
 | |
|         "Contains a zulip.com/foo.jpeg file",
 | |
|         "Contains a https://zulip.com/image.png file",
 | |
|         "twitter url https://twitter.com/jacobian/status/407886996565016579",
 | |
|         "https://twitter.com/jacobian/status/407886996565016579",
 | |
|         "then https://twitter.com/jacobian/status/407886996565016579",
 | |
|         "twitter url http://twitter.com/jacobian/status/407886996565016579",
 | |
|         "youtube url https://www.youtube.com/watch?v=HHZ8iqswiCw&feature=youtu.be&a",
 | |
|     ];
 | |
| 
 | |
|     no_markup.forEach(function (content) {
 | |
|         assert.equal(markdown.contains_backend_only_syntax(content), false);
 | |
|     });
 | |
| 
 | |
|     markup.forEach(function (content) {
 | |
|         assert.equal(markdown.contains_backend_only_syntax(content), true);
 | |
|     });
 | |
| });
 | |
| 
 | |
| run_test('marked_shared', () => {
 | |
|     const tests = bugdown_data.regular_tests;
 | |
| 
 | |
|     tests.forEach(function (test) {
 | |
| 
 | |
|         // Ignore tests if specified
 | |
|         if (test.ignore === true) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         const message = {raw_content: test.input};
 | |
|         page_params.translate_emoticons = test.translate_emoticons || false;
 | |
|         markdown.apply_markdown(message);
 | |
|         const output = message.content;
 | |
|         const error_message = `Failure in test: ${test.name}`;
 | |
|         if (test.marked_expected_output) {
 | |
|             global.bugdown_assert.notEqual(test.expected_output, output, error_message);
 | |
|             global.bugdown_assert.equal(test.marked_expected_output, output, error_message);
 | |
|         } else if (test.backend_only_rendering) {
 | |
|             assert.equal(markdown.contains_backend_only_syntax(test.input), true);
 | |
|         } else {
 | |
|             global.bugdown_assert.equal(test.expected_output, output, error_message);
 | |
|         }
 | |
|     });
 | |
| });
 | |
| 
 | |
| run_test('message_flags', () => {
 | |
|     let message = {raw_content: '@**Leo**'};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert(!message.mentioned);
 | |
|     assert(!message.mentioned_me_directly);
 | |
| 
 | |
|     message = {raw_content: '@**Cordelia Lear**'};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert(message.mentioned);
 | |
|     assert(message.mentioned_me_directly);
 | |
| 
 | |
|     message = {raw_content: '@**all**'};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert(message.mentioned);
 | |
|     assert(!message.mentioned_me_directly);
 | |
| });
 | |
| 
 | |
| run_test('marked', () => {
 | |
|     const test_cases = [
 | |
|         {input: 'hello', expected: '<p>hello</p>'},
 | |
|         {input: 'hello there', expected: '<p>hello there</p>'},
 | |
|         {input: 'hello **bold** for you', expected: '<p>hello <strong>bold</strong> for you</p>'},
 | |
|         {input: 'hello ***foo*** for you', expected: '<p>hello <strong><em>foo</em></strong> for you</p>'},
 | |
|         {input: '__hello__', expected: '<p>__hello__</p>'},
 | |
|         {input: '\n```\nfenced code\n```\n\nand then after\n',
 | |
|          expected: '<div class="codehilite"><pre><span></span>fenced code\n</pre></div>\n\n\n<p>and then after</p>'},
 | |
|         {input: '\n```\n    fenced code trailing whitespace            \n```\n\nand then after\n',
 | |
|          expected: '<div class="codehilite"><pre><span></span>    fenced code trailing whitespace\n</pre></div>\n\n\n<p>and then after</p>'},
 | |
|         {input: '* a\n* list \n* here',
 | |
|          expected: '<ul>\n<li>a</li>\n<li>list </li>\n<li>here</li>\n</ul>'},
 | |
|         {input: '\n```c#\nfenced code special\n```\n\nand then after\n',
 | |
|          expected: '<div class="codehilite"><pre><span></span>fenced code special\n</pre></div>\n\n\n<p>and then after</p>'},
 | |
|         {input: '\n```vb.net\nfenced code dot\n```\n\nand then after\n',
 | |
|          expected: '<div class="codehilite"><pre><span></span>fenced code dot\n</pre></div>\n\n\n<p>and then after</p>'},
 | |
|         {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>'},
 | |
|         {input: 'This is a @**Cordelia Lear** mention',
 | |
|          expected: '<p>This is a <span class="user-mention" data-user-id="101">@Cordelia Lear</span> mention</p>'},
 | |
|         {input: 'These @ @**** are not mentions',
 | |
|          expected: '<p>These @ @<em>**</em> are not mentions</p>'},
 | |
|         {input: 'These # #**** are not mentions',
 | |
|          expected: '<p>These # #<em>**</em> are not mentions</p>'},
 | |
|         {input: 'These @* are not mentions',
 | |
|          expected: '<p>These @* are not mentions</p>'},
 | |
|         {input: 'These #* #*** are also not mentions',
 | |
|          expected: '<p>These #* #*** are also not mentions</p>'},
 | |
|         {input: 'This is a #**Denmark** stream link',
 | |
|          expected: '<p>This is a <a class="stream" data-stream-id="1" href="/#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: '<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>'},
 | |
|         {input: 'This has two links: #**Denmark>some topic** and #**social>other topic**.',
 | |
|          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>'},
 | |
|         {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>'},
 | |
|         {input: "\ud83d\udca9",
 | |
|          expected: '<p><span aria-label="poop" class="emoji emoji-1f4a9" role="img" title="poop">:poop:</span></p>'},
 | |
|         {input: '\u{1f6b2}',
 | |
|          expected: '<p>\u{1f6b2}</p>' },
 | |
|         {input: 'Silent mention: @_**Cordelia Lear**',
 | |
|          expected: '<p>Silent mention: <span class="user-mention silent" data-user-id="101">Cordelia Lear</span></p>'},
 | |
|         {input: '> Mention in quote: @**Cordelia Lear**\n\nMention outside quote: @**Cordelia Lear**',
 | |
|          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>'},
 | |
|         // Test only those realm filters which don't return True for
 | |
|         // `contains_backend_only_syntax()`. Those which return True
 | |
|         // are tested separately.
 | |
|         {input: 'This is a realm filter #1234 with text after it',
 | |
|          expected: '<p>This is a realm filter <a href="https://trac.zulip.net/ticket/1234" target="_blank" title="https://trac.zulip.net/ticket/1234">#1234</a> with text after it</p>'},
 | |
|         {input: '#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>'},
 | |
|         {input: 'This is a realm filter with ZGROUP_123:45 groups',
 | |
|          expected: '<p>This is a realm filter with <a href="https://zone_45.zulip.net/ticket/123" target="_blank" title="https://zone_45.zulip.net/ticket/123">ZGROUP_123:45</a> groups</p>'},
 | |
|         {input: 'This is an !avatar(cordelia@zulip.com) of Cordelia Lear',
 | |
|          expected: '<p>This is an <img alt="cordelia@zulip.com" class="message_body_gravatar" src="/avatar/cordelia@zulip.com?s=30" title="cordelia@zulip.com"> of Cordelia Lear</p>'},
 | |
|         {input: 'This is a !gravatar(cordelia@zulip.com) of Cordelia Lear',
 | |
|          expected: '<p>This is a <img alt="cordelia@zulip.com" class="message_body_gravatar" src="/avatar/cordelia@zulip.com?s=30" title="cordelia@zulip.com"> of Cordelia Lear</p>'},
 | |
|         {input: 'Test *italic*',
 | |
|          expected: '<p>Test <em>italic</em></p>'},
 | |
|         {input: 'T\n#**Denmark**',
 | |
|          expected: '<p>T<br>\n<a class="stream" data-stream-id="1" href="/#narrow/stream/1-Denmark">#Denmark</a></p>'},
 | |
|         {input: 'T\n@**Cordelia Lear**',
 | |
|          expected: '<p>T<br>\n<span class="user-mention" data-user-id="101">@Cordelia Lear</span></p>'},
 | |
|         {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>'},
 | |
|         {input: '@**Cordelia Lear|103** has a wrong user_id.',
 | |
|          expected: '<p>@**Cordelia Lear|103** has a wrong user_id.</p>'},
 | |
|         {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>'},
 | |
|         {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>'},
 | |
|         {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>'},
 | |
|         // Test the emoticon conversion
 | |
|         {input: ':)',
 | |
|          expected: '<p>:)</p>'},
 | |
|         {input: ':)',
 | |
|          expected: '<p><span aria-label="slight smile" class="emoji emoji-1f642" role="img" title="slight smile">:slight_smile:</span></p>',
 | |
|          translate_emoticons: true},
 | |
|         // Test HTML Escape in Custom Zulip Rules
 | |
|         {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>'},
 | |
|         {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: '<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>'},
 | |
|         {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>'},
 | |
|     ];
 | |
| 
 | |
|     // We remove one of the unicode emoji we put as input in one of the test
 | |
|     // 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'];
 | |
| 
 | |
|     test_cases.forEach(function (test_case) {
 | |
|         // Disable emoji conversion by default.
 | |
|         page_params.translate_emoticons = test_case.translate_emoticons || false;
 | |
| 
 | |
|         const input = test_case.input;
 | |
|         const expected = test_case.expected;
 | |
| 
 | |
|         const message = {raw_content: input};
 | |
|         markdown.apply_markdown(message);
 | |
|         const output = message.content;
 | |
|         assert.equal(expected, output);
 | |
|     });
 | |
| });
 | |
| 
 | |
| run_test('topic_links', () => {
 | |
|     let message = {type: 'stream', topic: "No links here"};
 | |
|     markdown.add_topic_links(message);
 | |
|     assert.equal(message.topic_links.length, 0);
 | |
| 
 | |
|     message = {type: 'stream', topic: "One #123 link here"};
 | |
|     markdown.add_topic_links(message);
 | |
|     assert.equal(message.topic_links.length, 1);
 | |
|     assert.equal(message.topic_links[0], "https://trac.zulip.net/ticket/123");
 | |
| 
 | |
|     message = {type: 'stream', topic: "Two #123 #456 link here"};
 | |
|     markdown.add_topic_links(message);
 | |
|     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");
 | |
| 
 | |
|     message = {type: 'stream', topic: "New ZBUG_123 link here"};
 | |
|     markdown.add_topic_links(message);
 | |
|     assert.equal(message.topic_links.length, 1);
 | |
|     assert.equal(message.topic_links[0], "https://trac2.zulip.net/ticket/123");
 | |
| 
 | |
|     message = {type: 'stream', topic: "New ZBUG_123 with #456 link here"};
 | |
|     markdown.add_topic_links(message);
 | |
|     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"));
 | |
| 
 | |
|     message = {type: 'stream', topic: "One ZGROUP_123:45 link here"};
 | |
|     markdown.add_topic_links(message);
 | |
|     assert.equal(message.topic_links.length, 1);
 | |
|     assert.equal(message.topic_links[0], "https://zone_45.zulip.net/ticket/123");
 | |
| 
 | |
|     message = {type: 'stream', topic: "Hello https://google.com"};
 | |
|     markdown.add_topic_links(message);
 | |
|     assert.equal(message.topic_links.length, 1);
 | |
|     assert.equal(message.topic_links[0], "https://google.com");
 | |
| 
 | |
|     message = {type: 'stream', topic: "#456 https://google.com https://github.com"};
 | |
|     markdown.add_topic_links(message);
 | |
|     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"));
 | |
| 
 | |
|     message = {type: "not-stream"};
 | |
|     markdown.add_topic_links(message);
 | |
|     assert.equal(message.topic_links.length, 0);
 | |
| });
 | |
| 
 | |
| run_test('message_flags', () => {
 | |
|     let input = "/me is testing this";
 | |
|     let message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
| 
 | |
|     assert.equal(message.is_me_message, true);
 | |
|     assert(!message.unread);
 | |
| 
 | |
|     input = "/me is testing\nthis";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
| 
 | |
|     assert.equal(message.is_me_message, true);
 | |
| 
 | |
|     input = "testing this @**all** @**Cordelia Lear**";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
| 
 | |
|     assert.equal(message.is_me_message, false);
 | |
|     assert.equal(message.mentioned, true);
 | |
|     assert.equal(message.mentioned_me_directly, true);
 | |
| 
 | |
|     input = "test @**everyone**";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     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**";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     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 @all";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, false);
 | |
| 
 | |
|     input = "test @everyone";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, false);
 | |
| 
 | |
|     input = "test @any";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, false);
 | |
| 
 | |
|     input = "test @alleycat.com";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, false);
 | |
| 
 | |
|     input = "test @*hamletcharacters*";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, true);
 | |
| 
 | |
|     input = "test @*backend*";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, false);
 | |
| 
 | |
|     input = "test @**invalid_user**";
 | |
|     message = {topic: "No links here", raw_content: input};
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(message.mentioned, false);
 | |
| });
 | |
| 
 | |
| run_test('backend_only_realm_filters', () => {
 | |
|     const backend_only_realm_filters = [
 | |
|         '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);
 | |
|     });
 | |
| });
 | |
| 
 | |
| run_test('python_to_js_filter', () => {
 | |
|     // The only way to reach python_to_js_filter is indirectly, hence the call
 | |
|     // to set_realm_filters.
 | |
|     markdown.set_realm_filters([['/a(?im)a/g'], ['/a(?L)a/g']]);
 | |
|     let actual_value = marked.InlineLexer.rules.zulip.realm_filters;
 | |
|     let expected_value = [/\/aa\/g(?![\w])/gim, /\/aa\/g(?![\w])/g];
 | |
|     assert.deepEqual(actual_value, expected_value);
 | |
|     // Test case with multiple replacements.
 | |
|     markdown.set_realm_filters([['#cf(?P<contest>[0-9]+)(?P<problem>[A-Z][0-9A-Z]*)', 'http://google.com']]);
 | |
|     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);
 | |
|     // Test incorrect syntax.
 | |
|     blueslip.set_test_data('error', 'python_to_js_filter: Invalid regular expression: /!@#@(!#&((!&(@#((?![\\w])/: Unterminated group');
 | |
|     markdown.set_realm_filters([['!@#@(!#&((!&(@#(', 'http://google.com']]);
 | |
|     actual_value = marked.InlineLexer.rules.zulip.realm_filters;
 | |
|     expected_value = [];
 | |
|     assert.deepEqual(actual_value, expected_value);
 | |
|     assert.equal(blueslip.get_test_logs('error').length, 1);
 | |
|     blueslip.clear_test_data();
 | |
| });
 | |
| 
 | |
| run_test('katex_throws_unexpected_exceptions', () => {
 | |
|     katex.renderToString = function () { throw new Error('some-exception'); };
 | |
|     blueslip.set_test_data('error', 'Error: some-exception');
 | |
|     const message = { raw_content: '$$a$$' };
 | |
|     markdown.apply_markdown(message);
 | |
|     assert.equal(blueslip.get_test_logs('error').length, 1);
 | |
|     blueslip.clear_test_data();
 | |
| });
 | |
| 
 | |
| 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');
 | |
| });
 | |
| 
 | |
| 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);
 | |
|         }
 | |
|     }
 | |
| });
 |