From 1f059f5906d706c01b7efbad278bc177ce5ddc98 Mon Sep 17 00:00:00 2001 From: evykassirer Date: Fri, 29 Mar 2024 16:22:25 -0700 Subject: [PATCH] composebox_typeahead: Use local `token` instead of typeahead `this`. --- web/src/composebox_typeahead.js | 50 ++++----- web/tests/composebox_typeahead.test.js | 140 ++++++++++++------------- 2 files changed, 93 insertions(+), 97 deletions(-) diff --git a/web/src/composebox_typeahead.js b/web/src/composebox_typeahead.js index 18fa8338d7..2f5d08f0a6 100644 --- a/web/src/composebox_typeahead.js +++ b/web/src/composebox_typeahead.js @@ -49,6 +49,14 @@ export const max_num_items = 8; export let emoji_collection = []; let completing; +let token; + +export function get_or_set_token_for_testing(val) { + if (val !== undefined) { + token = val; + } + return token; +} export function get_or_set_completing_for_tests(val) { if (val !== undefined) { @@ -640,10 +648,6 @@ export function get_sorted_filtered_items(query) { return false; } - // We are still hacking info onto the "this" from - // bootstrap. Yuck. - const token = this.token; - const opts = get_stream_topic_data(this); if (completing === "mention" || completing === "silent_mention") { @@ -714,7 +718,7 @@ export function get_candidates(query) { current_token = current_token.slice(1); } completing = "syntax"; - this.token = current_token; + token = current_token; // If the code formatting button was triggered, we want to show a blank option // to improve the discoverability of the possibility of specifying a language. const language_list = compose_ui.code_formatting_button_triggered @@ -739,7 +743,7 @@ export function get_candidates(query) { return false; } completing = "emoji"; - this.token = current_token.slice(1); + token = current_token.slice(1); return emoji_collection; } @@ -758,7 +762,7 @@ export function get_candidates(query) { completing = null; return false; } - this.token = current_token; + token = current_token; return {is_silent}; } @@ -771,7 +775,7 @@ export function get_candidates(query) { current_token = current_token.slice(1); completing = "slash"; - this.token = current_token; + token = current_token; return get_slash_commands_data(); } @@ -791,7 +795,7 @@ export function get_candidates(query) { } completing = "stream"; - this.token = current_token; + token = current_token; return stream_data.get_unsorted_subs(); } @@ -802,7 +806,7 @@ export function get_candidates(query) { const should_jump_inside_typeahead = stream_regex.test(split[0]); if (should_jump_inside_typeahead) { completing = "topic_jump"; - this.token = ">"; + token = ">"; // We return something so that the typeahead is shown, but ultimately return [""]; } @@ -815,17 +819,17 @@ export function get_candidates(query) { const tokens = stream_topic_regex.exec(split[0]); if (tokens[1]) { const stream_name = tokens[1]; - this.token = tokens[2] || ""; + token = tokens[2] || ""; // Don't autocomplete if there is a space following '>' - if (this.token[0] === " ") { + if (token[0] === " ") { return false; } const stream_id = stream_data.get_stream_id(stream_name); const topic_list = topics_seen_for(stream_id); - if (should_show_custom_query(this.token, topic_list)) { - topic_list.push(this.token); + if (should_show_custom_query(token, topic_list)) { + topic_list.push(token); } return topic_list; } @@ -888,17 +892,15 @@ export function content_typeahead_selected(item, event) { beginning.charAt(beginning.lastIndexOf(":") - 1) === " " || beginning.charAt(beginning.lastIndexOf(":") - 1) === "\n" ) { - beginning = - beginning.slice(0, -this.token.length - 1) + ":" + item.emoji_name + ": "; + beginning = beginning.slice(0, -token.length - 1) + ":" + item.emoji_name + ": "; } else { - beginning = - beginning.slice(0, -this.token.length - 1) + " :" + item.emoji_name + ": "; + beginning = beginning.slice(0, -token.length - 1) + " :" + item.emoji_name + ": "; } break; case "silent_mention": case "mention": { const is_silent = completing === "silent_mention"; - beginning = beginning.slice(0, -this.token.length - 1); + beginning = beginning.slice(0, -token.length - 1); if (beginning.endsWith("@_*")) { beginning = beginning.slice(0, -3); } else if (beginning.endsWith("@*") || beginning.endsWith("@_")) { @@ -934,7 +936,7 @@ export function content_typeahead_selected(item, event) { break; } case "slash": - beginning = beginning.slice(0, -this.token.length - 1) + "/" + item.name + " "; + beginning = beginning.slice(0, -token.length - 1) + "/" + item.name + " "; if (item.placeholder) { beginning = beginning + item.placeholder; highlight.start = item.name.length + 2; @@ -942,7 +944,7 @@ export function content_typeahead_selected(item, event) { } break; case "stream": - beginning = beginning.slice(0, -this.token.length - 1); + beginning = beginning.slice(0, -token.length - 1); if (beginning.endsWith("#*")) { beginning = beginning.slice(0, -2); } @@ -960,7 +962,7 @@ export function content_typeahead_selected(item, event) { case "syntax": { // Isolate the end index of the triple backticks/tildes, including // possibly a space afterward - const backticks = beginning.length - this.token.length; + const backticks = beginning.length - token.length; beginning = beginning.slice(0, backticks) + item; if (item === "spoiler") { // to add in and highlight placeholder "Header" @@ -991,9 +993,9 @@ export function content_typeahead_selected(item, event) { } case "topic_list": { // Stream + topic mention typeahead; close the stream+topic mention syntax - // with the topic and the final **. Note that this.token.length can be 0 + // with the topic and the final **. Note that token.length can be 0 // if we are completing from `**streamname>`. - const start = beginning.length - this.token.length; + const start = beginning.length - token.length; beginning = beginning.slice(0, start) + item + "** "; break; } diff --git a/web/tests/composebox_typeahead.test.js b/web/tests/composebox_typeahead.test.js index 9a3d7208b2..06ef3ce416 100644 --- a/web/tests/composebox_typeahead.test.js +++ b/web/tests/composebox_typeahead.test.js @@ -464,7 +464,7 @@ test("content_typeahead_selected", ({override}) => { // emoji ct.get_or_set_completing_for_tests("emoji"); fake_this.query = ":octo"; - fake_this.token = "octo"; + ct.get_or_set_token_for_testing("octo"); const item = { emoji_name: "octopus", }; @@ -474,13 +474,13 @@ test("content_typeahead_selected", ({override}) => { assert.equal(actual_value, expected_value); fake_this.query = " :octo"; - fake_this.token = "octo"; + ct.get_or_set_token_for_testing("octo"); actual_value = ct.content_typeahead_selected.call(fake_this, item); expected_value = " :octopus: "; assert.equal(actual_value, expected_value); fake_this.query = "{:octo"; - fake_this.token = "octo"; + ct.get_or_set_token_for_testing("octo"); actual_value = ct.content_typeahead_selected.call(fake_this, item); expected_value = "{ :octopus: "; assert.equal(actual_value, expected_value); @@ -496,7 +496,7 @@ test("content_typeahead_selected", ({override}) => { ); fake_this.query = "@**Mark Tw"; - fake_this.token = "Mark Tw"; + ct.get_or_set_token_for_testing("Mark Tw"); actual_value = ct.content_typeahead_selected.call(fake_this, twin1); expected_value = "@**Mark Twin|105** "; assert.equal(actual_value, expected_value); @@ -508,32 +508,32 @@ test("content_typeahead_selected", ({override}) => { }); fake_this.query = "@oth"; - fake_this.token = "oth"; + ct.get_or_set_token_for_testing("oth"); actual_value = ct.content_typeahead_selected.call(fake_this, othello); expected_value = "@**Othello, the Moor of Venice** "; assert.equal(actual_value, expected_value); assert.ok(warned_for_mention); fake_this.query = "Hello @oth"; - fake_this.token = "oth"; + ct.get_or_set_token_for_testing("oth"); actual_value = ct.content_typeahead_selected.call(fake_this, othello); expected_value = "Hello @**Othello, the Moor of Venice** "; assert.equal(actual_value, expected_value); fake_this.query = "@**oth"; - fake_this.token = "oth"; + ct.get_or_set_token_for_testing("oth"); actual_value = ct.content_typeahead_selected.call(fake_this, othello); expected_value = "@**Othello, the Moor of Venice** "; assert.equal(actual_value, expected_value); fake_this.query = "@*oth"; - fake_this.token = "oth"; + ct.get_or_set_token_for_testing("oth"); actual_value = ct.content_typeahead_selected.call(fake_this, othello); expected_value = "@**Othello, the Moor of Venice** "; assert.equal(actual_value, expected_value); fake_this.query = "@back"; - fake_this.token = "back"; + ct.get_or_set_token_for_testing("back"); with_overrides(({disallow}) => { disallow(compose_validate, "warn_if_mentioning_unsubscribed_user"); actual_value = ct.content_typeahead_selected.call(fake_this, backend); @@ -542,7 +542,7 @@ test("content_typeahead_selected", ({override}) => { assert.equal(actual_value, expected_value); fake_this.query = "@*back"; - fake_this.token = "back"; + ct.get_or_set_token_for_testing("back"); actual_value = ct.content_typeahead_selected.call(fake_this, backend); expected_value = "@*Backend* "; assert.equal(actual_value, expected_value); @@ -550,7 +550,7 @@ test("content_typeahead_selected", ({override}) => { // silent mention ct.get_or_set_completing_for_tests("silent_mention"); fake_this.query = "@_kin"; - fake_this.token = "kin"; + ct.get_or_set_token_for_testing("kin"); with_overrides(({disallow}) => { disallow(compose_validate, "warn_if_mentioning_unsubscribed_user"); actual_value = ct.content_typeahead_selected.call(fake_this, hamlet); @@ -560,25 +560,25 @@ test("content_typeahead_selected", ({override}) => { assert.equal(actual_value, expected_value); fake_this.query = "Hello @_kin"; - fake_this.token = "kin"; + ct.get_or_set_token_for_testing("kin"); actual_value = ct.content_typeahead_selected.call(fake_this, hamlet); expected_value = "Hello @_**King Hamlet** "; assert.equal(actual_value, expected_value); fake_this.query = "@_*kin"; - fake_this.token = "kin"; + ct.get_or_set_token_for_testing("kin"); actual_value = ct.content_typeahead_selected.call(fake_this, hamlet); expected_value = "@_**King Hamlet** "; assert.equal(actual_value, expected_value); fake_this.query = "@_**kin"; - fake_this.token = "kin"; + ct.get_or_set_token_for_testing("kin"); actual_value = ct.content_typeahead_selected.call(fake_this, hamlet); expected_value = "@_**King Hamlet** "; assert.equal(actual_value, expected_value); fake_this.query = "@_back"; - fake_this.token = "back"; + ct.get_or_set_token_for_testing("back"); with_overrides(({disallow}) => { disallow(compose_validate, "warn_if_mentioning_unsubscribed_user"); actual_value = ct.content_typeahead_selected.call(fake_this, backend); @@ -587,7 +587,7 @@ test("content_typeahead_selected", ({override}) => { assert.equal(actual_value, expected_value); fake_this.query = "@_*back"; - fake_this.token = "back"; + ct.get_or_set_token_for_testing("back"); actual_value = ct.content_typeahead_selected.call(fake_this, backend); expected_value = "@_*Backend* "; assert.equal(actual_value, expected_value); @@ -631,19 +631,19 @@ test("content_typeahead_selected", ({override}) => { }); fake_this.query = "#swed"; - fake_this.token = "swed"; + ct.get_or_set_token_for_testing("swed"); actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream); expected_value = "#**Sweden** "; assert.equal(actual_value, expected_value); fake_this.query = "Hello #swed"; - fake_this.token = "swed"; + ct.get_or_set_token_for_testing("swed"); actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream); expected_value = "Hello #**Sweden** "; assert.equal(actual_value, expected_value); fake_this.query = "#**swed"; - fake_this.token = "swed"; + ct.get_or_set_token_for_testing("swed"); actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream); expected_value = "#**Sweden** "; assert.equal(actual_value, expected_value); @@ -652,13 +652,13 @@ test("content_typeahead_selected", ({override}) => { ct.get_or_set_completing_for_tests("topic_list"); fake_this.query = "Hello #**Sweden>test"; - fake_this.token = "test"; + ct.get_or_set_token_for_testing("test"); actual_value = ct.content_typeahead_selected.call(fake_this, "testing"); expected_value = "Hello #**Sweden>testing** "; assert.equal(actual_value, expected_value); fake_this.query = "Hello #**Sweden>"; - fake_this.token = ""; + ct.get_or_set_token_for_testing(""); actual_value = ct.content_typeahead_selected.call(fake_this, "testing"); expected_value = "Hello #**Sweden>testing** "; assert.equal(actual_value, expected_value); @@ -667,32 +667,32 @@ test("content_typeahead_selected", ({override}) => { ct.get_or_set_completing_for_tests("syntax"); fake_this.query = "~~~p"; - fake_this.token = "p"; + ct.get_or_set_token_for_testing("p"); actual_value = ct.content_typeahead_selected.call(fake_this, "python"); expected_value = "~~~python\n\n~~~"; assert.equal(actual_value, expected_value); fake_this.query = "Hello ~~~p"; - fake_this.token = "p"; + ct.get_or_set_token_for_testing("p"); actual_value = ct.content_typeahead_selected.call(fake_this, "python"); expected_value = "Hello ~~~python\n\n~~~"; assert.equal(actual_value, expected_value); fake_this.query = "```p"; - fake_this.token = "p"; + ct.get_or_set_token_for_testing("p"); actual_value = ct.content_typeahead_selected.call(fake_this, "python"); expected_value = "```python\n\n```"; assert.equal(actual_value, expected_value); fake_this.query = "```spo"; - fake_this.token = "spo"; + ct.get_or_set_token_for_testing("spo"); actual_value = ct.content_typeahead_selected.call(fake_this, "spoiler"); expected_value = "```spoiler translated: Header\n\n```"; assert.equal(actual_value, expected_value); // Test special case to not close code blocks if there is text afterward fake_this.query = "```p\nsome existing code"; - fake_this.token = "p"; + ct.get_or_set_token_for_testing("p"); fake_this.input_element.$element.caret = () => 4; // Put cursor right after ```p actual_value = ct.content_typeahead_selected.call(fake_this, "python"); expected_value = "```python\nsome existing code"; @@ -989,7 +989,7 @@ test("initialize", ({override, override_rewire, mock_template}) => { // For now we only test that get_sorted_filtered_items has been // properly set as the .source(). All its features are tested later on // in test_begins_typeahead(). - let fake_this = { + const fake_this = { input_element: { $element: {}, type: "input", @@ -1011,8 +1011,8 @@ test("initialize", ({override, override_rewire, mock_template}) => { // Again, here we only verify that the highlighter has been set to // content_highlighter_html. ct.get_or_set_completing_for_tests("mention"); - fake_this = {token: "othello"}; - actual_value = options.highlighter_html.call(fake_this, othello); + ct.get_or_set_token_for_testing("othello"); + actual_value = options.highlighter_html(othello); expected_value = ` \n` + ` \n` + @@ -1023,41 +1023,41 @@ test("initialize", ({override, override_rewire, mock_template}) => { othello.delivery_email = null; ct.get_or_set_completing_for_tests("mention"); - fake_this = {token: "hamletcharacters"}; - actual_value = options.highlighter_html.call(fake_this, hamletcharacters); + ct.get_or_set_token_for_testing("hamletcharacters"); + actual_value = options.highlighter_html(hamletcharacters); expected_value = ' \nhamletcharacters  \nCharacters of Hamlet\n'; assert.equal(actual_value, expected_value); // matching - function match(fake_this, item) { - const token = fake_this.token; + function match(item) { + const token = ct.get_or_set_token_for_testing(); const completing = ct.get_or_set_completing_for_tests(); return ct.compose_content_matcher(completing, token)(item); } ct.get_or_set_completing_for_tests("emoji"); - fake_this = {token: "ta"}; - assert.equal(match(fake_this, make_emoji(emoji_tada)), true); - assert.equal(match(fake_this, make_emoji(emoji_moneybag)), false); + ct.get_or_set_token_for_testing("ta"); + assert.equal(match(make_emoji(emoji_tada)), true); + assert.equal(match(make_emoji(emoji_moneybag)), false); ct.get_or_set_completing_for_tests("stream"); - fake_this = {token: "swed"}; - assert.equal(match(fake_this, sweden_stream), true); - assert.equal(match(fake_this, denmark_stream), false); + ct.get_or_set_token_for_testing("swed"); + assert.equal(match(sweden_stream), true); + assert.equal(match(denmark_stream), false); ct.get_or_set_completing_for_tests("syntax"); - fake_this = {token: "py"}; - assert.equal(match(fake_this, "python"), true); - assert.equal(match(fake_this, "javascript"), false); + ct.get_or_set_token_for_testing("py"); + assert.equal(match("python"), true); + assert.equal(match("javascript"), false); ct.get_or_set_completing_for_tests("non-existing-completion"); - assert.equal(match(fake_this), undefined); + assert.equal(match(), undefined); - function sort_items(fake_this, item) { - const token = fake_this.token; + function sort_items(item) { + const token = ct.get_or_set_token_for_testing(); const completing = ct.get_or_set_completing_for_tests(); return ct.sort_results(completing, item, token); @@ -1065,17 +1065,14 @@ test("initialize", ({override, override_rewire, mock_template}) => { // options.sorter() ct.get_or_set_completing_for_tests("emoji"); - fake_this = {token: "ta"}; - actual_value = sort_items(fake_this, [ - make_emoji(emoji_stadium), - make_emoji(emoji_tada), - ]); + ct.get_or_set_token_for_testing("ta"); + actual_value = sort_items([make_emoji(emoji_stadium), make_emoji(emoji_tada)]); expected_value = [make_emoji(emoji_tada), make_emoji(emoji_stadium)]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("emoji"); - fake_this = {token: "th"}; - actual_value = sort_items(fake_this, [ + ct.get_or_set_token_for_testing("th"); + actual_value = sort_items([ make_emoji(emoji_thermometer), make_emoji(emoji_thumbs_up), ]); @@ -1083,29 +1080,26 @@ test("initialize", ({override, override_rewire, mock_template}) => { assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("emoji"); - fake_this = {token: "he"}; - actual_value = sort_items(fake_this, [ - make_emoji(emoji_headphones), - make_emoji(emoji_heart), - ]); + ct.get_or_set_token_for_testing("he"); + actual_value = sort_items([make_emoji(emoji_headphones), make_emoji(emoji_heart)]); expected_value = [make_emoji(emoji_heart), make_emoji(emoji_headphones)]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("slash"); - fake_this = {token: "m"}; - actual_value = sort_items(fake_this, [my_slash, me_slash]); + ct.get_or_set_token_for_testing("m"); + actual_value = sort_items([my_slash, me_slash]); expected_value = [me_slash, my_slash]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("slash"); - fake_this = {token: "da"}; - actual_value = sort_items(fake_this, [dark_slash, light_slash]); + ct.get_or_set_token_for_testing("da"); + actual_value = sort_items([dark_slash, light_slash]); expected_value = [dark_slash, light_slash]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("stream"); - fake_this = {token: "de"}; - actual_value = sort_items(fake_this, [sweden_stream, denmark_stream]); + ct.get_or_set_token_for_testing("de"); + actual_value = sort_items([sweden_stream, denmark_stream]); expected_value = [denmark_stream, sweden_stream]; assert.deepEqual(actual_value, expected_value); @@ -1113,14 +1107,14 @@ test("initialize", ({override, override_rewire, mock_template}) => { // Testing "co" for "cold", in both streams' description. It's at the // beginning of Sweden's description, so that one should go first. ct.get_or_set_completing_for_tests("stream"); - fake_this = {token: "co"}; - actual_value = sort_items(fake_this, [denmark_stream, sweden_stream]); + ct.get_or_set_token_for_testing("co"); + actual_value = sort_items([denmark_stream, sweden_stream]); expected_value = [sweden_stream, denmark_stream]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("syntax"); - fake_this = {token: "ap"}; - actual_value = sort_items(fake_this, ["abap", "applescript"]); + ct.get_or_set_token_for_testing("ap"); + actual_value = sort_items(["abap", "applescript"]); expected_value = ["applescript", "abap"]; assert.deepEqual(actual_value, expected_value); @@ -1139,8 +1133,8 @@ test("initialize", ({override, override_rewire, mock_template}) => { stream_list_sort.set_filter_out_inactives(); ct.get_or_set_completing_for_tests("stream"); - fake_this = {token: "s"}; - actual_value = sort_items(fake_this, [sweden_stream, serbia_stream]); + ct.get_or_set_token_for_testing("s"); + actual_value = sort_items([sweden_stream, serbia_stream]); expected_value = [sweden_stream, serbia_stream]; assert.deepEqual(actual_value, expected_value); // Subscribed stream is inactive @@ -1151,18 +1145,18 @@ test("initialize", ({override, override_rewire, mock_template}) => { ); stream_list_sort.set_filter_out_inactives(); - actual_value = sort_items(fake_this, [sweden_stream, serbia_stream]); + actual_value = sort_items([sweden_stream, serbia_stream]); expected_value = [sweden_stream, serbia_stream]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("stream"); - fake_this = {token: "ser"}; - actual_value = sort_items(fake_this, [denmark_stream, serbia_stream]); + ct.get_or_set_token_for_testing("ser"); + actual_value = sort_items([denmark_stream, serbia_stream]); expected_value = [serbia_stream, denmark_stream]; assert.deepEqual(actual_value, expected_value); ct.get_or_set_completing_for_tests("non-existing-completion"); - assert.equal(sort_items(fake_this), undefined); + assert.equal(sort_items(), undefined); compose_textarea_typeahead_called = true;