From 987eeef7100c6c168114e4cfc869aba9e9274cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Thu, 29 Jun 2023 15:44:27 +0000 Subject: [PATCH] [FIX] mail: debounce suggestion (and channel selector) searches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, fetching suggestions (e.g. channel selector in discuss sidebar "Find or create a channel", or `@`-mentions) were performing RPCs on each user input, resulting in wasted network and server load. This commit fixes the issue by debouncing the fetching of suggestions to 0.5 sec. closes odoo/odoo#126952 X-original-commit: 6770ac3 Related: odoo/enterprise#43502 Signed-off-by: Matthieu Stockbauer (tsm) Signed-off-by: Alexandre Kühn (aku) --- .../static/tests/composer_patch_tests.js | 21 +++++++-- .../static/src/core/common/navigable_list.js | 6 +-- .../static/src/core/common/suggestion_hook.js | 13 ++++- .../src/core/common/suggestion_service.js | 2 + .../src/discuss/core/web/channel_selector.js | 8 +++- .../static/tests/composer/composer_tests.js | 27 +++++++++-- .../tests/discuss/call/web/call_tests.js | 13 +++-- .../tests/discuss/core/suggestion_tests.js | 8 +++- .../core/web/chat_window_new_message_tests.js | 17 +++++-- .../tests/discuss/core/web/crosstab_tests.js | 7 ++- .../tests/discuss/core/web/discuss_tests.js | 47 +++++++++++++------ .../discuss/core/web/messaging_menu_tests.js | 15 ++++-- .../tests/discuss/core/web/sidebar_tests.js | 9 +++- .../static/tests/discuss_app/inbox_tests.js | 13 ++++- .../static/tests/message/message_tests.js | 17 +++++-- .../tests/suggestion/suggestion_tests.js | 36 +++++++++++--- .../mail/static/tests/thread/thread_tests.js | 47 +++++++++++++++---- 17 files changed, 239 insertions(+), 67 deletions(-) diff --git a/addons/im_livechat/static/tests/composer_patch_tests.js b/addons/im_livechat/static/tests/composer_patch_tests.js index 04f033cc4de..a5966b911ec 100644 --- a/addons/im_livechat/static/tests/composer_patch_tests.js +++ b/addons/im_livechat/static/tests/composer_patch_tests.js @@ -9,6 +9,7 @@ import { insertText, start, } from "@mail/../tests/helpers/test_utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; import { editInput, nextTick, triggerHotkey } from "@web/../tests/helpers/utils"; @@ -111,10 +112,13 @@ QUnit.test('display canned response suggestions on typing ":"', async (assert) = source: "hello", substitution: "Hello! How are you?", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); await insertText(".o-mail-Composer-input", ":"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); }); @@ -133,11 +137,14 @@ QUnit.test("use a canned response", async (assert) => { source: "hello", substitution: "Hello! How are you?", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", ":"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion"); await click(".o-mail-Composer-suggestion"); assert.strictEqual( @@ -162,13 +169,16 @@ QUnit.test("use a canned response some text", async (assert) => { source: "hello", substitution: "Hello! How are you?", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "bluhbluh "); assert.strictEqual($(".o-mail-Composer-input").val(), "bluhbluh "); await insertText(".o-mail-Composer-input", ":"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion"); await click(".o-mail-Composer-suggestion"); assert.strictEqual( @@ -193,11 +203,14 @@ QUnit.test("add an emoji after a canned response", async (assert) => { source: "hello", substitution: "Hello! How are you?", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", ":"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion"); await click(".o-mail-Composer-suggestion"); assert.strictEqual( diff --git a/addons/mail/static/src/core/common/navigable_list.js b/addons/mail/static/src/core/common/navigable_list.js index 5c06f245eac..87f90f78f1e 100644 --- a/addons/mail/static/src/core/common/navigable_list.js +++ b/addons/mail/static/src/core/common/navigable_list.js @@ -65,9 +65,6 @@ export class NavigableList extends Component { } open() { - if (this.state.isLoading) { - return; - } this.load().then(() => { this.state.open = true; this.navigate("first"); @@ -80,6 +77,7 @@ export class NavigableList extends Component { } async load() { + this.state.isLoading = true; this.state.options = []; const makeOption = (opt) => { return Object.assign(Object.create(opt), { @@ -87,12 +85,12 @@ export class NavigableList extends Component { }); }; if (this.props.options instanceof Promise) { - this.state.isLoading = true; return this.props.options.then((opts) => { opts.forEach((opt) => this.state.options.push(makeOption(opt))); this.state.isLoading = false; }); } + this.state.isLoading = false; if (this.props.options instanceof Array) { if (this.props.options.length === 0) { return; diff --git a/addons/mail/static/src/core/common/suggestion_hook.js b/addons/mail/static/src/core/common/suggestion_hook.js index 6a1a276539d..6a3f85a1ca7 100644 --- a/addons/mail/static/src/core/common/suggestion_hook.js +++ b/addons/mail/static/src/core/common/suggestion_hook.js @@ -1,8 +1,10 @@ /* @odoo-module */ +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; import { useComponent, useEffect, useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; +import { useDebounced } from "@web/core/utils/timing"; export function useSuggestion() { const comp = useComponent(); @@ -124,9 +126,10 @@ export function useSuggestion() { state: useState({ count: 0, items: undefined, + loading: false, }), update() { - if (!self.search.delimiter) { + if (!self.search.delimiter || self.state.loading) { return; } const suggestions = suggestionService.searchSuggestions( @@ -150,6 +153,10 @@ export function useSuggestion() { self.state.items = { type, mainSuggestions, extraSuggestions }; }, }; + const debouncedFetchSuggestions = useDebounced( + (...args) => suggestionService.fetchSuggestions(...args), + DEBOUNCE_FETCH_SUGGESTION_TIME + ); useEffect( () => { self.update(); @@ -157,12 +164,14 @@ export function useSuggestion() { if (self.search.position === undefined || !self.search.delimiter) { return; // ignore obsolete call } - await suggestionService.fetchSuggestions(self.search, { + self.state.loading = true; + await debouncedFetchSuggestions(self.search, { thread: self.thread, onFetched() { if (owl.status(comp) === "destroyed") { return; } + self.state.loading = false; self.update(); }, }); diff --git a/addons/mail/static/src/core/common/suggestion_service.js b/addons/mail/static/src/core/common/suggestion_service.js index dc1ef8e3c0b..52ea4447d90 100644 --- a/addons/mail/static/src/core/common/suggestion_service.js +++ b/addons/mail/static/src/core/common/suggestion_service.js @@ -4,6 +4,8 @@ import { cleanTerm } from "@mail/utils/common/format"; import { registry } from "@web/core/registry"; +export const DEBOUNCE_FETCH_SUGGESTION_TIME = 500; + export class SuggestionService { constructor(env, services) { this.orm = services.orm; diff --git a/addons/mail/static/src/discuss/core/web/channel_selector.js b/addons/mail/static/src/discuss/core/web/channel_selector.js index 99c5023d843..a93946c3c20 100644 --- a/addons/mail/static/src/discuss/core/web/channel_selector.js +++ b/addons/mail/static/src/discuss/core/web/channel_selector.js @@ -2,6 +2,7 @@ import { useStore } from "@mail/core/common/messaging_hook"; import { NavigableList } from "@mail/core/common/navigable_list"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; import { cleanTerm } from "@mail/utils/common/format"; import { createLocalId, isEventHandled, markEventHandled } from "@mail/utils/common/misc"; @@ -11,6 +12,7 @@ import { getActiveHotkey } from "@web/core/hotkeys/hotkey_service"; import { _t } from "@web/core/l10n/translation"; import { TagsList } from "@web/core/tags_list/tags_list"; import { useService } from "@web/core/utils/hooks"; +import { useDebounced } from "@web/core/utils/timing"; export class ChannelSelector extends Component { static components = { TagsList, NavigableList }; @@ -35,6 +37,10 @@ export class ChannelSelector extends Component { onMounted(() => this.inputRef.el.focus()); } this.markEventHandled = markEventHandled; + this.debouncedFetchSuggestions = useDebounced( + this.fetchSuggestions.bind(this), + DEBOUNCE_FETCH_SUGGESTION_TIME + ); } async fetchSuggestions() { @@ -198,7 +204,7 @@ export class ChannelSelector extends Component { this.props.category.id === "channels" ? "discuss.ChannelSelector.channel" : "discuss.ChannelSelector.chat", - options: this.fetchSuggestions(), + options: this.debouncedFetchSuggestions(), }; } } diff --git a/addons/mail/static/tests/composer/composer_tests.js b/addons/mail/static/tests/composer/composer_tests.js index f1c48d0513b..e240e8e670a 100644 --- a/addons/mail/static/tests/composer/composer_tests.js +++ b/addons/mail/static/tests/composer/composer_tests.js @@ -28,6 +28,7 @@ import { import { file } from "web.test_utils"; const { inputFiles } = file; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("composer", { async beforeEach() { @@ -359,13 +360,16 @@ QUnit.test("add an emoji after a partner mention", async (assert) => { Command.create({ partner_id: partnerId }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "T"); await insertText(".o-mail-Composer-input", "e"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "@TestPartner "); @@ -380,7 +384,7 @@ QUnit.test("mention a channel after some text", async (assert) => { name: "General", channel_type: "channel", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); @@ -391,6 +395,9 @@ QUnit.test("mention a channel after some text", async (assert) => { "text content of composer should have content" ); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion"); await click(".o-mail-Composer-suggestion"); assert.strictEqual( @@ -406,11 +413,14 @@ QUnit.test("add an emoji after a channel mention", async (assert) => { name: "General", channel_type: "channel", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion"); await click(".o-mail-Composer-suggestion"); assert.strictEqual( @@ -427,10 +437,13 @@ QUnit.test("add an emoji after a channel mention", async (assert) => { QUnit.test("pending mentions are kept when toggling composer", async (assert) => { const pyEnv = await startServer(); - const { openFormView } = await start(); + const { advanceTime, openFormView } = await start({ hasTimeControl: true }); await openFormView("res.partner", pyEnv.currentPartnerId); await click("button:contains(Send message)"); await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:contains(Mitchell Admin)"); await click("button:contains(Send message)"); await click("button:contains(Send message)"); @@ -611,7 +624,8 @@ QUnit.test("Select composer suggestion via Enter does not send the message", asy Command.create({ partner_id: partnerId }), ], }); - const { openDiscuss } = await start({ + const { advanceTime, openDiscuss } = await start({ + hasTimeControl: true, async mockRPC(route, args) { if (route === "/mail/message/post") { assert.step("message_post"); @@ -621,6 +635,9 @@ QUnit.test("Select composer suggestion via Enter does not send the message", asy await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Shrek"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await afterNextRender(() => triggerHotkey("Enter")); assert.equal($(".o-mail-Composer-input").val().trim(), "@Shrek"); assert.verifySteps([]); diff --git a/addons/mail/static/tests/discuss/call/web/call_tests.js b/addons/mail/static/tests/discuss/call/web/call_tests.js index 0cad45aa2de..280d8d9435f 100644 --- a/addons/mail/static/tests/discuss/call/web/call_tests.js +++ b/addons/mail/static/tests/discuss/call/web/call_tests.js @@ -1,8 +1,9 @@ /* @odoo-module */ import { afterNextRender, click, start, startServer } from "@mail/../tests/helpers/test_utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; -import { editInput, triggerEvent } from "@web/../tests/helpers/utils"; +import { editInput, nextTick, triggerEvent } from "@web/../tests/helpers/utils"; QUnit.module("call"); @@ -10,7 +11,7 @@ QUnit.test("no default rtc after joining a chat conversation", async (assert) => const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Mario" }); pyEnv["res.users"].create({ partner_id: partnerId }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); assert.containsNone($, ".o-mail-DiscussCategoryItem"); @@ -18,6 +19,8 @@ QUnit.test("no default rtc after joining a chat conversation", async (assert) => await afterNextRender(() => editInput(document.body, ".o-discuss-ChannelSelector input", "mario") ); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await triggerEvent(document.body, ".o-discuss-ChannelSelector input", "keydown", { key: "Enter", @@ -34,17 +37,21 @@ QUnit.test("no default rtc after joining a group conversation", async (assert) = { name: "Luigi" }, ]); pyEnv["res.users"].create([{ partner_id: partnerId_1 }, { partner_id: partnerId_2 }]); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); assert.containsNone($, ".o-mail-DiscussCategoryItem"); await click(".o-mail-DiscussSidebar i[title='Start a conversation']"); await afterNextRender(() => editInput(document.body, ".o-discuss-ChannelSelector input", "mario") ); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await afterNextRender(() => editInput(document.body, ".o-discuss-ChannelSelector input", "luigi") ); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await triggerEvent(document.body, ".o-discuss-ChannelSelector input", "keydown", { key: "Enter", diff --git a/addons/mail/static/tests/discuss/core/suggestion_tests.js b/addons/mail/static/tests/discuss/core/suggestion_tests.js index 997a352e791..5604dcb1ce6 100644 --- a/addons/mail/static/tests/discuss/core/suggestion_tests.js +++ b/addons/mail/static/tests/discuss/core/suggestion_tests.js @@ -4,7 +4,8 @@ import { Composer } from "@mail/core/common/composer"; import { Command } from "@mail/../tests/helpers/command"; import { click, insertText, start, startServer } from "@mail/../tests/helpers/test_utils"; -import { patchWithCleanup } from "@web/../tests/helpers/utils"; +import { nextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("suggestion", { async beforeEach() { @@ -110,7 +111,7 @@ QUnit.test("Sort partner suggestions by recent chats", async (assert) => { channel_type: "chat", }, ]); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); await click(".o-mail-DiscussCategoryItem:contains('User 2')"); await insertText(".o-mail-Composer-input", "This is a test"); @@ -119,6 +120,9 @@ QUnit.test("Sort partner suggestions by recent chats", async (assert) => { await click(".o-mail-DiscussCategoryItem:contains('General')"); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "User"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsN($, ".o-mail-Composer-suggestion", 3); assert.strictEqual($(".o-mail-Composer-suggestion").eq(0).text(), "User 2"); assert.strictEqual($(".o-mail-Composer-suggestion").eq(1).text(), "User 1"); diff --git a/addons/mail/static/tests/discuss/core/web/chat_window_new_message_tests.js b/addons/mail/static/tests/discuss/core/web/chat_window_new_message_tests.js index 7c02dea1366..11e1bcd39af 100644 --- a/addons/mail/static/tests/discuss/core/web/chat_window_new_message_tests.js +++ b/addons/mail/static/tests/discuss/core/web/chat_window_new_message_tests.js @@ -15,7 +15,8 @@ import { startServer, } from "@mail/../tests/helpers/test_utils"; -import { makeDeferred } from "@web/../tests/helpers/utils"; +import { makeDeferred, nextTick } from "@web/../tests/helpers/utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("chat window: new message"); @@ -103,7 +104,8 @@ QUnit.test( 1920, "should have enough space to open 3 chat windows simultaneously" ); - await start({ + const { advanceTime } = await start({ + hasTimeControl: true, mockRPC(route, args) { if (args.method === "im_search") { imSearchDef.resolve(); @@ -137,6 +139,8 @@ QUnit.test( // search for a user in "new message" autocomplete await afterNextRender(async () => { await insertText(".o-discuss-ChannelSelector input", "131"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await imSearchDef; }); assert.containsOnce($, ".o-discuss-ChannelSelector-suggestion a"); @@ -171,10 +175,12 @@ QUnit.test( channel_type: "chat", name: "Partner 131", }); - await start(); + const { advanceTime } = await start({ hasTimeControl: true }); await click(".o_menu_systray i[aria-label='Messages']"); await click("button:contains(New Message)"); await insertText(".o-discuss-ChannelSelector", "131"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion a"); assert.containsNone($, ".o-mail-ChatWindow-header:contains(New message)"); assert.containsOnce($, ".o-mail-ChatWindow"); @@ -186,7 +192,8 @@ QUnit.test("new message autocomplete should automatically select first result", const partnerId = pyEnv["res.partner"].create({ name: "Partner 131" }); pyEnv["res.users"].create({ partner_id: partnerId }); const imSearchDef = makeDeferred(); - await start({ + const { advanceTime } = await start({ + hasTimeControl: true, mockRPC(route, args) { if (args.method === "im_search") { imSearchDef.resolve(); @@ -199,6 +206,8 @@ QUnit.test("new message autocomplete should automatically select first result", // search for a user in "new message" autocomplete await afterNextRender(async () => { await insertText(".o-discuss-ChannelSelector", "131"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await imSearchDef; }); assert.hasClass($(".o-discuss-ChannelSelector-suggestion a"), "o-mail-NavigableList-active"); diff --git a/addons/mail/static/tests/discuss/core/web/crosstab_tests.js b/addons/mail/static/tests/discuss/core/web/crosstab_tests.js index 8458a7d5172..ace55ec9706 100644 --- a/addons/mail/static/tests/discuss/core/web/crosstab_tests.js +++ b/addons/mail/static/tests/discuss/core/web/crosstab_tests.js @@ -7,15 +7,16 @@ import { start, startServer, } from "@mail/../tests/helpers/test_utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; -import { triggerHotkey, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { triggerHotkey, patchWithCleanup, nextTick } from "@web/../tests/helpers/utils"; QUnit.module("crosstab"); QUnit.test("Channel subscription is renewed when channel is manually added", async (assert) => { const pyEnv = await startServer(); pyEnv["discuss.channel"].create({ name: "General", channel_member_ids: [] }); - const { env, openDiscuss } = await start(); + const { advanceTime, env, openDiscuss } = await start({ hasTimeControl: true }); patchWithCleanup(env.services["bus_service"], { forceUpdateChannels() { assert.step("update-channels"); @@ -24,6 +25,8 @@ QUnit.test("Channel subscription is renewed when channel is manually added", asy await openDiscuss(); await click("[title='Add or join a channel']"); await insertText(".o-discuss-ChannelSelector", "General"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await afterNextRender(() => triggerHotkey("Enter")); assert.verifySteps(["update-channels"]); }); diff --git a/addons/mail/static/tests/discuss/core/web/discuss_tests.js b/addons/mail/static/tests/discuss/core/web/discuss_tests.js index 8f4087f9d57..4cc273d3fe5 100644 --- a/addons/mail/static/tests/discuss/core/web/discuss_tests.js +++ b/addons/mail/static/tests/discuss/core/web/discuss_tests.js @@ -7,14 +7,16 @@ import { start, startServer, } from "@mail/../tests/helpers/test_utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; -import { editInput, triggerEvent, triggerHotkey } from "@web/../tests/helpers/utils"; +import { editInput, nextTick, triggerEvent, triggerHotkey } from "@web/../tests/helpers/utils"; QUnit.module("discuss"); QUnit.test("can create a new channel [REQUIRE FOCUS]", async (assert) => { await startServer(); - const { openDiscuss } = await start({ + const { advanceTime, openDiscuss } = await start({ + hasTimeControl: true, mockRPC(route, params) { if ( route.startsWith("/mail") || @@ -35,6 +37,8 @@ QUnit.test("can create a new channel [REQUIRE FOCUS]", async (assert) => { await afterNextRender(() => editInput(document.body, ".o-discuss-ChannelSelector input", "abc") ); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); assert.containsOnce($, ".o-mail-DiscussCategoryItem"); assert.containsNone($, ".o-mail-Discuss-content .o-mail-Message"); @@ -54,14 +58,15 @@ QUnit.test( const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Mario" }); pyEnv["res.users"].create({ partner_id: partnerId }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); assert.containsNone($, ".o-mail-DiscussCategoryItem"); await click("i[title='Start a conversation']"); - await afterNextRender(() => - editInput(document.body, ".o-discuss-ChannelSelector input", "mario") - ); + await afterNextRender(async () => { + editInput(document.body, ".o-discuss-ChannelSelector input", "mario"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + }); await click(".o-discuss-ChannelSelector-suggestion"); assert.containsOnce($, ".o-discuss-ChannelSelector span[title='Mario']"); assert.containsNone($, ".o-mail-DiscussCategoryItem"); @@ -71,9 +76,10 @@ QUnit.test( }); assert.containsNone($, ".o-discuss-ChannelSelector span[title='Mario']"); - await afterNextRender(() => - editInput(document.body, ".o-discuss-ChannelSelector input", "mario") - ); + await afterNextRender(async () => { + editInput(document.body, ".o-discuss-ChannelSelector input", "mario"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + }); await triggerEvent(document.body, ".o-discuss-ChannelSelector input", "keydown", { key: "Enter", }); @@ -86,7 +92,8 @@ QUnit.test("can join a chat conversation", async (assert) => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Mario" }); pyEnv["res.users"].create({ partner_id: partnerId }); - const { openDiscuss } = await start({ + const { advanceTime, openDiscuss } = await start({ + hasTimeControl: true, mockRPC(route, params) { if ( route.startsWith("/mail") || @@ -107,6 +114,8 @@ QUnit.test("can join a chat conversation", async (assert) => { await afterNextRender(() => editInput(document.body, ".o-discuss-ChannelSelector input", "mario") ); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await triggerEvent(document.body, ".o-discuss-ChannelSelector input", "keydown", { key: "Enter", @@ -129,13 +138,17 @@ QUnit.test("can create a group chat conversation", async (assert) => { { name: "Luigi" }, ]); pyEnv["res.users"].create([{ partner_id: partnerId_1 }, { partner_id: partnerId_2 }]); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); assert.containsNone($, ".o-mail-DiscussCategoryItem"); await click(".o-mail-DiscussSidebar i[title='Start a conversation']"); await insertText(".o-discuss-ChannelSelector input", "Mario"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await insertText(".o-discuss-ChannelSelector input", "Luigi"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await triggerEvent(document.body, ".o-discuss-ChannelSelector input", "keydown", { key: "Enter", @@ -145,10 +158,12 @@ QUnit.test("can create a group chat conversation", async (assert) => { }); QUnit.test("chat search should display no result when no matches found", async (assert) => { - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); await click(".o-mail-DiscussSidebar i[title='Start a conversation']"); await insertText(".o-discuss-ChannelSelector", "Rainbow Panda"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); assert.containsOnce($, ".o-discuss-ChannelSelector-suggestion:contains(No results found)"); }); @@ -158,11 +173,13 @@ QUnit.test( const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Panda" }); pyEnv["res.users"].create({ partner_id: partnerId }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); assert.containsNone($, ".o-mail-DiscussCategoryItem"); await click(".o-mail-DiscussSidebar i[title='Start a conversation']"); await insertText(".o-discuss-ChannelSelector", "Panda"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); assert.containsOnce($, ".o-discuss-ChannelSelector-suggestion"); await click(".o-mail-DiscussSidebar"); assert.containsNone($, ".o-discuss-ChannelSelector-suggestion"); @@ -187,12 +204,14 @@ QUnit.test("Chat is added to discuss on other tab that the one that joined", asy const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Jerry Golay" }); pyEnv["res.users"].create({ partner_id: partnerId }); - const tab1 = await start({ asTab: true }); + const tab1 = await start({ asTab: true, hasTimeControl: true }); const tab2 = await start({ asTab: true }); await tab1.openDiscuss(); await tab2.openDiscuss(); await tab1.click(".o-mail-DiscussCategory-chat .o-mail-DiscussCategory-add"); await tab1.insertText(".o-discuss-ChannelSelector input", "Jer"); + await tab1.advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await tab1.click(".o-discuss-ChannelSelector-suggestion"); await afterNextRender(() => triggerHotkey("Enter")); assert.containsOnce(tab1.target, ".o-mail-DiscussCategoryItem:contains(Jerry Golay)"); diff --git a/addons/mail/static/tests/discuss/core/web/messaging_menu_tests.js b/addons/mail/static/tests/discuss/core/web/messaging_menu_tests.js index 714df5e14d7..ddf2cb7d2af 100644 --- a/addons/mail/static/tests/discuss/core/web/messaging_menu_tests.js +++ b/addons/mail/static/tests/discuss/core/web/messaging_menu_tests.js @@ -9,7 +9,8 @@ import { } from "@mail/../tests/helpers/test_utils"; import { patchUiSize } from "@mail/../tests/helpers/patch_ui_size"; -import { triggerHotkey } from "@web/../tests/helpers/utils"; +import { nextTick, triggerHotkey } from "@web/../tests/helpers/utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("messaging menu"); @@ -18,11 +19,13 @@ QUnit.test('"Start a conversation" item selection opens chat', async (assert) => const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Gandalf" }); pyEnv["res.users"].create({ partner_id: partnerId }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); await click("button:contains(Chat)"); await click("button:contains(Start a conversation)"); await insertText("input[placeholder='Start a conversation']", "Gandalf"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); await afterNextRender(() => triggerHotkey("Enter")); assert.containsOnce($, ".o-mail-ChatWindow-name[title='Gandalf']"); @@ -32,22 +35,26 @@ QUnit.test('"New channel" item selection opens channel (existing)', async (asser patchUiSize({ height: 360, width: 640 }); const pyEnv = await startServer(); pyEnv["discuss.channel"].create({ name: "Gryffindors" }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); await click("button:contains(Channel)"); await click("button:contains(New Channel)"); await insertText("input[placeholder='Add or join a channel']", "Gryff"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); assert.containsOnce($, ".o-mail-ChatWindow-name[title='Gryffindors']"); }); QUnit.test('"New channel" item selection opens channel (new)', async (assert) => { patchUiSize({ height: 360, width: 640 }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); await click("button:contains(Channel)"); await click("button:contains(New Channel)"); await insertText("input[placeholder='Add or join a channel']", "slytherins"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); await click(".o-discuss-ChannelSelector-suggestion"); assert.containsOnce($, ".o-mail-ChatWindow-name[title='slytherins']"); }); diff --git a/addons/mail/static/tests/discuss/core/web/sidebar_tests.js b/addons/mail/static/tests/discuss/core/web/sidebar_tests.js index a7a80ca6b63..88745c54d69 100644 --- a/addons/mail/static/tests/discuss/core/web/sidebar_tests.js +++ b/addons/mail/static/tests/discuss/core/web/sidebar_tests.js @@ -8,6 +8,7 @@ import { start, startServer, } from "@mail/../tests/helpers/test_utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; import { makeDeferred } from "@web/../tests/helpers/utils"; @@ -22,7 +23,8 @@ QUnit.test("sidebar find shows channels matching search term", async (assert) => name: "test", }); const def = makeDeferred(); - const { openDiscuss } = await start({ + const { advanceTime, openDiscuss } = await start({ + hasTimeControl: true, async mockRPC(route, args) { if (args.method === "search_read") { def.resolve(); @@ -32,6 +34,7 @@ QUnit.test("sidebar find shows channels matching search term", async (assert) => await openDiscuss(); await click(".o-mail-DiscussCategory-add"); await insertText(".o-discuss-ChannelSelector input", "test"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); await def; await nextAnimationFrame(); // ensures search_read rpc is rendered. // When searching for a single existing channel, the results list will have at least 2 lines: @@ -52,7 +55,8 @@ QUnit.test( name: "test", }); const def = makeDeferred(); - const { openDiscuss } = await start({ + const { advanceTime, openDiscuss } = await start({ + hasTimeControl: true, async mockRPC(route, args) { if (args.method === "search_read") { def.resolve(); @@ -62,6 +66,7 @@ QUnit.test( await openDiscuss(); await click(".o-mail-DiscussCategory-add"); await insertText(".o-discuss-ChannelSelector input", "test"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); await def; await nextAnimationFrame(); // ensures search_read rpc is rendered. // When searching for a single existing channel, the results list will have at least 2 lines: diff --git a/addons/mail/static/tests/discuss_app/inbox_tests.js b/addons/mail/static/tests/discuss_app/inbox_tests.js index 71d27dc980e..4baa1dffab0 100644 --- a/addons/mail/static/tests/discuss_app/inbox_tests.js +++ b/addons/mail/static/tests/discuss_app/inbox_tests.js @@ -7,8 +7,14 @@ import { start, startServer, } from "@mail/../tests/helpers/test_utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; -import { patchWithCleanup, triggerHotkey, mockTimeout } from "@web/../tests/helpers/utils"; +import { + patchWithCleanup, + triggerHotkey, + mockTimeout, + nextTick, +} from "@web/../tests/helpers/utils"; QUnit.module("discuss inbox"); @@ -57,7 +63,7 @@ QUnit.test("reply: discard on pressing escape", async (assert) => { notification_type: "inbox", res_partner_id: pyEnv.currentPartnerId, }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(); assert.containsOnce($, ".o-mail-Message"); @@ -73,6 +79,9 @@ QUnit.test("reply: discard on pressing escape", async (assert) => { // Escape on suggestion prompt does not stop replying await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); await afterNextRender(() => triggerHotkey("Escape")); assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); diff --git a/addons/mail/static/tests/message/message_tests.js b/addons/mail/static/tests/message/message_tests.js index 30becb2d8f0..54f08496849 100644 --- a/addons/mail/static/tests/message/message_tests.js +++ b/addons/mail/static/tests/message/message_tests.js @@ -16,12 +16,14 @@ import { url } from "@web/core/utils/urls"; import { editInput, makeDeferred, + nextTick, patchWithCleanup, triggerEvent, triggerHotkey, } from "@web/../tests/helpers/utils"; const { DateTime } = luxon; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("message"); @@ -319,11 +321,14 @@ QUnit.test("can add new mentions when editing message", async (assert) => { res_id: channelId, message_type: "comment", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message [title='Edit']"); await insertText(".o-mail-Composer-input", " @"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:contains(TestPartner)"); await click(".o-mail-Message a:contains('save')"); assert.strictEqual($(".o-mail-Message-body")[0].innerText, "Hello @TestPartner"); @@ -1236,12 +1241,15 @@ QUnit.test("Chat with partner should be opened after clicking on their mention", email: "testpartner@odoo.com", }); pyEnv["res.users"].create({ partner_id: partnerId }); - const { openFormView } = await start(); + const { advanceTime, openFormView } = await start({ hasTimeControl: true }); await openFormView("res.partner", partnerId); await click("button:contains(Send message)"); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "T"); await insertText(".o-mail-Composer-input", "e"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:contains(Test Partner)"); await click(".o-mail-Composer-send"); await click(".o_mail_redirect"); @@ -1284,10 +1292,13 @@ QUnit.test("Channel should be opened after clicking on its mention", async (asse const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({}); pyEnv["discuss.channel"].create({ name: "my-channel" }); - const { openFormView } = await start(); + const { advanceTime, openFormView } = await start({ hasTimeControl: true }); await openFormView("res.partner", partnerId); await click("button:contains(Send message)"); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:contains(my-channel)"); await click(".o-mail-Composer-send"); await click(".o_channel_redirect"); diff --git a/addons/mail/static/tests/suggestion/suggestion_tests.js b/addons/mail/static/tests/suggestion/suggestion_tests.js index 9c96b525504..fd91c2e4160 100644 --- a/addons/mail/static/tests/suggestion/suggestion_tests.js +++ b/addons/mail/static/tests/suggestion/suggestion_tests.js @@ -10,6 +10,7 @@ import { patchWithCleanup, triggerHotkey, } from "@web/../tests/helpers/utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("suggestion", { async beforeEach() { @@ -41,11 +42,14 @@ QUnit.test('display partner mention suggestions on typing "@"', async (assert) = Command.create({ partner_id: partnerId_2 }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsN($, ".o-mail-Composer-suggestion", 3); }); @@ -70,7 +74,7 @@ QUnit.test( Command.create({ partner_id: partnerId_2 }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", "first message"); @@ -78,17 +82,23 @@ QUnit.test( await nextTick(); await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsN($, ".o-mail-Composer-suggestion", 3); } ); QUnit.test('display partner mention suggestions on typing "@" in chatter', async (assert) => { const pyEnv = await startServer(); - const { openFormView } = await start(); + const { advanceTime, openFormView } = await start({ hasTimeControl: true }); await openFormView("res.partner", pyEnv.currentPartnerId); await click("button:contains(Send message)"); assert.containsNone($, ".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion:contains(Mitchell Admin)"); }); @@ -105,9 +115,12 @@ QUnit.test("show other channel member in @ mention", async (assert) => { Command.create({ partner_id: partnerId }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion:contains(TestPartner)"); }); @@ -124,9 +137,12 @@ QUnit.test("select @ mention insert mention text in composer", async (assert) => Command.create({ partner_id: partnerId }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:contains(TestPartner)"); assert.strictEqual($(".o-mail-Composer-input").val().trim(), "@TestPartner"); }); @@ -137,10 +153,13 @@ QUnit.test('display channel mention suggestions on typing "#"', async (assert) = name: "General", channel_type: "channel", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); }); @@ -150,11 +169,14 @@ QUnit.test("mention a channel", async (assert) => { name: "General", channel_type: "channel", }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); assert.containsOnce($, ".o-mail-Composer-suggestion"); await click(".o-mail-Composer-suggestion"); assert.strictEqual( diff --git a/addons/mail/static/tests/thread/thread_tests.js b/addons/mail/static/tests/thread/thread_tests.js index 49f75958a17..d1cff7e0465 100644 --- a/addons/mail/static/tests/thread/thread_tests.js +++ b/addons/mail/static/tests/thread/thread_tests.js @@ -21,6 +21,7 @@ import { patchWithCleanup, triggerEvents, } from "@web/../tests/helpers/utils"; +import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; QUnit.module("thread"); @@ -208,9 +209,12 @@ QUnit.test("thread is still scrolling after scrolling up then to bottom", async QUnit.test("mention a channel with space in the name", async (assert) => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General good boy" }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce($(".o-mail-Message-body"), ".o_channel_redirect"); @@ -220,9 +224,12 @@ QUnit.test("mention a channel with space in the name", async (assert) => { QUnit.test('mention a channel with "&" in the name', async (assert) => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General & good" }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce($(".o-mail-Message-body"), ".o_channel_redirect"); @@ -543,10 +550,13 @@ QUnit.test("Mention a partner with special character (e.g. apostrophe ')", async Command.create({ partner_id: partnerId }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Pyn"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce( @@ -575,13 +585,19 @@ QUnit.test("mention 2 different partners that have the same name", async (assert Command.create({ partner_id: partnerId_2 }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Te"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:eq(0)"); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Te"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:eq(1)"); await click(".o-mail-Composer-send"); assert.containsOnce($, ".o-mail-Message-body"); @@ -598,10 +614,13 @@ QUnit.test("mention 2 different partners that have the same name", async (assert QUnit.test("mention a channel on a second line when the first line contains #", async (assert) => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General good" }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#blabla\n"); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce($(".o-mail-Message-body"), ".o_channel_redirect"); @@ -613,9 +632,12 @@ QUnit.test( async (assert) => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General good" }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); const text = $(".o-mail-Composer-input").val(); $(".o-mail-Composer-input").val(text.slice(0, -1)); @@ -639,13 +661,19 @@ QUnit.test("mention 2 different channels that have the same name", async (assert name: "my channel", }, ]); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId_1); await insertText(".o-mail-Composer-input", "#"); await insertText(".o-mail-Composer-input", "m"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:eq(0)"); await insertText(".o-mail-Composer-input", "#"); await insertText(".o-mail-Composer-input", "m"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion:eq(1)"); await click(".o-mail-Composer-send"); assert.containsOnce($, ".o-mail-Message-body"); @@ -674,11 +702,14 @@ QUnit.test( Command.create({ partner_id: partnerId }), ], }); - const { openDiscuss } = await start(); + const { advanceTime, openDiscuss } = await start({ hasTimeControl: true }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "email@odoo.com\n"); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Te"); + await advanceTime(DEBOUNCE_FETCH_SUGGESTION_TIME); + await nextTick(); + await nextTick(); await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce(