diff --git a/frontend_tests/node_tests/emoji.js b/frontend_tests/node_tests/emoji.js index 9c7d8ab48d..9c42cbe788 100644 --- a/frontend_tests/node_tests/emoji.js +++ b/frontend_tests/node_tests/emoji.js @@ -6,7 +6,6 @@ set_global('page_params', { set_global('upload_widget', {}); set_global('blueslip', global.make_zblueslip()); -const emoji_codes = zrequire('emoji_codes', 'generated/emoji/emoji_codes.json'); zrequire('emoji'); zrequire('markdown'); @@ -81,39 +80,3 @@ run_test('get_canonical_name', () => { assert.equal(blueslip.get_test_logs('error').length, 1); blueslip.clear_test_data(); }); - -run_test('translate_emoticons_to_names', () => { - // Simple test - const test_text = 'Testing :)'; - const expected = 'Testing :slight_smile:'; - const result = emoji.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: '', expected: ''}, - {name: 'space at start', original: ' ', expected: ' '}, - {name: 'space at end', original: ' ', expected: ' '}, - {name: 'symbol at end', original: '!', expected: '!'}, - {name: 'symbol at start', original: 'Hello,', expected: 'Hello,'}, - {name: 'after a word', original: 'Hello', expected: 'Hello'}, - {name: 'between words', original: 'HelloWorld', expected: 'HelloWorld'}, - {name: 'end of sentence', original: 'End of sentence. ', expected: 'End of sentence. '}, - {name: 'between symbols', original: 'Hello.! World.', expected: 'Hello.! World.'}, - {name: 'before end of sentence', original: 'Hello !', expected: 'Hello !'}, - ]; - 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(/()/g, shortcut); - expected = expected.replace(/()/g, shortcut) - .replace(/()/g, converted_value); - const result = emoji.translate_emoticons_to_names(original); - assert.equal(result, expected); - } - } -}); diff --git a/frontend_tests/node_tests/markdown.js b/frontend_tests/node_tests/markdown.js index 2befbe4ccd..95a8305a4f 100644 --- a/frontend_tests/node_tests/markdown.js +++ b/frontend_tests/node_tests/markdown.js @@ -608,3 +608,39 @@ run_test('misc_helpers', () => { 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: '', expected: ''}, + {name: 'space at start', original: ' ', expected: ' '}, + {name: 'space at end', original: ' ', expected: ' '}, + {name: 'symbol at end', original: '!', expected: '!'}, + {name: 'symbol at start', original: 'Hello,', expected: 'Hello,'}, + {name: 'after a word', original: 'Hello', expected: 'Hello'}, + {name: 'between words', original: 'HelloWorld', expected: 'HelloWorld'}, + {name: 'end of sentence', original: 'End of sentence. ', expected: 'End of sentence. '}, + {name: 'between symbols', original: 'Hello.! World.', expected: 'Hello.! World.'}, + {name: 'before end of sentence', original: 'Hello !', expected: 'Hello !'}, + ]; + 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(/()/g, shortcut); + expected = expected.replace(/()/g, shortcut) + .replace(/()/g, converted_value); + const result = markdown.translate_emoticons_to_names(original); + assert.equal(result, expected); + } + } +}); diff --git a/static/js/emoji.js b/static/js/emoji.js index 06445dc99e..c4247a86dc 100644 --- a/static/js/emoji.js +++ b/static/js/emoji.js @@ -184,43 +184,6 @@ exports.get_canonical_name = function (emoji_name) { return emoji_codes.codepoint_to_name[codepoint]; }; -// Translates emoticons in a string to their colon syntax. -exports.translate_emoticons_to_names = function translate_emoticons_to_names(text) { - let translated = text; - let replacement_text; - const terminal_symbols = ',.;?!()[] "\'\n\t'; // From composebox_typeahead - const symbols_except_space = terminal_symbols.replace(' ', ''); - - const emoticon_replacer = function (match, g1, offset, str) { - const prev_char = str[offset - 1]; - const next_char = str[offset + match.length]; - - const symbol_at_start = terminal_symbols.includes(prev_char); - const symbol_at_end = terminal_symbols.includes(next_char); - const non_space_at_start = symbols_except_space.includes(prev_char); - const non_space_at_end = symbols_except_space.includes(next_char); - const valid_start = symbol_at_start || offset === 0; - const valid_end = symbol_at_end || offset === str.length - match.length; - - if (non_space_at_start && non_space_at_end) { // Hello!:)? - return match; - } - if (valid_start && valid_end) { - return replacement_text; - } - return match; - }; - - for (const translation of emoticon_translations) { - // We can't pass replacement_text directly into - // emoticon_replacer, because emoticon_replacer is - // a callback for `replace()`. Instead we just mutate - // the `replacement_text` that the function closes on. - replacement_text = translation.replacement_text; - translated = translated.replace(translation.regex, emoticon_replacer); - } - - return translated; -}; +exports.get_emoticon_translations = () => emoticon_translations; window.emoji = exports; diff --git a/static/js/markdown.js b/static/js/markdown.js index 286477e747..3930d1b2a2 100644 --- a/static/js/markdown.js +++ b/static/js/markdown.js @@ -34,6 +34,45 @@ exports.set_name_in_mention_element = function (element, name) { } }; +exports.translate_emoticons_to_names = (text) => { + // Translates emoticons in a string to their colon syntax. + let translated = text; + let replacement_text; + const terminal_symbols = ',.;?!()[] "\'\n\t'; // From composebox_typeahead + const symbols_except_space = terminal_symbols.replace(' ', ''); + + const emoticon_replacer = function (match, g1, offset, str) { + const prev_char = str[offset - 1]; + const next_char = str[offset + match.length]; + + const symbol_at_start = terminal_symbols.includes(prev_char); + const symbol_at_end = terminal_symbols.includes(next_char); + const non_space_at_start = symbols_except_space.includes(prev_char); + const non_space_at_end = symbols_except_space.includes(next_char); + const valid_start = symbol_at_start || offset === 0; + const valid_end = symbol_at_end || offset === str.length - match.length; + + if (non_space_at_start && non_space_at_end) { // Hello!:)? + return match; + } + if (valid_start && valid_end) { + return replacement_text; + } + return match; + }; + + for (const translation of emoji.get_emoticon_translations()) { + // We can't pass replacement_text directly into + // emoticon_replacer, because emoticon_replacer is + // a callback for `replace()`. Instead we just mutate + // the `replacement_text` that the function closes on. + replacement_text = translation.replacement_text; + translated = translated.replace(translation.regex, emoticon_replacer); + } + + return translated; +}; + exports.contains_backend_only_syntax = function (content) { // Try to guess whether or not a message has bugdown in it // If it doesn't, we can immediately render it client-side @@ -376,7 +415,7 @@ exports.initialize = function () { // In this scenario, the message has to be from the user, so the only // requirement should be that they have the setting on. - return emoji.translate_emoticons_to_names(src); + return exports.translate_emoticons_to_names(src); } // Disable lheadings