From a72b23f6b5fbcf637d5de16f9f05ba00552cc186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Fri, 9 Aug 2019 10:53:36 +0000 Subject: [FIX] mail: register thread window after append in DOM Before this commit, we may have the following error: `TypeError: Cannot read property 'html' of undefined` This happened in the following scenario: Terminology: - in multi-tab environment, there is a master tab and slave tabs - master tab fetches notifications from the server and forwards them to slave tabs State: - 2 logged-in users: Michell & Marc - Mitchell has open 2 browser tabs - Mitchell has a pinned private conversation with Marc Steps: - Mitchell has focus on a slave tab - Marc posts several messages to Mitchell in very quick succession => Mitchell gets crash error above. This issue is caused by chat window being registered in mail manager while it has not been mounted to DOM. On receiving 2nd new message, it has to re-render chat window header in order to update displayed unread counter. However, this fails due to chat window having no DOM node yet. This commit solves the issue by mounting chat window to DOM before being registered in mail manager. That way, on receiving 2nd new message, it does not trigger a re-render on chat window that is about to be mounted to DOM, since it has not been registered yet. Note that the displayed unread counter will be correct, because it uses the up-to-date unread counter on mount. Task-ID 2050108 closes odoo/odoo#35601 Signed-off-by: Martin Geubelle (mge) --- .../thread_window/basic_thread_window_tests.js | 337 +++++++++++---------- 1 file changed, 174 insertions(+), 163 deletions(-) (limited to 'addons/mail/static/tests/thread_window/basic_thread_window_tests.js') diff --git a/addons/mail/static/tests/thread_window/basic_thread_window_tests.js b/addons/mail/static/tests/thread_window/basic_thread_window_tests.js index 7c3cfbdc793..3200ae38d85 100644 --- a/addons/mail/static/tests/thread_window/basic_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/basic_thread_window_tests.js @@ -16,6 +16,29 @@ QUnit.module('Basic', { // define channel to link to chat window this.data = { + 'mail.channel': { + fields: { + name: { + string: "Name", + type: "char", + required: true, + }, + channel_type: { + string: "Channel Type", + type: "selection", + }, + channel_message_ids: { + string: "Messages", + type: "many2many", + relation: 'mail.message' + }, + message_unread_counter: { + string: "Amount of Unread Messages", + type: "integer" + }, + }, + records: [], + }, 'mail.message': { fields: {}, records: [], @@ -292,34 +315,12 @@ QUnit.test('do not increment unread counter with focus on thread window', async QUnit.test('do not mark as read the newly open thread window from received message', async function (assert) { assert.expect(5); - this.data['mail.channel'] = { - fields: { - name: { - string: "Name", - type: "char", - required: true, - }, - channel_type: { - string: "Channel Type", - type: "selection", - }, - channel_message_ids: { - string: "Messages", - type: "many2many", - relation: 'mail.message' - }, - message_unread_counter: { - string: "Amount of Unread Messages", - type: "integer" - }, - }, - records: [{ - id: 2, - name: "DM", - channel_type: "chat", - message_unread_counter: 0, - }], - }; + this.data['mail.channel'].records = [{ + id: 2, + name: "DM", + channel_type: "chat", + message_unread_counter: 0, + }]; var parent = this.createParent({ data: this.data, @@ -371,33 +372,11 @@ QUnit.test('do not mark as read the newly open thread window from received messa QUnit.test('show document link of message linked to a document', async function (assert) { assert.expect(6); - this.data['mail.channel'] = { - fields: { - name: { - string: "Name", - type: "char", - required: true, - }, - channel_type: { - string: "Channel Type", - type: "selection", - }, - channel_message_ids: { - string: "Messages", - type: "many2many", - relation: 'mail.message' - }, - message_unread_counter: { - string: "Amount of Unread Messages", - type: "integer" - }, - }, - records: [{ - id: 2, - name: "R&D Tasks", - channel_type: "channel", - }], - }; + this.data['mail.channel'].records = [{ + id: 2, + name: "R&D Tasks", + channel_type: "channel", + }]; this.data['mail.message'].records.push({ author_id: [5, "Someone else"], body: "

Test message

