odoo.define('mail.discuss_test', function (require) { "use strict"; var Discuss = require('mail.Discuss'); var mailTestUtils = require('mail.testUtils'); var concurrency = require('web.concurrency'); var SearchView = require('web.SearchView'); var testUtils = require('web.test_utils'); var createDiscuss = mailTestUtils.createDiscuss; QUnit.module('mail', {}, function () { QUnit.module('Discuss', { beforeEach: function () { // patch _.debounce and _.throttle to be fast and synchronous this.underscoreDebounce = _.debounce; this.underscoreThrottle = _.throttle; _.debounce = _.identity; _.throttle = _.identity; this.data = { 'mail.message': { fields: { body: { string: "Contents", type: 'html', }, author_id: { string: "Author", relation: 'res.partner', }, channel_ids: { string: "Channels", type: 'many2many', relation: 'mail.channel', }, starred: { string: "Starred", type: 'boolean', }, needaction: { string: "Need Action", type: 'boolean', }, needaction_partner_ids: { string: "Partners with Need Action", type: 'many2many', relation: 'res.partner', }, starred_partner_ids: { string: "Favorited By", type: 'many2many', relation: 'res.partner', }, model: { string: "Related Document model", type: 'char', }, res_id: { string: "Related Document ID", type: 'integer', }, }, }, }; this.services = mailTestUtils.getMailServices(); }, afterEach: function () { // unpatch _.debounce and _.throttle _.debounce = this.underscoreDebounce; _.throttle = this.underscoreThrottle; } }); QUnit.test('basic rendering', function (assert) { assert.expect(5); var done = assert.async(); createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, }) .then(function (discuss) { // test basic rendering var $sidebar = discuss.$('.o_mail_discuss_sidebar'); assert.strictEqual($sidebar.length, 1, "should have rendered a sidebar"); assert.strictEqual(discuss.$('.o_mail_discuss_content').length, 1, "should have rendered the content"); assert.strictEqual(discuss.$('.o_mail_no_content').length, 1, "should display no content message"); var $inbox = $sidebar.find('.o_mail_discuss_item[data-thread-id=mailbox_inbox]'); assert.strictEqual($inbox.length, 1, "should have the channel item 'mailbox_inbox' in the sidebar"); var $starred = $sidebar.find('.o_mail_discuss_item[data-thread-id=mailbox_starred]'); assert.strictEqual($starred.length, 1, "should have the channel item 'mailbox_starred' in the sidebar"); discuss.destroy(); done(); }); }); QUnit.test('searchview options visibility', function (assert) { assert.expect(4); var done = assert.async(); createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, }) .then(function (discuss) { var $searchviewOptions = $('.o_search_options > div'); var $searchviewOptionsToggler = $('.o_searchview_more.fa.fa-search-minus'); assert.strictEqual($searchviewOptions.length, 1, "should have search options"); assert.strictEqual($searchviewOptionsToggler.length, 1, "should have a button to toggle search options"); assert.strictEqual($searchviewOptions.css('display'), 'block', "search options should be visible by default"); $searchviewOptionsToggler.click(); assert.strictEqual($searchviewOptions.css('display'), 'none', "search options should be hidden after clicking on search option toggler"); discuss.destroy(); done(); }); }); QUnit.test('searchview filter messages', function (assert) { assert.expect(10); var done = assert.async(); this.data['mail.message'].records = [{ author_id: [5, 'Demo User'], body: '
abc
', id: 1, needaction: true, needaction_partner_ids: [3], }, { author_id: [6, 'Test User'], body: 'def
', id: 2, needaction: true, needaction_partner_ids: [3], }]; createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, archs: { 'mail.message,false,search': 'test ' + i + '
', channel_ids: [1], id: i, }); } this.data.initMessaging = { channel_slots: { channel_channel: [{ id: 1, channel_type: "channel", name: "general", static: true, }], }, }; this.data['mail.message'].records = messageData; createDiscuss({ context: {}, data: this.data, params: {}, services: this.services, mockRPC: function (route, args) { if (args.method === 'message_fetch') { assert.step(args.method); fetchCount++; // 1st fetch: inbox initial fetch // 2nd fetch: general initial fetch // 3rd fetch: general load more if (fetchCount === 1) { assert.strictEqual(args.kwargs.limit, 30, "should ask to fetch 30 messages at most"); } if (fetchCount === 3) { loadMoreDef.resolve(); } } return this._super.apply(this, arguments); }, }).then(function (discuss) { assert.verifySteps(['message_fetch'], "should fetch messages once for needaction messages (Inbox)"); var $general = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); assert.strictEqual($general.length, 1, "should have a channel item with id 1"); // switch to 'general' $general.click(); assert.verifySteps(['message_fetch', 'message_fetch'], "should fetch a second time for general channel messages (30 last messages)"); assert.strictEqual(discuss.$('.o_thread_message').length, 30, "should display the 30 messages"); // simulate a scroll to top to load more messages discuss.$('.o_mail_thread').scrollTop(0); loadMoreDef .then(concurrency.delay.bind(concurrency, 0)) .then(function () { assert.verifySteps(['message_fetch', 'message_fetch', 'message_fetch'], "should fetch a third time for general channel messages (5 remaining messages)"); assert.strictEqual(discuss.$('.o_thread_message').length, 35, "all messages should now be loaded"); discuss.destroy(); done(); }); }); }); QUnit.test('"Unstar all" button should reset the starred counter', function (assert) { assert.expect(2); var done = assert.async(); var messageData = []; _.each(_.range(1, 41), function (num) { messageData.push({ id: num, body: "test" + num + "
", author_id: ["1", "Me"], channel_ids: [1], starred: true, starred_partner_ids: [1], } ); }); this.data.initMessaging = { channel_slots: { channel_channel: [{ id: 1, channel_type: "channel", name: "general", }], }, starred_counter: messageData.length, }; this.data['mail.message'].records = messageData; var objectDiscuss; createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, mockRPC: function (route, args) { if (args.method === 'unstar_all') { var data = { message_ids: _.range(1, 41), starred: false, type: 'toggle_star', }; var notification = [[false, 'res.partner'], data]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); return $.when(42); } return this._super.apply(this, arguments); }, session: {partner_id: 1}, }) .then(function (discuss) { objectDiscuss = discuss; var $starred = discuss.$('.o_mail_discuss_sidebar').find('.o_mail_mailbox_title_starred'); var $starredCounter = $('.o_mail_mailbox_title_starred > .o_mail_sidebar_needaction'); // Go to Starred channel $starred.click(); // Test Initial Value assert.strictEqual($starredCounter.text().trim(), "40", "40 messages should be starred"); // Unstar all and wait 'update_starred' $('.o_control_panel .o_mail_discuss_button_unstar_all').click(); $starredCounter = $('.o_mail_mailbox_title_starred > .o_mail_sidebar_needaction'); assert.strictEqual($starredCounter.text().trim(), "0", "All messages should be unstarred"); discuss.destroy(); done(); }); }); QUnit.test('do not crash when destroyed before start is completed', function (assert) { assert.expect(3); var discuss; testUtils.patch(Discuss, { init: function () { discuss = this; this._super.apply(this, arguments); }, }); createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, mockRPC: function (route, args) { if (args.method) { assert.step(args.method); } var result = this._super.apply(this, arguments); if (args.method === 'message_fetch') { discuss.destroy(); } return result; }, }); assert.verifySteps([ "load_views", "message_fetch" ]); testUtils.unpatch(Discuss); }); QUnit.test('do not crash when destroyed between start en end of _renderSearchView', function (assert) { assert.expect(2); var discuss; testUtils.patch(Discuss, { init: function () { discuss = this; this._super.apply(this, arguments); }, }); var def = $.Deferred(); testUtils.patch(SearchView, { willStart: function () { var result = this._super.apply(this, arguments); return def.then($.when(result)); }, }); createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, mockRPC: function (route, args) { if (args.method) { assert.step(args.method); } return this._super.apply(this, arguments); }, }); discuss.destroy(); def.resolve(); assert.verifySteps([ "load_views", ]); testUtils.unpatch(Discuss); testUtils.unpatch(SearchView); }); QUnit.test('confirm dialog when administrator leave (not chat) channel', function (assert) { assert.expect(2); var done = assert.async(); this.data.initMessaging = { channel_slots: { channel_channel: [{ id: 1, channel_type: "channel", name: "MyChannel", create_uid: 3, }], }, }; createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { uid: 3, }, }) .then(function (discuss) { // Unsubscribe on MyChannel as administrator discuss.$('.o_mail_partner_unpin').click(); assert.strictEqual($('.modal-dialog').length, 1, "should display a dialog"); assert.strictEqual($('.modal-body').text(), "You are the administrator of this channel. Are you sure you want to unsubscribe?", "Warn user that he will be unsubscribed from channel as admin."); discuss.destroy(); done(); }); }); QUnit.test('convert emoji sources to unicodes on message_post', function (assert) { assert.expect(2); var done = assert.async(); var receiveMessageDef = $.Deferred(); this.data.initMessaging = { channel_slots: { channel_channel: [{ id: 1, channel_type: "channel", name: "general", }], }, }; var objectDiscuss; createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, mockRPC: function (route, args) { if (args.method === 'message_post') { assert.strictEqual(args.kwargs.body, "😊 😂", "message_post data should have all emojis in their unicode representation"); var data = { author_id: ["42", "Me"], body: args.kwargs.body, channel_ids: [1], }; var notification = [[false, 'mail.channel', 1], data]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); receiveMessageDef.resolve(); return $.when(42); } return this._super.apply(this, arguments); }, }) .then(function (discuss) { objectDiscuss= discuss; var $general = discuss.$('.o_mail_discuss_sidebar') .find('.o_mail_discuss_item[data-thread-id=1]'); // click on general $general.click(); var $input = discuss.$('textarea.o_composer_text_field').first(); $input.focus(); $input.val(":) x'D"); $input.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); receiveMessageDef .then(concurrency.delay.bind(concurrency, 0)) .then(function () { assert.strictEqual(discuss.$('.o_thread_message_content').text().replace(/\s/g, ""), "😊😂", "New posted message should contain all emojis in their unicode representation"); discuss.destroy(); done(); }); }); }); QUnit.test('mark all messages as read from Inbox', function (assert) { var done = assert.async(); assert.expect(9); this.data['mail.message'].records = [{ author_id: [5, 'Demo User'], body: 'test 1
', id: 1, needaction: true, needaction_partner_ids: [3], }, { author_id: [6, 'Test User'], body: 'test 2
', id: 2, needaction: true, needaction_partner_ids: [3], }]; this.data.initMessaging = { needaction_inbox_counter: 2, }; var markAllReadDef = $.Deferred(); var objectDiscuss; createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, mockRPC: function (route, args) { if (args.method === 'mark_all_as_read') { _.each(this.data['mail.message'].records, function (message) { message.needaction = false; }); var notificationData = { type: 'mark_as_read', message_ids: [1, 2], }; var notification = [[false, 'res.partner', 3], notificationData]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); markAllReadDef.resolve(); return $.when(); } return this._super.apply(this, arguments); }, }) .then(function (discuss) { objectDiscuss = discuss; var $inbox = discuss.$('.o_mail_discuss_item[data-thread-id="mailbox_inbox"]'); assert.strictEqual($inbox.length, 1, "there should be an 'Inbox' item in Discuss sidebar"); assert.strictEqual($inbox.find('.o_mail_sidebar_needaction').text().trim(), "2", "the mailbox counter of 'Inbox' should be 2"); assert.ok($inbox.hasClass('o_active'), "'Inbox' should be the currently active thread"); assert.strictEqual(discuss.$('.o_thread_message').length, 2, "there should be 2 messages in inbox"); var $markAllReadButton = $('.o_mail_discuss_button_mark_all_read'); assert.strictEqual($markAllReadButton.length, 1, "there should be a 'Mark All As Read' button"); assert.strictEqual($markAllReadButton.attr('style'), 'display: inline-block;', "the 'Mark All As Read' button should be visible"); assert.notOk($markAllReadButton.prop('disabled'), "the 'Mark All As Read' button should not be disabled"); $markAllReadButton.click(); markAllReadDef.then(function () { // immediately jump to end of the fadeout animation on messages $inbox = discuss.$('.o_mail_discuss_item[data-thread-id="mailbox_inbox"]'); discuss.$('.o_thread_message').stop(false, true); assert.strictEqual($inbox.find('.o_mail_sidebar_needaction').text().trim(), "0", "the mailbox counter of 'Inbox' should have reset to 0"); assert.strictEqual(discuss.$('.o_thread_message').length, 0, "there should no message in inbox anymore"); discuss.destroy(); done(); }); }); }); }); });