From 7422254b021dbdebdb352904c54bf50d2e2a33dc Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Tue, 20 Sep 2022 12:07:44 +0000 Subject: [PATCH] [FIX] bus: fix im_status during tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this PR, the `_mockIrWebsocket__updatePresence` route of the bus mock server could lead to errors when no im status ids were provided. This commit fixes this issue by ensuring im status ids were passed before calling `_mockIrWebsocket__getImStatus` method. Moreover, old longpolling routes were still present on the bus mock server. This commit removes these routes. closes odoo/odoo#100583 Signed-off-by: Alexandre Kühn (aku) --- addons/bus/models/ir_websocket.py | 8 ++- .../bus/static/tests/helpers/mock_server.js | 22 -------- .../mock_server/models/ir_websocket.js | 16 ++++-- addons/mail/models/ir_websocket.py | 5 +- .../mail/static/tests/helpers/mock_server.js | 4 ++ .../mock_server/models/ir_websocket.js | 25 +++++++++ .../tests/helpers/model_definitions_setup.js | 2 +- .../persona_im_status_icon_tests.js | 56 +++++++++++++++++++ 8 files changed, 106 insertions(+), 32 deletions(-) create mode 100644 addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js diff --git a/addons/bus/models/ir_websocket.py b/addons/bus/models/ir_websocket.py index 8f663d16b90..5c1b6b3ffd0 100644 --- a/addons/bus/models/ir_websocket.py +++ b/addons/bus/models/ir_websocket.py @@ -4,6 +4,7 @@ from odoo.service import security from ..models.bus import dispatch from ..websocket import wsrequest + class IrWebsocket(models.AbstractModel): _name = 'ir.websocket' _description = 'websocket message handling' @@ -11,8 +12,9 @@ class IrWebsocket(models.AbstractModel): def _get_im_status(self, im_status_ids_by_model): im_status = {} if 'res.partner' in im_status_ids_by_model: - im_status['partners'] = self.env['res.partner'].with_context(active_test=False).search( - [('id', 'in', im_status_ids_by_model['res.partner'])]).read(['im_status'] + im_status['partners'] = self.env['res.partner'].with_context(active_test=False).search_read( + [('id', 'in', im_status_ids_by_model['res.partner'])], + ['im_status'] ) return im_status @@ -41,7 +43,7 @@ class IrWebsocket(models.AbstractModel): identity_value=self.env.uid ) im_status_notification = self._get_im_status(im_status_ids_by_model) - if im_status_ids_by_model: + if im_status_notification: self.env['bus.bus']._sendone(self.env.user.partner_id, 'bus/im_status', im_status_notification) @classmethod diff --git a/addons/bus/static/tests/helpers/mock_server.js b/addons/bus/static/tests/helpers/mock_server.js index e3983320400..74f0859bfe3 100644 --- a/addons/bus/static/tests/helpers/mock_server.js +++ b/addons/bus/static/tests/helpers/mock_server.js @@ -4,9 +4,7 @@ import { TEST_USER_IDS } from "@bus/../tests/helpers/test_constants"; import { patchWebsocketWorkerWithCleanup } from '@bus/../tests/helpers/mock_websocket'; import { patch } from "@web/core/utils/patch"; -import { ConnectionLostError } from "@web/core/network/rpc_service"; import { MockServer } from "@web/../tests/helpers/mock_server"; -import { makeDeferred } from "@web/../tests/helpers/utils"; patch(MockServer.prototype, 'bus', { init() { @@ -40,26 +38,6 @@ patch(MockServer.prototype, 'bus', { this._mockIrWebsocket__updatePresence(inactivity_period, im_status_ids_by_model); } }, - - /** - * @override - */ - async _performRPC(route, args) { - if (route === '/longpolling/poll') { - const longpollingPromise = makeDeferred(); - if (this.hasLostConnection) { - longpollingPromise.reject(new ConnectionLostError()); - this.hasLostConnection = false; - } else if (this.notificationsToBeResolved.length) { - longpollingPromise.resolve(this.notificationsToBeResolved); - this.notificationsToBeResolved = []; - } else { - this.pendingLongpollingPromise = longpollingPromise; - } - return longpollingPromise; - } - return this._super(route, args); - }, /** * Simulates `_sendone` on `bus.bus`. * diff --git a/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js b/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js index 147f0c8e3b1..6d5158c5604 100644 --- a/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js +++ b/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js @@ -11,7 +11,10 @@ patch(MockServer.prototype, 'bus/models/ir_websocket', { * @param imStatusIdsByModel */ _mockIrWebsocket__updatePresence(inactivityPeriod, imStatusIdsByModel) { - this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', this._mockIrWebsocket__getImStatus(imStatusIdsByModel)); + const imStatusNotifications = this._mockIrWebsocket__getImStatus(imStatusIdsByModel); + if (Object.keys(imStatusNotifications).length > 0) { + this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', imStatusNotifications); + } }, /** * Simulates `_get_im_status` on `ir.websocket`. @@ -20,9 +23,12 @@ patch(MockServer.prototype, 'bus/models/ir_websocket', { * @param {Number[]|undefined} res.partner ids of res.partners whose im_status * should be monitored. */ - _mockIrWebsocket__getImStatus({ 'res.partner': partnerIds }) { - return { - 'partners': this.mockSearchRead('res.partner', [[['id', 'in', partnerIds]]], { context: { 'active_test': false }, fields: ['im_status'] }) - }; + _mockIrWebsocket__getImStatus(imStatusIdsByModel) { + const imStatus = {}; + const { 'res.partner': partnerIds } = imStatusIdsByModel; + if (partnerIds) { + imStatus['partners'] = this.mockSearchRead('res.partner', [[['id', 'in', partnerIds]]], { context: { 'active_test': false }, fields: ['im_status'] }) + } + return imStatus; }, }); diff --git a/addons/mail/models/ir_websocket.py b/addons/mail/models/ir_websocket.py index 733c756b44d..07028759b05 100644 --- a/addons/mail/models/ir_websocket.py +++ b/addons/mail/models/ir_websocket.py @@ -9,7 +9,10 @@ class IrWebsocket(models.AbstractModel): def _get_im_status(self, data): im_status = super()._get_im_status(data) if 'mail.guest' in data: - im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search([('id', 'in', data['mail.guest'])]).read(['im_status']) + im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search_read( + [('id', 'in', data['mail.guest'])], + ['im_status'] + ) return im_status def _build_bus_channel_list(self, channels): diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 67ce38180e2..ae0619e85f0 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -1559,6 +1559,10 @@ patch(MockServer.prototype, 'mail', { ])[0]; response.subtype_description = subtype.description; } + if (message.author_guest_id) { + const [guest] = this.pyEnv['mail.guest'].searchRead([['id', '=', message.author_guest_id]]); + response['guestAuthor'] = { id: guest.id, name: guest.name }; + } return response; }); }, diff --git a/addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js b/addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js new file mode 100644 index 00000000000..f6e6cc80822 --- /dev/null +++ b/addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js @@ -0,0 +1,25 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { MockServer } from "@web/../tests/helpers/mock_server"; + +// ensure bus override is applied first. +import "@bus/../tests/helpers/mock_server"; + +patch(MockServer.prototype, 'mail/models/ir_websocket', { + /** + * Simulates `_get_im_status` on `ir.websocket`. + * + * @param {Object} imStatusIdsByModel + * @param {Number[]|undefined} mail.guest ids of mail.guest whose im_status + * should be monitored. + */ + _mockIrWebsocket__getImStatus(imStatusIdsByModel) { + const imStatus = this._super(imStatusIdsByModel); + const { 'mail.guest': guestIds } = imStatusIdsByModel; + if (guestIds) { + imStatus['guests'] = this.pyEnv['mail.guest'].searchRead([['id', 'in', guestIds]], { context: { 'active_test': false }, fields: ['im_status'] }); + } + return imStatus; + }, +}); diff --git a/addons/mail/static/tests/helpers/model_definitions_setup.js b/addons/mail/static/tests/helpers/model_definitions_setup.js index 538da118fce..9efad779e15 100644 --- a/addons/mail/static/tests/helpers/model_definitions_setup.js +++ b/addons/mail/static/tests/helpers/model_definitions_setup.js @@ -14,7 +14,7 @@ import { addModelNamesToFetch([ 'mail.activity', 'mail.activity.type', 'mail.channel', 'mail.channel.member', - 'mail.channel.rtc.session', 'mail.followers', 'mail.link.preview', 'mail.message', + 'mail.channel.rtc.session', 'mail.followers', 'mail.guest', 'mail.link.preview', 'mail.message', 'mail.message.subtype', 'mail.notification', 'mail.shortcode', 'mail.template', 'mail.tracking.value', 'res.users.settings', 'res.users.settings.volumes' ]); diff --git a/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js b/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js index 3723f8cc3d7..235b1ec287e 100644 --- a/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js @@ -148,5 +148,61 @@ QUnit.test('change icon on change partner im_status', async function (assert) { ); }); +QUnit.test('change icon on change guest im_status', async function (assert) { + assert.expect(4); + + const pyEnv = await startServer(); + const guestId = pyEnv['mail.guest'].create({ im_status: 'online' }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_member_ids: [[0, 0, { partner_id: pyEnv.currentPartnerId }], [0, 0, { guest_id: guestId }]], + channel_type: 'group', + }); + pyEnv['mail.message'].create({ + author_guest_id: guestId, + author_id: false, + body: 'not empty', + model: 'mail.channel', + res_id: mailChannelId, + }); + const { advanceTime, afterNextRender, openDiscuss } = await start({ + discuss: { + params: { + default_active_id: mailChannelId, + }, + }, + hasTimeControl: true, + }); + await openDiscuss(); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, + 1, + "persona IM status icon should have online status rendering" + ); + + pyEnv['mail.guest'].write([guestId], { im_status: 'offline' }); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-offline`).length, + 1, + "persona IM status icon should have offline status rendering" + ); + + pyEnv['mail.guest'].write([guestId], { im_status: 'away' }); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-away`).length, + 1, + "persona IM status icon should have away status rendering" + ); + + pyEnv['mail.guest'].write([guestId], { im_status: 'online' }); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, + 1, + "persona IM status icon should have online status rendering in the end" + ); +}); + }); });