", @@ -460,35 +439,13 @@ QUnit.test('do not autofocus chat window on receiving new direct message', async }], }; - this.data['mail.channel'] = { - fields: { - name: { - string: "Name", - type: "char", - required: true, - }, - channel_type: { - string: "Channel Type", - type: "selection", - }, - channel_message_ids: { - string: "Messages", - type: "many2many", - relation: 'mail.message' - }, - message_unread_counter: { - string: "Amount of Unread Messages", - type: "integer" - }, - }, - records: [{ - id: 2, - name: "DM", - channel_type: "chat", - message_unread_counter: 0, - direct_partner: [{ id: 666, name: 'DemoUser1', im_status: '' }], - }], - }; + this.data['mail.channel'].records = [{ + id: 2, + name: "DM", + channel_type: "chat", + message_unread_counter: 0, + direct_partner: [{ id: 666, name: 'DemoUser1', im_status: '' }], + }]; var form = await testUtils.createView({ View: FormView, @@ -543,37 +500,15 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', assert.expect(10); var self = this; - this.data['mail.channel'] = { - fields: { - name: { - string: "Name", - type: "char", - required: true, - }, - channel_type: { - string: "Channel Type", - type: "selection", - }, - channel_message_ids: { - string: "Messages", - type: "many2many", - relation: 'mail.message' - }, - message_unread_counter: { - string: "Amount of Unread Messages", - type: "integer" - }, - }, - records: [{ - id: 2, - name: "DM", - channel_type: "chat", - message_unread_counter: 1, - direct_partner: [{ id: 666, name: 'DemoUser1', im_status: '' }], - is_minimized: false, - state: 'open', - }], - }; + this.data['mail.channel'].records = [{ + id: 2, + name: "DM", + channel_type: "chat", + message_unread_counter: 1, + direct_partner: [{ id: 666, name: 'DemoUser1', im_status: '' }], + is_minimized: false, + state: 'open', + }]; var parent = this.createParent({ data: this.data, @@ -654,21 +589,15 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', QUnit.test('out-of-office status in thread window', async function (assert) { assert.expect(1); - this.data = { - 'mail.message': { - fields: {}, - records: [], - }, - initMessaging: { - channel_slots: { - channel_channel: [{ - id: 1, - name: "DM", - channel_type: "chat", - message_unread_counter: 0, - direct_partner: [{ id: 666, name: 'DemoUser1', im_status: 'online', out_of_office_message: 'Please don\'t disturb'}], - }], - }, + this.data.initMessaging = { + channel_slots: { + channel_channel: [{ + id: 1, + name: "DM", + channel_type: "chat", + message_unread_counter: 0, + direct_partner: [{ id: 666, name: 'DemoUser1', im_status: 'online', out_of_office_message: 'Please don\'t disturb'}], + }], }, }; var parent = this.createParent({ @@ -691,21 +620,15 @@ QUnit.test('out-of-office status in thread window', async function (assert) { QUnit.test('no out-of-office status in thread window', async function (assert) { assert.expect(1); - this.data = { - 'mail.message': { - fields: {}, - records: [], - }, - initMessaging: { - channel_slots: { - channel_channel: [{ - id: 1, - name: "DM", - channel_type: "chat", - message_unread_counter: 0, - direct_partner: [{ id: 666, name: 'DemoUser1', im_status: 'online'}], - }], - }, + this.data.initMessaging = { + channel_slots: { + channel_channel: [{ + id: 1, + name: "DM", + channel_type: "chat", + message_unread_counter: 0, + direct_partner: [{ id: 666, name: 'DemoUser1', im_status: 'online'}], + }], }, }; var parent = this.createParent({ @@ -734,25 +657,19 @@ QUnit.test('auto-update out-of-office info on im_status change', async function var timeoutMock = mailTestUtils.patchMailTimeouts(); var step = 0; - this.data = { - 'mail.message': { - fields: {}, - records: [], - }, - initMessaging: { - channel_slots: { - channel_channel: [{ - id: 1, - name: "DM", - channel_type: "chat", - message_unread_counter: 0, - direct_partner: [{ - id: 666, - name: 'DemoUser1', - im_status: 'online', - }], + this.data.initMessaging = { + channel_slots: { + channel_channel: [{ + id: 1, + name: "DM", + channel_type: "chat", + message_unread_counter: 0, + direct_partner: [{ + id: 666, + name: 'DemoUser1', + im_status: 'online', }], - }, + }], }, }; @@ -839,6 +756,100 @@ QUnit.test('auto-update out-of-office info on im_status change', async function parent.destroy(); }); +QUnit.test('receive 2 new DM messages in quick succession (no chat window initially)', async function (assert) { + assert.expect(3); + + const self = this; + this.data['mail.channel'].records = [{ + channel_type: "chat", + direct_partner: [{ + id: 5, + name: 'Someone else', + im_status: 'online', + }], + id: 10, + is_minimized: false, + message_unread_counter: 1, + name: "DM", + state: 'open', + }]; + + this.data.initMessaging.channel_slots = { + channel_direct_message: [{ + channel_type: 'chat', + direct_partner: [{ + id: 5, + name: 'Someone else', + im_status: 'online', + }], + id: 10, + message_unread_counter: 0, + name: "DM", + }], + }; + + const parent = this.createParent({ + data: this.data, + mockRPC(route, args) { + if (args.method === 'channel_minimize') { + Object.assign(self.data['mail.channel'].records[0], { + is_minimized: true, + state: 'open', + }); + } + return this._super(...arguments); + }, + services: this.services, + session: { + partner_id: 3, + }, + }); + + await testUtils.nextTick(); + assert.containsNone( + $, + '.o_thread_window', + "should not have any DM window open"); + + // simulate receiving 2 new messages from someone else in quick succession + const messageData1 = { + author_id: [5, "Someone else"], + body: "

Test message1

", + channel_ids: [10], + id: 2, + model: 'mail.channel', + res_id: 10, + }; + this.data['mail.message'].records.push(messageData1); + const notification1 = [[false, 'mail.channel', 2], messageData1]; + parent.call('bus_service', 'trigger', 'notification', [notification1]); + // simulate short delay for receiving new message + await testUtils.nextMicrotaskTick(); + const messageData2 = { + author_id: [5, "Someone else"], + body: "

Test message2

", + channel_ids: [10], + id: 3, + model: 'mail.channel', + res_id: 10, + }; + this.data['mail.message'].records.push(messageData2); + const notification2 = [[false, 'mail.channel', 2], messageData2]; + parent.call('bus_service', 'trigger', 'notification', [notification2]); + await testUtils.nextTick(); + assert.containsOnce( + $, + '.o_thread_window', + "should have DM window open"); + assert.containsN( + $('.o_thread_window'), + '.o_thread_message', + 2, + "should have 2 messages in chat window"); + + parent.destroy(); +}); + }); }); }); -- cgit v1.2.3