diff --git a/frontend_tests/node_tests/markdown.js b/frontend_tests/node_tests/markdown.js index 883e1525f6..808e0ffd21 100644 --- a/frontend_tests/node_tests/markdown.js +++ b/frontend_tests/node_tests/markdown.js @@ -229,8 +229,15 @@ var bugdown_data = JSON.parse(fs.readFileSync(path.join(__dirname, '../../zerver expected: '

:poop:

'}, {input: '\u{1f937}', expected: '

\u{1f937}

' }, + // 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: '

This is a realm filter #1234 with text after it

'}, + {input: '#1234is not a realm filter.', + expected: '

#1234is not a realm filter.

'}, + {input: 'A pattern written as #1234is not a realm filter.', + expected: '

A pattern written as #1234is not a realm filter.

'}, {input: 'This is a realm filter with ZGROUP_123:45 groups', expected: '

This is a realm filter with ZGROUP_123:45 groups

'}, {input: 'This is an !avatar(cordelia@zulip.com) of Cordelia Lear', @@ -335,12 +342,22 @@ var bugdown_data = JSON.parse(fs.readFileSync(path.join(__dirname, '../../zerver assert(message.flags.indexOf('mentioned') === -1); }()); +(function test_backend_only_realm_filters() { + var 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); + }); +}()); + (function 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' ]]); var actual_value = (marked.InlineLexer.rules.zulip.realm_filters); - var expected_value = [ /\/aa\/g/gim, /\/aa\/g/g ]; + var expected_value = [ /\/aa\/g(?![\w])/gim, /\/aa\/g(?![\w])/g ]; assert.deepEqual(actual_value, expected_value); }()); diff --git a/static/js/markdown.js b/static/js/markdown.js index 95ef63eb67..5e18ab5784 100644 --- a/static/js/markdown.js +++ b/static/js/markdown.js @@ -30,7 +30,16 @@ exports.contains_backend_only_syntax = function (content) { var markedup = _.find(backend_only_markdown_re, function (re) { return re.test(content); }); - return markedup !== undefined; + + // If a realm filter doesn't start with some specified characters + // then don't render it locally. It is workaround for the fact that + // javascript regex doesn't support lookbehind. + var false_filter_match = _.find(realm_filter_list, function (re) { + var pattern = /(?:[^\s'"\(,:<])/.source + re[0].source + /(?![\w])/.source; + var regex = new RegExp(pattern); + return regex.test(content); + }); + return markedup !== undefined || false_filter_match !== undefined; }; function push_uniquely(lst, elem) { @@ -221,6 +230,15 @@ function python_to_js_filter(pattern, url) { }); pattern = pattern.replace(inline_flag_re, ""); } + // Ideally we should have been checking that realm filters + // begin with certain characters but since there is no + // support for negative lookbehind in javascript, we check + // for this condition in `contains_backend_only_syntax()` + // function. If the condition is satisfied then the message + // is rendered locally, otherwise, we return false there and + // message is rendered on the backend which has proper support + // for negative lookbehind. + pattern = pattern + /(?![\w])/.source; return [new RegExp(pattern, js_flags), url]; }