From 1ae64793fa489b2c1d07408cd9671bc7126f78fa Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Wed, 6 Mar 2019 14:02:19 +0100 Subject: [PATCH] [REF] mail: adapt code after jQuery update Part of task 1896658 Co-authored-by: Aaron Bohy Co-authored-by: Christophe Matthieu Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: David Monjoie Co-authored-by: Martin Geubelle Co-authored-by: svs-odoo Co-authored-by: Vincent Schippefilt --- addons/mail/static/src/js/activity.js | 22 +- addons/mail/static/src/js/chatter.js | 53 +- .../static/src/js/composers/basic_composer.js | 55 +- .../src/js/composers/chatter_composer.js | 274 ++-- .../src/js/composers/extended_composer.js | 2 +- .../src/js/composers/mention_manager.js | 2 +- addons/mail/static/src/js/discuss.js | 27 +- addons/mail/static/src/js/discuss_mobile.js | 4 +- addons/mail/static/src/js/document_viewer.js | 4 +- addons/mail/static/src/js/followers.js | 38 +- addons/mail/static/src/js/form_renderer.js | 12 +- .../static/src/js/many2many_tags_email.js | 58 +- .../static/src/js/models/messages/message.js | 2 +- .../src/js/models/threads/abstract_thread.js | 14 +- .../static/src/js/models/threads/channel.js | 32 +- .../src/js/models/threads/document_thread.js | 14 +- .../static/src/js/models/threads/livechat.js | 2 +- .../static/src/js/models/threads/mailbox.js | 6 +- .../threads/mixins/thread_typing_mixin.js | 4 +- .../js/models/threads/multi_user_channel.js | 2 +- .../js/models/threads/searchable_thread.js | 4 +- .../static/src/js/models/threads/thread.js | 22 +- .../src/js/models/threads/two_user_channel.js | 2 +- .../services/mail_document_thread_manager.js | 2 +- .../static/src/js/services/mail_manager.js | 152 +- .../js/services/mail_notification_manager.js | 29 +- .../src/js/services/mail_status_manager.js | 2 +- .../src/js/services/mail_window_manager.js | 27 +- .../src/js/systray/systray_activity_menu.js | 2 +- .../src/js/systray/systray_messaging_menu.js | 4 +- addons/mail/static/src/js/thread_field.js | 14 +- addons/mail/static/src/js/thread_widget.js | 10 +- .../thread_windows/abstract_thread_window.js | 2 +- .../src/js/thread_windows/thread_window.js | 4 +- .../src/js/views/activity/activity_model.js | 6 +- .../js/views/activity/activity_renderer.js | 33 +- addons/mail/static/tests/activity_tests.js | 64 +- addons/mail/static/tests/chatter_tests.js | 572 ++++---- .../mail/static/tests/discuss_mobile_tests.js | 94 +- .../static/tests/discuss_moderation_tests.js | 107 +- .../tests/discuss_seen_indicator_tests.js | 807 +++++------ addons/mail/static/tests/discuss_tests.js | 1278 ++++++++--------- .../discuss_typing_notification_tests.js | 719 +++++----- .../tests/document_thread_window_tests.js | 73 +- .../static/tests/document_viewer_tests.js | 45 +- .../mail/static/tests/helpers/mock_server.js | 26 +- .../mail/static/tests/helpers/test_utils.js | 6 +- .../static/tests/mail_status_manager_tests.js | 27 +- .../models/cc_throttle_function_tests.js | 20 +- .../mail/static/tests/models/timer_tests.js | 35 +- .../systray/systray_activity_menu_tests.js | 45 +- ...stray_messaging_menu_mail_failure_tests.js | 18 +- .../systray/systray_messaging_menu_tests.js | 138 +- .../basic_thread_window_tests.js | 109 +- .../blank_thread_window_tests.js | 83 +- .../hidden_thread_window_tests.js | 15 +- .../moderation_thread_window_tests.js | 5 +- .../typing_thread_window_tests.js | 13 +- .../static/tests/tools/debug_manager_tests.js | 9 +- 59 files changed, 2613 insertions(+), 2637 deletions(-) diff --git a/addons/mail/static/src/js/activity.js b/addons/mail/static/src/js/activity.js index 68ff4b0b86f..607f1b8c553 100644 --- a/addons/mail/static/src/js/activity.js +++ b/addons/mail/static/src/js/activity.js @@ -25,11 +25,11 @@ var _t = core._t; * * @param {Widget} self a widget instance that can perform RPCs * @param {Array} ids the ids of activities to read - * @return {Deferred} resolved with the activities + * @return {Promise} resolved with the activities */ function _readActivities(self, ids) { if (!ids.length) { - return $.when([]); + return Promise.resolve([]); } var context = self.getSession().user_context; if (self.record && !_.isEmpty(self.record.getContext())) { @@ -64,7 +64,7 @@ BasicModel.include({ * @private * @param {Object} record - an element from the localData * @param {string} fieldName - * @return {Deferred} resolved with the activities + * @return {Promise} resolved with the activities */ _fetchSpecialActivity: function (record, fieldName) { var localID = (record._changes && fieldName in record._changes) ? @@ -144,7 +144,7 @@ var BasicActivity = AbstractField.extend({ /** * @param {integer} previousActivityTypeID - * @return {$.Promise} + * @return {Promise} */ scheduleActivity: function () { var callback = this._reload.bind(this, { activity: true, thread: true }); @@ -213,7 +213,7 @@ var BasicActivity = AbstractField.extend({ * @param {integer} id * @param {integer} previousActivityTypeID * @param {function} callback - * @return {$.Deferred} + * @return {Promise} */ _openActivityForm: function (id, callback) { var action = { @@ -237,7 +237,7 @@ var BasicActivity = AbstractField.extend({ * @param {integer} activityID * @param {string} feedback * @param {integer[]} attachmentIds - * @return {$.Promise} + * @return {Promise} */ _sendActivityFeedback: function (activityID, feedback, attachmentIds) { return this._rpc({ @@ -303,7 +303,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Promise} + * @returns {Promise} */ _onEditActivity: function (ev) { ev.preventDefault(); @@ -449,7 +449,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Deferred} + * @returns {Promise} */ _onPreviewMailTemplate: function (ev) { ev.stopPropagation(); @@ -477,7 +477,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Promise} + * @returns {Promise} */ _onSendMailTemplate: function (ev) { ev.stopPropagation(); @@ -493,7 +493,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Deferred} + * @returns {Promise} */ _onScheduleActivity: function (ev) { ev.preventDefault(); @@ -504,7 +504,7 @@ var BasicActivity = AbstractField.extend({ * @private * @param {MouseEvent} ev * @param {Object} options - * @returns {$.Promise} + * @returns {Promise} */ _onUnlinkActivity: function (ev, options) { ev.preventDefault(); diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 72971fbeee9..397ae2c8a16 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -103,7 +103,7 @@ var Chatter = Widget.extend({ })); // start and append the widgets var fieldDefs = _.invoke(this.fields, 'appendTo', $('
')); - var def = this._dp.add($.when.apply($, fieldDefs)); + var def = this._dp.add(Promise.all(fieldDefs)); this._render(def).then(this._updateMentionSuggestions.bind(this)); return this._super.apply(this, arguments); @@ -153,7 +153,7 @@ var Chatter = Widget.extend({ fieldsToReset = this.fields; } var fieldDefs = _.invoke(fieldsToReset, 'reset', record); - var def = this._dp.add($.when.apply($, fieldDefs)); + var def = this._dp.add(Promise.all(fieldDefs)); this._render(def).then(function () { self.$el.height('auto'); self._updateMentionSuggestions(); @@ -203,17 +203,18 @@ var Chatter = Widget.extend({ * Discard changes on the record. * * @private - * @returns {$.Deferred} resolved if successfully discarding changes on + * @returns {Promise} resolved if successfully discarding changes on * the record, rejected otherwise */ _discardChanges: function () { - var def = $.Deferred(); - this.trigger_up('discard_changes', { - recordID: this.record.id, - onSuccess: def.resolve.bind(def), - onFailure: def.reject.bind(def), + var self = this; + return new Promise(function (resolve, reject) { + self.trigger_up('discard_changes', { + recordID: self.record.id, + onSuccess: resolve, + onFailure: reject, + }); }); - return def; }, /** * Discard changes on the record if the message will reload the record @@ -221,14 +222,14 @@ var Chatter = Widget.extend({ * * @private * @param {Object} messageData - * @return {$.Deferred} resolved if no reload or proceed to discard the + * @return {Promise} resolved if no reload or proceed to discard the * changes on the record, rejected otherwise */ _discardOnReload: function (messageData) { if (this._reloadAfterPost(messageData)) { return this._discardChanges(); } - return $.when(); + return Promise.resolve(); }, /** * @private @@ -323,7 +324,7 @@ var Chatter = Widget.extend({ self._reloadAttachmentBox(); self.trigger_up('reload', {fieldNames: ['message_attachment_count'], keepChanges: true}); } - }).fail(function () { + }).guardedCatch(function () { self._enableComposer(); }); }); @@ -375,9 +376,9 @@ var Chatter = Widget.extend({ _render: function (def) { // the rendering of the chatter is aynchronous: relational data of its fields needs to be // fetched (in some case, it might be synchronous as they hold an internal cache). - // this function takes a deferred as argument, which is resolved once all fields have + // this function takes a promise as argument, which is resolved once all fields have // fetched their data - // this function appends the fields where they should be once the given deferred is resolved + // this function appends the fields where they should be once the given promise is resolved // and if it takes more than 500ms, displays a spinner to indicate that it is loading var self = this; @@ -385,6 +386,11 @@ var Chatter = Widget.extend({ concurrency.rejectAfter(concurrency.delay(500), def).then(function () { $spinner.appendTo(self.$el); }); + var always = function () { + // disable widgets in create mode, otherwise enable + self._isCreateMode ? self._disableChatter() : self._enableChatter(); + $spinner.remove(); + }; return def.then(function () { if (self.fields.activity) { @@ -400,11 +406,7 @@ var Chatter = Widget.extend({ if (self.fields.thread) { self.fields.thread.$el.appendTo(self.$el); } - }).always(function () { - // disable widgets in create mode, otherwise enable - self._isCreateMode ? self._disableChatter() : self._enableChatter(); - $spinner.remove(); - }); + }).then(always).guardedCatch(always); }, /** * @private @@ -517,7 +519,7 @@ var Chatter = Widget.extend({ if (!this._areAttachmentsLoaded) { def = this._fetchAttachments(); } - $.when(def).then(this._openAttachmentBox.bind(this)); + Promise.resolve(def).then(this._openAttachmentBox.bind(this)); } }, /** @@ -534,10 +536,10 @@ var Chatter = Widget.extend({ _onOpenComposerMessage: function () { var self = this; if (!this.suggested_partners_def) { - this.suggested_partners_def = $.Deferred(); - var method = 'message_get_suggested_recipients'; - var args = [[this.context.default_res_id], this.context]; - this._rpc({model: this.record.model, method: method, args: args}) + this.suggested_partners_def = new Promise(function (resolve, reject) { + var method = 'message_get_suggested_recipients'; + var args = [[self.context.default_res_id], self.context]; + self._rpc({model: self.record.model, method: method, args: args}) .then(function (result) { if (!self.suggested_partners_def) { return; // widget has been reset (e.g. we just switched to another record) @@ -555,8 +557,9 @@ var Chatter = Widget.extend({ reason: recipient[2], }); }); - self.suggested_partners_def.resolve(suggested_partners); + resolve(suggested_partners); }); + }); } this.suggested_partners_def.then(function (suggested_partners) { self._openComposer({ isLog: false, suggested_partners: suggested_partners }); diff --git a/addons/mail/static/src/js/composers/basic_composer.js b/addons/mail/static/src/js/composers/basic_composer.js index 9def912612c..72936152ae5 100644 --- a/addons/mail/static/src/js/composers/basic_composer.js +++ b/addons/mail/static/src/js/composers/basic_composer.js @@ -128,7 +128,7 @@ var BasicComposer = Widget.extend({ .on('update_typing_partners', this, this._onUpdateTypingPartners); // Mention - this._mentionManager.prependTo(this.$('.o_composer')); + var prependPromise = this._mentionManager.prependTo(this.$('.o_composer')); // Drag-Drop files // Allowing body to detect dragenter and dragleave for display @@ -137,7 +137,7 @@ var BasicComposer = Widget.extend({ $body.on('dragleave.' + this._dropZoneNS, this._onBodyFileDragLeave.bind(this)); $body.on("dragover." + this._dropZoneNS, this._onBodyFileDragover.bind(this)); $body.on("drop." + this._dropZoneNS, this._onBodyFileDrop.bind(this)); - return this._super(); + return Promise.all([this._super(), prependPromise]); }, destroy: function () { $("body").off('dragleave.' + this._dropZoneNS); @@ -236,7 +236,7 @@ var BasicComposer = Widget.extend({ * displayed to the user. If none of them match, then it will fetch for more * partner suggestions (@see _mentionFetchPartners). * - * @param {$.Deferred} prefetchedPartners list of list of + * @param {Promise} prefetchedPartners list of list of * prefetched partners. */ mentionSetPrefetchedPartners: function (prefetchedPartners) { @@ -299,20 +299,21 @@ var BasicComposer = Widget.extend({ /** * @private * @param {string} search - * @returns {$.Deferred} + * @returns {Promise} */ _mentionGetCannedResponses: function (search) { var self = this; - var def = $.Deferred(); - clearTimeout(this._cannedTimeout); - this._cannedTimeout = setTimeout(function () { - var cannedResponses = self.call('mail_service', 'getCannedResponses'); - var matches = fuzzy.filter(utils.unaccent(search), _.pluck(cannedResponses, 'source')); - var indexes = _.pluck(matches.slice(0, self.options.mentionFetchLimit), 'index'); - def.resolve(_.map(indexes, function (index) { - return cannedResponses[index]; - })); - }, 500); + var def = new Promise(function (resolve, reject) { + clearTimeout(self._cannedTimeout); + self._cannedTimeout = setTimeout(function () { + var cannedResponses = self.call('mail_service', 'getCannedResponses'); + var matches = fuzzy.filter(utils.unaccent(search), _.pluck(cannedResponses, 'source')); + var indexes = _.pluck(matches.slice(0, self.options.mentionFetchLimit), 'index'); + resolve(_.map(indexes, function (index) { + return cannedResponses[index]; + })); + }, 500); + }); return def; }, /** @@ -346,7 +347,7 @@ var BasicComposer = Widget.extend({ */ _mentionFetchPartners: function (search) { var self = this; - return $.when(this._mentionPrefetchedPartners).then(function (prefetchedPartners) { + return Promise.resolve(this._mentionPrefetchedPartners).then(function (prefetchedPartners) { // filter prefetched partners with the given search string var suggestions = []; var limit = self.options.mentionFetchLimit; @@ -371,7 +372,7 @@ var BasicComposer = Widget.extend({ { limit: limit, search: search } ); } - return $.when(suggestions).then(function (suggestions) { + return Promise.resolve(suggestions).then(function (suggestions) { //add im_status on suggestions _.each(suggestions, function (suggestionsSet) { _.each(suggestionsSet, function (suggestion) { @@ -387,27 +388,27 @@ var BasicComposer = Widget.extend({ * @param {string} model * @param {string} method * @param {Object} kwargs - * @return {$.Deferred} + * @return {Promise} */ _mentionFetchThrottled: function (model, method, kwargs) { var self = this; // Delays the execution of the RPC to prevent unnecessary RPCs when the user is still typing - var def = $.Deferred(); - clearTimeout(this.mentionFetchTimer); - this.mentionFetchTimer = setTimeout(function () { - return self._rpc({model: model, method: method, kwargs: kwargs}) + return new Promise(function (resolve, reject) { + clearTimeout(self.mentionFetchTimer); + self.mentionFetchTimer = setTimeout(function () { + return self._rpc({model: model, method: method, kwargs: kwargs}) .then(function (results) { - def.resolve(results); + resolve(results); }); - }, this.MENTION_THROTTLE); - return def; + }, self.MENTION_THROTTLE); + }); }, /** * @private - * @returns {$.Deferred} + * @returns {Promise} */ _preprocessMessage: function () { - // Return a deferred as this function is extended with asynchronous + // Return a promise as this function is extended with asynchronous // behavior for the chatter composer //Removing unwanted extra spaces from message @@ -420,7 +421,7 @@ var BasicComposer = Widget.extend({ var commands = this.options.commandsEnabled ? this._mentionManager.getListenerSelection('/') : []; - return $.when({ + return Promise.resolve({ content: this._mentionManager.generateLinks(value), attachment_ids: _.pluck(this.get('attachment_ids'), 'id'), partner_ids: _.uniq(_.pluck(this._mentionManager.getListenerSelection('@'), 'id')), diff --git a/addons/mail/static/src/js/composers/chatter_composer.js b/addons/mail/static/src/js/composers/chatter_composer.js index 777467e6eb8..8aaaf08dd6d 100644 --- a/addons/mail/static/src/js/composers/chatter_composer.js +++ b/addons/mail/static/src/js/composers/chatter_composer.js @@ -59,109 +59,112 @@ var ChatterComposer = BasicComposer.extend({ * @private * @param {Array} checkedSuggestedPartners list of 'recipient' partners to * complete informations or validate - * @returns {Deferred} resolved with the list of checked suggested partners + * @returns {Promise} resolved with the list of checked suggested partners * (real partner) **/ _checkSuggestedPartners: function (checkedSuggestedPartners) { var self = this; - var checkDone = $.Deferred(); - - var recipients = _.filter(checkedSuggestedPartners, function (recipient) { - return recipient.checked; - }); - var recipientsToFind = _.filter(recipients, function (recipient) { - return (! recipient.partner_id); - }); - var namesToFind = _.pluck(recipientsToFind, 'full_name'); - var recipientsToCheck = _.filter(recipients, function (recipient) { - return (recipient.partner_id && ! recipient.email_address); - }); - var recipientIDs = _.pluck(_.filter(recipients, function (recipient) { - return recipient.partner_id && recipient.email_address; - }), 'partner_id'); - - var namesToRemove = []; - var recipientIDsToRemove = []; - - // have unknown names - // -> call message_get_partner_info_from_emails to try to find - // partner_id - var def; - if (namesToFind.length > 0) { - def = this._rpc({ - model: this._model, - method: 'message_partner_info_from_emails', - args: [[this.context.default_res_id], namesToFind], - }); - } - - // for unknown names + incomplete partners - // -> open popup - cancel = remove from recipients - $.when(def).pipe(function (result) { - result = result || []; - var emailDefs = []; - var recipientPopups = result.concat(recipientsToCheck); - - _.each(recipientPopups, function (partnerInfo) { - var deferred = $.Deferred(); - emailDefs.push(deferred); - - var partnerName = partnerInfo.full_name; - var partnerID = partnerInfo.partner_id; - var parsedEmail = mailUtils.parseEmail(partnerName); - - var dialog = new viewDialogs.FormViewDialog(self, { - res_model: 'res.partner', - res_id: partnerID, - context: { - active_model: self._model, - active_id: self.context.default_res_id, - force_email: true, - ref: 'compound_context', - default_name: parsedEmail[0], - default_email: parsedEmail[1], - }, - title: _t("Please complete customer's informations"), - disable_multiple_selection: true, - }).open(); - dialog.on('closed', self, function () { - deferred.resolve(); - }); - dialog.opened().then(function () { - dialog.form_view.on('on_button_cancel', self, function () { - namesToRemove.push(partnerName); - if (partnerID) { - recipientIDsToRemove.push(partnerID); - } - }); - }); + var checkDone = new Promise(function (resolve, reject) { + var recipients = _.filter(checkedSuggestedPartners, function (recipient) { + return recipient.checked; }); - $.when.apply($, emailDefs).then(function () { - var newNamesToFind = _.difference(namesToFind, namesToRemove); - var def; - if (newNamesToFind.length > 0) { - def = self._rpc({ - model: self._model, - method: 'message_partner_info_from_emails', - args: [[self.context.default_res_id], newNamesToFind, true], + var recipientsToFind = _.filter(recipients, function (recipient) { + return (! recipient.partner_id); + }); + var namesToFind = _.pluck(recipientsToFind, 'full_name'); + var recipientsToCheck = _.filter(recipients, function (recipient) { + return (recipient.partner_id && ! recipient.email_address); + }); + var recipientIDs = _.pluck(_.filter(recipients, function (recipient) { + return recipient.partner_id && recipient.email_address; + }), 'partner_id'); + + var namesToRemove = []; + var recipientIDsToRemove = []; + + // have unknown names + // -> call message_get_partner_info_from_emails to try to find + // partner_id + var def; + if (namesToFind.length > 0) { + def = self._rpc({ + model: self._model, + method: 'message_partner_info_from_emails', + args: [[self.context.default_res_id], namesToFind], + }); + } + + // for unknown names + incomplete partners + // -> open popup - cancel = remove from recipients + Promise.resolve(def).then(function (result) { + result = result || []; + var emailDefs = []; + var recipientPopups = result.concat(recipientsToCheck); + + _.each(recipientPopups, function (partnerInfo) { + var prom = new Promise(function(innerResolve, innerReject) { + var partnerName = partnerInfo.full_name; + var partnerID = partnerInfo.partner_id; + var parsedEmail = mailUtils.parseEmail(partnerName); + + var dialog = new viewDialogs.FormViewDialog(self, { + res_model: 'res.partner', + res_id: partnerID, + context: { + active_model: self._model, + active_id: self.context.default_res_id, + force_email: true, + ref: 'compound_context', + default_name: parsedEmail[0], + default_email: parsedEmail[1], + }, + title: _t("Please complete customer's informations"), + disable_multiple_selection: true, + }).open(); + dialog.on('closed', self, function () { + innerResolve(); + }); + dialog.opened().then(function () { + dialog.form_view.on('on_button_cancel', self, function () { + namesToRemove.push(partnerName); + if (partnerID) { + recipientIDsToRemove.push(partnerID); + } + }); }); - } - $.when(def).pipe(function (result) { - result = result || []; - var recipientPopups = result.concat(recipientsToCheck); - _.each(recipientPopups, function (partnerInfo) { - if ( - partnerInfo.partner_id && - _.indexOf(partnerInfo.partner_id, recipientIDsToRemove) === -1 - ) { - recipientIDs.push(partnerInfo.partner_id); - } }); - }).pipe(function () { - checkDone.resolve(recipientIDs); + + emailDefs.push(prom); + + }); + Promise.all(emailDefs).then(function () { + var newNamesToFind = _.difference(namesToFind, namesToRemove); + var def; + if (newNamesToFind.length > 0) { + def = self._rpc({ + model: self._model, + method: 'message_partner_info_from_emails', + args: [[self.context.default_res_id], newNamesToFind, true], + }); + } + Promise.resolve(def).then(function (result) { + result = result || []; + var recipientPopups = result.concat(recipientsToCheck); + _.each(recipientPopups, function (partnerInfo) { + if ( + partnerInfo.partner_id && + _.indexOf(partnerInfo.partner_id, recipientIDsToRemove) === -1 + ) { + recipientIDs.push(partnerInfo.partner_id); + } + }); + }).then(function () { + resolve(recipientIDs); + }); }); }); }); + return checkDone; }, /** @@ -187,41 +190,40 @@ var ChatterComposer = BasicComposer.extend({ /** * @override * @private - * @returns {$.Deferred} + * @returns {Promise} */ _preprocessMessage: function () { var self = this; - var def = $.Deferred(); - this._super().then(function (message) { - message = _.extend(message, { - subtype: 'mail.mt_comment', - message_type: 'comment', - context: _.defaults({}, self.context, session.user_context), - }); - - // Subtype - if (self.options.isLog) { - message.subtype = 'mail.mt_note'; - } - - // Partner_ids - if (!self.options.isLog) { - var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); - self._checkSuggestedPartners(checkedSuggestedPartners).done(function (partnerIDs) { - message.partner_ids = (message.partner_ids || []).concat(partnerIDs); - // update context - message.context = _.defaults({}, message.context, { - mail_post_autofollow: true, - }); - def.resolve(message); + return new Promise(function (resolve, reject) { + self._super().then(function (message) { + message = _.extend(message, { + subtype: 'mail.mt_comment', + message_type: 'comment', + context: _.defaults({}, self.context, session.user_context), }); - } else { - def.resolve(message); - } + // Subtype + if (self.options.isLog) { + message.subtype = 'mail.mt_note'; + } + + // Partner_ids + if (!self.options.isLog) { + var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); + self._checkSuggestedPartners(checkedSuggestedPartners).then(function (partnerIDs) { + message.partner_ids = (message.partner_ids || []).concat(partnerIDs); + // update context + message.context = _.defaults({}, message.context, { + mail_post_autofollow: true, + }); + resolve(message); + }); + } else { + resolve(message); + } + + }); }); - - return def; }, /** * @override @@ -244,20 +246,20 @@ var ChatterComposer = BasicComposer.extend({ } var self = this; - var recipientDoneDef = $.Deferred(); - - // any operation on the full-composer will reload the record, so - // warn the user that any unsaved changes on the record will be lost. - this.trigger_up('discard_record_changes', { - proceed: function () { - if (self.options.isLog) { - recipientDoneDef.resolve([]); - } else { - var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); - self._checkSuggestedPartners(checkedSuggestedPartners) - .then(recipientDoneDef.resolve.bind(recipientDoneDef)); - } - }, + var recipientDoneDef = new Promise(function (resolve, reject) { + // any operation on the full-composer will reload the record, so + // warn the user that any unsaved changes on the record will be lost. + self.trigger_up('discard_record_changes', { + proceed: function () { + if (self.options.isLog) { + resolve([]); + } else { + var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); + self._checkSuggestedPartners(checkedSuggestedPartners) + .then(resolve); + } + }, + }); }); recipientDoneDef.then(function (partnerIDs) { diff --git a/addons/mail/static/src/js/composers/extended_composer.js b/addons/mail/static/src/js/composers/extended_composer.js index 917b71fa880..1acfba1058c 100644 --- a/addons/mail/static/src/js/composers/extended_composer.js +++ b/addons/mail/static/src/js/composers/extended_composer.js @@ -102,7 +102,7 @@ var ExtendedComposer = BasicComposer.extend({ /** * @override * @private - * @returns {$.Deferred} + * @returns {Promise} */ _preprocessMessage: function () { var self = this; diff --git a/addons/mail/static/src/js/composers/mention_manager.js b/addons/mail/static/src/js/composers/mention_manager.js index 6cbaab5002a..7ad8a36b39a 100644 --- a/addons/mail/static/src/js/composers/mention_manager.js +++ b/addons/mail/static/src/js/composers/mention_manager.js @@ -114,7 +114,7 @@ var MentionManager = Widget.extend({ if (this._activeListener) { var mentionWord = this._mentionWord; - $.when(this._activeListener.fetchCallback(mentionWord)) + Promise.resolve(this._activeListener.fetchCallback(mentionWord)) .then(function (suggestions) { if (mentionWord === self._mentionWord) { // update suggestions only if mentionWord didn't change diff --git a/addons/mail/static/src/js/discuss.js b/addons/mail/static/src/js/discuss.js index 0e3b66dd720..4e88434f86d 100644 --- a/addons/mail/static/src/js/discuss.js +++ b/addons/mail/static/src/js/discuss.js @@ -82,7 +82,7 @@ var PartnerInviteDialog = Dialog.extend({ /** * @private - * @returns {$.Promise} + * @returns {Promise} */ _addChannel: function () { var self = this; @@ -297,7 +297,7 @@ var Discuss = AbstractAction.extend({ * @override */ willStart: function () { - return $.when(this._super(), this.call('mail_service', 'isReady')); + return Promise.all([this._super(), this.call('mail_service', 'isReady')]); }, /** * @override @@ -331,7 +331,7 @@ var Discuss = AbstractAction.extend({ this._extendedComposer.appendTo(this.$('.o_mail_discuss_content'))); defs.push(this._super.apply(this, arguments)); - return $.when.apply($, defs) + return Promise.all(defs) .then(function () { return self._setThread(self._defaultThreadID); }) @@ -474,7 +474,7 @@ var Discuss = AbstractAction.extend({ }, /** * @private - * @returns {$.Promise} + * @returns {Promise} */ _fetchAndRenderThread: function () { var self = this; @@ -567,7 +567,7 @@ var Discuss = AbstractAction.extend({ * loaded when scrolling to the top, so they can't be loaded if there is no * scrollbar) * - * @returns {Deferred} resolved when there are enough messages to fill the + * @returns {Promise} resolved when there are enough messages to fill the * screen, or when there is no more message to fetch */ _loadEnoughMessages: function () { @@ -583,7 +583,7 @@ var Discuss = AbstractAction.extend({ * Load more messages for the current thread * * @private - * @returns {$.Promise} + * @returns {Promise} */ _loadMoreMessages: function () { var self = this; @@ -647,7 +647,7 @@ var Discuss = AbstractAction.extend({ autoFocus: true, source: function (request, response) { self._lastSearchVal = _.escape(request.term); - self._searchChannel(self._lastSearchVal).done(function (result){ + self._searchChannel(self._lastSearchVal).then(function (result){ result.push({ label: _.str.sprintf( '' + _t("Create %s (Public)") + '', @@ -689,7 +689,7 @@ var Discuss = AbstractAction.extend({ autoFocus: true, source: function (request, response) { self._lastSearchVal = _.escape(request.term); - self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).done(response); + self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).then(response); }, select: function (ev, ui) { var partnerID = ui.item.id; @@ -819,7 +819,7 @@ var Discuss = AbstractAction.extend({ * Renders, binds events and appends a thread widget. * * @private - * @returns {Deferred} + * @returns {Promise} */ _renderThread: function () { this._threadWidget = new ThreadWidget(this, { @@ -877,7 +877,7 @@ var Discuss = AbstractAction.extend({ /** * @private * @param {string} searchVal - * @returns {$.Promise} + * @returns {Promise} */ _searchChannel: function (searchVal){ return this._rpc({ @@ -915,7 +915,7 @@ var Discuss = AbstractAction.extend({ this._extendedComposer.do_show(); this._threadWidget.scrollToMessage({ - msgID: messageID, + messageID: messageID, duration: 200, onlyIfNecessary: true }); @@ -927,7 +927,7 @@ var Discuss = AbstractAction.extend({ * * @private * @param {integer} threadID a thread with such ID - * @returns {$.Promise} + * @returns {Promise} */ _setThread: function (threadID) { var self = this; @@ -1483,9 +1483,6 @@ var Discuss = AbstractAction.extend({ } else { self._threadWidget.scrollToBottom(); } - }) - .fail(function () { - // todo: display notifications }); }, /** diff --git a/addons/mail/static/src/js/discuss_mobile.js b/addons/mail/static/src/js/discuss_mobile.js index 549e05b78fd..617700c20c1 100644 --- a/addons/mail/static/src/js/discuss_mobile.js +++ b/addons/mail/static/src/js/discuss_mobile.js @@ -116,7 +116,7 @@ Discuss.include({ this._thread = thread; if (thread.getType() !== 'mailbox') { this.call('mail_service', 'openThreadWindow', threadID); - return $.when(); + return Promise.resolve(); } else { return this._super.apply(this, arguments); } @@ -179,7 +179,7 @@ Discuss.include({ }); def = this.call('mail_service', 'getChannelPreviews', channels); } - return $.when(def).then(function (previews) { + return Promise.resolve(def).then(function (previews) { // update content if (inMailbox) { if (!previouslyInInbox) { diff --git a/addons/mail/static/src/js/document_viewer.js b/addons/mail/static/src/js/document_viewer.js index 9affc3f41cf..77f33d152c0 100644 --- a/addons/mail/static/src/js/document_viewer.js +++ b/addons/mail/static/src/js/document_viewer.js @@ -76,7 +76,7 @@ var DocumentViewer = Widget.extend({ start: function () { this.$el.modal('show'); this.$el.on('hidden.bs.modal', _.bind(this._onDestroy, this)); - this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this)); + this.$('.o_viewer_img').on("load", _.bind(this._onImageLoaded, this)); this.$('[data-toggle="tooltip"]').tooltip({delay: 0}); return this._super.apply(this, arguments); }, @@ -132,7 +132,7 @@ var DocumentViewer = Widget.extend({ this.$('.o_viewer_content').html(QWeb.render('DocumentViewer.Content', { widget: this })); - this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this)); + this.$('.o_viewer_img').on("load", _.bind(this._onImageLoaded, this)); this.$('[data-toggle="tooltip"]').tooltip({delay: 0}); this._reset(); }, diff --git a/addons/mail/static/src/js/followers.js b/addons/mail/static/src/js/followers.js index 0a760516607..a8475f43219 100644 --- a/addons/mail/static/src/js/followers.js +++ b/addons/mail/static/src/js/followers.js @@ -55,14 +55,14 @@ var Followers = AbstractField.extend({ // note: the rendering of this widget is asynchronous as it needs to // fetch the details of the followers, but it performs a first rendering // synchronously (_displayGeneric), and updates its rendering once it - // has fetched the required data, so this function doesn't return a deferred + // has fetched the required data, so this function doesn't return a promise // as we don't want to wait to the data to be loaded to display the widget var self = this; var fetch_def = this.dp.add(this._readFollowers()); concurrency.rejectAfter(concurrency.delay(0), fetch_def).then(this._displayGeneric.bind(this)); - fetch_def.then(function () { + return fetch_def.then(function () { self._displayButtons(); self._displayFollowers(self.followers); if (self.subtypes) { // current user is follower @@ -203,7 +203,7 @@ var Followers = AbstractField.extend({ params: { follower_ids: missing_ids, res_model: this.model } }); } - return $.when(def).then(function (results) { + return Promise.resolve(def).then(function (results) { if (results) { self.followers = _.uniq(results.followers.concat(self.followers), 'id'); if (results.subtypes) { //read_followers will return False if current user is not in the list @@ -241,27 +241,27 @@ var Followers = AbstractField.extend({ */ _unfollow: function (ids) { var self = this; - var def = $.Deferred(); - var text = _t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document.\n Do you really want to remove this follower ?"); - Dialog.confirm(this, text, { - confirm_callback: function () { - var args = [ - [self.res_id], - ids.partner_ids, - ids.channel_ids, - {}, // FIXME - ]; - self._rpc({ + return new Promise(function (resolve, reject) { + var text = _t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document.\n Do you really want to remove this follower ?"); + Dialog.confirm(this, text, { + confirm_callback: function () { + var args = [ + [self.res_id], + ids.partner_ids, + ids.channel_ids, + {}, // FIXME + ]; + self._rpc({ model: self.model, method: 'message_unsubscribe', args: args }) .then(self._reload.bind(self)); - def.resolve(); - }, - cancel_callback: def.reject.bind(def), + resolve(); + }, + cancel_callback: reject, + }); }); - return def; }, _updateSubscription: function (event, followerID, isChannel) { var ids = {}; @@ -290,7 +290,7 @@ var Followers = AbstractField.extend({ // If no more subtype followed, unsubscribe the follower if (!checklist.length) { - this._unfollow(ids).fail(function () { + this._unfollow(ids).guardedCatch(function () { $(event.currentTarget).find('input').addBack('input').prop('checked', true); }); } else { diff --git a/addons/mail/static/src/js/form_renderer.js b/addons/mail/static/src/js/form_renderer.js index 91d2c2033c4..e7b767c0b2f 100644 --- a/addons/mail/static/src/js/form_renderer.js +++ b/addons/mail/static/src/js/form_renderer.js @@ -50,18 +50,24 @@ FormRenderer.include({ * @private */ _renderNode: function (node) { + var self = this; if (node.tag === 'div' && node.attrs.class === 'oe_chatter') { if (!this.chatter) { this.chatter = new Chatter(this, this.state, this.mailFields, { isEditable: this.activeActions.edit, viewType: 'form', }); - this.chatter.appendTo($('
')); - this._handleAttributes(this.chatter.$el, node); + + var $temporaryParentDiv = $('
'); + this.defs.push(this.chatter.appendTo($temporaryParentDiv).then(function () { + self.chatter.$el.unwrap(); + self._handleAttributes(self.chatter.$el, node); + })); + return $temporaryParentDiv; } else { this.chatter.update(this.state); + return this.chatter.$el; } - return this.chatter.$el; } else { return this._super.apply(this, arguments); } diff --git a/addons/mail/static/src/js/many2many_tags_email.js b/addons/mail/static/src/js/many2many_tags_email.js index e66412a7a2c..6648ef50423 100644 --- a/addons/mail/static/src/js/many2many_tags_email.js +++ b/addons/mail/static/src/js/many2many_tags_email.js @@ -20,7 +20,7 @@ BasicModel.include({ * @private * @param {Object} record - an element from the localData * @param {string} fieldName - * @return {Deferred} the deferred is resolved with the + * @return {Promise} the promise is resolved with the * invalidPartnerIds */ _setInvalidMany2ManyTagsEmail: function (record, fieldName) { @@ -42,10 +42,10 @@ BasicModel.include({ var changes = {operation: 'DELETE', ids: _.pluck(invalidPartnerIds, 'id')}; def = this._applyX2ManyChange(record, fieldName, changes); } - return $.when(def).then(function () { - return $.when({ + return Promise.resolve(def).then(function () { + return { invalidPartnerIds: _.pluck(invalidPartnerIds, 'res_id'), - }); + }; }); }, }); @@ -65,7 +65,7 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({ * Open a popup for each invalid partners (without email) to fill the email. * * @private - * @returns {Deferred} + * @returns {Promise} */ _checkEmailPopup: function () { var self = this; @@ -75,25 +75,25 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({ // propose the user to correct invalid partners _.each(this.record.specialData[this.name].invalidPartnerIds, function (resID) { - var def = $.Deferred(); - popupDefs.push(def); - - var pop = new form_common.FormViewDialog(self, { - res_model: self.field.relation, - res_id: resID, - context: self.record.context, - title: "", - on_saved: function (record) { - if (record.data.email) { - validPartners.push(record.res_id); - } - }, - }).open(); - pop.on('closed', self, function () { - def.resolve(); + var def = new Promise(function (resolve, reject) { + var pop = new form_common.FormViewDialog(self, { + res_model: self.field.relation, + res_id: resID, + context: self.record.context, + title: "", + on_saved: function (record) { + if (record.data.email) { + validPartners.push(record.res_id); + } + }, + }).open(); + pop.on('closed', self, function () { + resolve(); + }); }); + popupDefs.push(def); }); - return $.when.apply($, popupDefs).then(function() { + return Promise.all(popupDefs).then(function() { // All popups have been processed for the given ids // It is now time to set the final value with valid partners ids. validPartners = _.uniq(validPartners); @@ -117,14 +117,14 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({ */ _render: function () { var self = this; - var def = $.Deferred(); var _super = this._super.bind(this); - if (this.record.specialData[this.name].invalidPartnerIds.length) { - def = this._checkEmailPopup(); - } else { - def.resolve(); - } - return def.then(function () { + return new Promise(function (resolve, reject) { + if (self.record.specialData[self.name].invalidPartnerIds.length) { + resolve(self._checkEmailPopup()); + } else { + resolve(); + } + }).then(function () { return _super.apply(self, arguments); }); }, diff --git a/addons/mail/static/src/js/models/messages/message.js b/addons/mail/static/src/js/models/messages/message.js index a414a11874b..77e4120da29 100644 --- a/addons/mail/static/src/js/models/messages/message.js +++ b/addons/mail/static/src/js/models/messages/message.js @@ -511,7 +511,7 @@ var Message = AbstractMessage.extend(Mixins.EventDispatcherMixin, ServicesMixin * @see {mail.Manager.Notification} for the receipt of 'toggle_star' * notification after this rpc. * - * @return {$.Promise} + * @return {Promise} */ toggleStarStatus: function () { return this._rpc({ diff --git a/addons/mail/static/src/js/models/threads/abstract_thread.js b/addons/mail/static/src/js/models/threads/abstract_thread.js index a515c4d2f8a..192d0e58c93 100644 --- a/addons/mail/static/src/js/models/threads/abstract_thread.js +++ b/addons/mail/static/src/js/models/threads/abstract_thread.js @@ -150,18 +150,18 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, { * Mark the thread as read, which resets the unread counter to 0. This is * only performed if the unread counter is not 0. * - * @returns {$.Promise} + * @returns {Promise} */ markAsRead: function () { if (this._unreadCounter > 0) { return this._markAsRead(); } - return $.when(); + return Promise.resolve(); }, /** * Post a message on this thread * - * @returns {$.Promise} resolved with the message object to be sent to the + * @returns {Promise} resolved with the message object to be sent to the * server */ postMessage: function () { @@ -200,22 +200,22 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, { * Mark the thread as read * * @private - * @returns {$.Promise} + * @returns {Promise} */ _markAsRead: function () { this.resetUnreadCounter(); - return $.when(); + return Promise.resolve(); }, /** * Post a message on this thread * * @abstract * @private - * @returns {$.Promise} resolved with the message object to be sent to the + * @returns {Promise} resolved with the message object to be sent to the * server */ _postMessage: function () { - return $.when(); + return Promise.resolve(); }, /** * Warn views (e.g. discuss app, thread window, etc.) to update visually diff --git a/addons/mail/static/src/js/models/threads/channel.js b/addons/mail/static/src/js/models/threads/channel.js index 9f0f37264db..9e9f3ac6a39 100644 --- a/addons/mail/static/src/js/models/threads/channel.js +++ b/addons/mail/static/src/js/models/threads/channel.js @@ -70,7 +70,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { this._isMyselfModerator = data.is_moderator; this._lastMessageDate = undefined; this._members = data.members || []; - // Deferred that is resolved on fetched members of this channel. + // Promise that is resolved on fetched members of this channel. this._membersDef = undefined; // number of messages that are 'needaction', which is equivalent to the // number of messages in this channel that are in inbox. @@ -129,19 +129,21 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * @pverride */ detach: function () { - this._super.apply(this, arguments); - this._rpc({ - model: 'mail.channel', - method: 'channel_minimize', - args: [this.getUUID(), true], - }, { - shadow: true, + var self = this; + return this._super.apply(this, arguments).then(function () { + self._rpc({ + model: 'mail.channel', + method: 'channel_minimize', + args: [self.getUUID(), true], + }, { + shadow: true, + }); }); }, /** * Force fetch members of the channel * - * @returns {$.Promise} resolved with list of channel listeners + * @returns {Promise} resolved with list of channel listeners */ forceFetchMembers: function () { this._membersDef = undefined; @@ -186,7 +188,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { /** * Get listeners of a channel * - * @returns {$.Promise>} resolved with list of list of + * @returns {Promise} resolved with list of list of * channel listeners. */ getMentionPartnerSuggestions: function () { @@ -302,7 +304,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * Unsubscribes from channel * * @abstract - * @returns {$.Promise} resolve when unsubscribed + * @returns {Promise} resolve when unsubscribed */ unsubscribe: function () {}, /** @@ -381,19 +383,19 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * * @override * @private - * @returns {$.Promise} resolved when message has been marked as read + * @returns {Promise} resolved when message has been marked as read */ _markAsRead: function () { var superDef = this._super.apply(this, arguments); var seenDef = this._notifySeen(); - return $.when(superDef, seenDef); + return Promise.all([superDef, seenDef]); }, /** * @override {mail.model.ThreadTypingMixin} * @private * @param {Object} params * @param {boolean} params.typing - * @returns {$.Promise} + * @returns {Promise} */ _notifyMyselfTyping: function (params) { return this._rpc({ @@ -424,7 +426,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * @override * @private * @param {Object} data data related to the new message - * @returns {$.Promise} resolved when the message has been sent to + * @returns {Promise} resolved when the message has been sent to * the server, with the object message that has been sent to the server. */ _postMessage: function (data) { diff --git a/addons/mail/static/src/js/models/threads/document_thread.js b/addons/mail/static/src/js/models/threads/document_thread.js index 6c15b39d25e..08e441610ee 100644 --- a/addons/mail/static/src/js/models/threads/document_thread.js +++ b/addons/mail/static/src/js/models/threads/document_thread.js @@ -92,7 +92,7 @@ var DocumentThread = Thread.extend({ * @param {Object} options * @param {boolean} [options.forceFetch] if true, fetch anyway, as user * clicked on 'load more'. - * @returns {$.Promise} + * @returns {Promise} */ fetchMessages: function (options) { var self = this; @@ -217,14 +217,14 @@ var DocumentThread = Thread.extend({ * Get most up to date messageIDs * * @private - * @returns {$.Promise} resolved when message IDs have been fetched and set + * @returns {Promise} resolved when message IDs have been fetched and set * in the model */ _fetchMessageIDs: function () { var self = this; var resID = this.getDocumentID(); if (!resID || !this._mustFetchMessageIDs) { - return $.when(); + return Promise.resolve(); } return this._rpc({ model: this.getDocumentModel(), @@ -239,7 +239,7 @@ var DocumentThread = Thread.extend({ * @private * @param {Object} options * @param {boolean} [options.forceFetch] - * @returns {$.Promise} resolved when messages have been fetched + document + * @returns {Promise} resolved when messages have been fetched + document * thread has updated messages */ _fetchMessages: function (options) { @@ -273,7 +273,7 @@ var DocumentThread = Thread.extend({ }); }); } else { - return $.when(); + return Promise.resolve(); } }); @@ -284,7 +284,7 @@ var DocumentThread = Thread.extend({ * * @override * @private - * @returns {$.Promise} resolved when messages have been marked as read on + * @returns {Promise} resolved when messages have been marked as read on * the server. */ _markAsRead: function () { @@ -299,7 +299,7 @@ var DocumentThread = Thread.extend({ * @override * @private * @param {Object} data data related to the new message - * @return {$.Promise} resolved when the message has been sent to + * @return {Promise} resolved when the message has been sent to * the server, with the object message that has been sent to the server. */ _postMessage: function (data) { diff --git a/addons/mail/static/src/js/models/threads/livechat.js b/addons/mail/static/src/js/models/threads/livechat.js index 45cab5aef10..c3b4a5e2b4d 100644 --- a/addons/mail/static/src/js/models/threads/livechat.js +++ b/addons/mail/static/src/js/models/threads/livechat.js @@ -34,7 +34,7 @@ var Livechat = TwoUserChannel.extend({ * display of a user that is typing. * * @override - * @returns {$.Promise>} resolved with list of list of + * @returns {Promise} resolved with list of list of * livechat members. */ getMentionPartnerSuggestions: function () { diff --git a/addons/mail/static/src/js/models/threads/mailbox.js b/addons/mail/static/src/js/models/threads/mailbox.js index 263072f575e..bedd6af27e1 100644 --- a/addons/mail/static/src/js/models/threads/mailbox.js +++ b/addons/mail/static/src/js/models/threads/mailbox.js @@ -59,7 +59,7 @@ var Mailbox = SearchableThread.extend({ * channel, only keep the lastest message of this document/channel in the * previews. * - * @returns {$.Promise} + * @returns {Promise} */ getMessagePreviews: function () { var self = this; @@ -119,7 +119,7 @@ var Mailbox = SearchableThread.extend({ * makes only sense for 'Inbox'. * * @param {Array} domain - * @return {$.Promise} resolved when all messages have been marked as read + * @return {Promise} resolved when all messages have been marked as read * on the server */ markAllMessagesAsRead: function (domain) { @@ -133,7 +133,7 @@ var Mailbox = SearchableThread.extend({ }, }); } - return $.when(); + return Promise.resolve(); }, //-------------------------------------------------------------------------- diff --git a/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js b/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js index f995b4e09b3..7d97538dbb9 100644 --- a/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js +++ b/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js @@ -239,11 +239,11 @@ var ThreadTypingMixin = { * @private * @param {Object} params * @param {boolean} params.typing whether we are typing something or not - * @returns {$.Promise} resolved if the server is notified, rejected + * @returns {Promise} resolved if the server is notified, rejected * otherwise */ _notifyMyselfTyping: function (params) { - return $.when(); + return Promise.resolve(); }, /** * Warn views that the list of users that are currently typing on this diff --git a/addons/mail/static/src/js/models/threads/multi_user_channel.js b/addons/mail/static/src/js/models/threads/multi_user_channel.js index bac417c361d..3dab9337502 100644 --- a/addons/mail/static/src/js/models/threads/multi_user_channel.js +++ b/addons/mail/static/src/js/models/threads/multi_user_channel.js @@ -56,7 +56,7 @@ var MultiUserChannel = Channel.extend({ * Unsubscribes from channel * * @override - * @returns {$.Promise} resolve when unsubscribed + * @returns {Promise} resolve when unsubscribed */ unsubscribe: function () { return this._rpc({ diff --git a/addons/mail/static/src/js/models/threads/searchable_thread.js b/addons/mail/static/src/js/models/threads/searchable_thread.js index 8ddff5457af..511bc6348c8 100644 --- a/addons/mail/static/src/js/models/threads/searchable_thread.js +++ b/addons/mail/static/src/js/models/threads/searchable_thread.js @@ -49,7 +49,7 @@ var SearchableThread = Thread.extend({ } var cache = this._getCache(domain); if (cache.loaded) { - return $.when(cache.messages); + return Promise.all(cache.messages); } else { return this._fetchMessages(domain); } @@ -159,7 +159,7 @@ var SearchableThread = Thread.extend({ * @private * @param {Array} [pDomain] filter on the messages of the channel * @param {boolean} [loadMore] Whether it should load more message - * @return {$.Promise} resolved with list of messages + * @return {Promise} resolved with list of messages */ _fetchMessages: function (pDomain, loadMore) { var self = this; diff --git a/addons/mail/static/src/js/models/threads/thread.js b/addons/mail/static/src/js/models/threads/thread.js index 8c622779bb3..8f4e8956d27 100644 --- a/addons/mail/static/src/js/models/threads/thread.js +++ b/addons/mail/static/src/js/models/threads/thread.js @@ -58,12 +58,13 @@ var Thread = AbstractThread.extend(ServicesMixin, { * of this thread. Otherwise unfold it while detaching it. * @param {boolean} [options.passively=false] if set, if the thread window * will be created passively. + * @returns {Promise} */ detach: function (options) { options = options || {}; this._detached = true; this._folded = options.keepFoldState ? this._folded : false; - this._warnUpdatedWindowState({ + return this._warnUpdatedWindowState({ passively: options.passively, }); }, @@ -79,10 +80,10 @@ var Thread = AbstractThread.extend(ServicesMixin, { * `this.LIMIT` number of messages at a time. * * @abstract - * @returns {$.Promise} + * @returns {Promise} */ fetchMessages: function () { - return $.when([]); + return Promise.resolve([]); }, /** * Updates the folded state of the thread. Must be overriden to reflect @@ -110,10 +111,10 @@ var Thread = AbstractThread.extend(ServicesMixin, { * By default, a thread has not listener. * * @abstract - * @returns {$.Promise>} + * @returns {Promise>} */ getMentionPartnerSuggestions: function () { - return $.when([]); + return Promise.resolve([]); }, /** * Returns the information required to render the preview of this channel. @@ -287,10 +288,10 @@ var Thread = AbstractThread.extend(ServicesMixin, { /** * @abstract * @private - * @return {$.Promise} + * @return {Promise} */ _fetchMessages: function () { - return $.when(); + return Promise.resolve(); }, /** * Replace character representations of emojis by their unicode @@ -330,7 +331,7 @@ var Thread = AbstractThread.extend(ServicesMixin, { * @abstract * @private * @param {Object} data - * @returns {$.Promise} resolved with the message object to be sent + * @returns {Promise} resolved with the message object to be sent * to the server */ _postMessage: function (data) { @@ -355,7 +356,7 @@ var Thread = AbstractThread.extend(ServicesMixin, { messageData.subject = data.subject; } return this._super.apply(this, arguments).then(function () { - return $.when(messageData); + return messageData; }); }, /** @@ -374,10 +375,11 @@ var Thread = AbstractThread.extend(ServicesMixin, { * @private * @param {Object} [options={}] * @param {boolean} [options.passively=false] + * @returns {Promise} */ _warnUpdatedWindowState: function (options) { options = options || {}; - this.call('mail_service', 'updateThreadWindow', this.getID(), options); + return this.call('mail_service', 'updateThreadWindow', this.getID(), options); }, }); diff --git a/addons/mail/static/src/js/models/threads/two_user_channel.js b/addons/mail/static/src/js/models/threads/two_user_channel.js index 204fd95eb82..54a4145bd13 100644 --- a/addons/mail/static/src/js/models/threads/two_user_channel.js +++ b/addons/mail/static/src/js/models/threads/two_user_channel.js @@ -15,7 +15,7 @@ var TwoUserChannel = Channel.extend({ * Unpin from two-user thread * * @override - * @returns {$.Promise} resolve when unsubscribed + * @returns {Promise} resolve when unsubscribed */ unsubscribe: function () { return this._rpc({ diff --git a/addons/mail/static/src/js/services/mail_document_thread_manager.js b/addons/mail/static/src/js/services/mail_document_thread_manager.js index a98ae317c9d..f1d9eefb44a 100644 --- a/addons/mail/static/src/js/services/mail_document_thread_manager.js +++ b/addons/mail/static/src/js/services/mail_document_thread_manager.js @@ -182,7 +182,7 @@ MailManager.include({ resModel: resModel, }); } - this._super.apply(this, arguments); + return this._super.apply(this, arguments); }, /** * Updates the thread windows related to document threads. diff --git a/addons/mail/static/src/js/services/mail_manager.js b/addons/mail/static/src/js/services/mail_manager.js index 5eb588d24e7..27a7183ec88 100644 --- a/addons/mail/static/src/js/services/mail_manager.js +++ b/addons/mail/static/src/js/services/mail_manager.js @@ -80,13 +80,14 @@ var MailManager = AbstractService.extend({ * the unread_counter of channel. * @param {boolean} [options.silent] whether it should inform in the mailBus * of the newly created message. - * @returns {mail.model.Message} message object + * @returns {Promise} */ addMessage: function (data, options) { options = options || {}; var message = this.getMessage(data.id); + var prom = Promise.resolve(); if (!message) { - message = this._addNewMessage(data, options); + prom = this._addNewMessage(data, options); } else { if (data.moderation_status === 'accepted') { message.setModerationStatus('accepted', { @@ -97,7 +98,7 @@ var MailManager = AbstractService.extend({ this._addMessageToThreads(message, options); } } - return message; + return prom; }, /** * Creates a channel, can be either a true channel or a DM chat based on @@ -105,7 +106,7 @@ var MailManager = AbstractService.extend({ * * @param {integer|string} name id of partner (in case of dm) or name * @param {string} type ['dm_chat', 'public', 'private'] - * @returns {$.Promise} resolved with ID of the newly created + * @returns {Promise} resolved with ID of the newly created * channel */ createChannel: function (name, type) { @@ -150,7 +151,7 @@ var MailManager = AbstractService.extend({ * Returns the content that will be shown in the mail navbar dropdown * * @param {mail.model.Channel[]} channels - * @returns {$.Promise} resolved with list of channel previews + * @returns {Promise} resolved with list of channel previews */ getChannelPreviews: function (channels) { var self = this; @@ -227,23 +228,22 @@ var MailManager = AbstractService.extend({ * * @param {string} [filter=undefined] specify 'chat' or 'channels' to only * get previews for that type of threads - * @returns {$.Promise} resolved with list of objects that have a + * @returns {Promise} resolved with list of objects that have a * valid format for rendering a messaging menu preview items. */ getSystrayPreviews: function (filter) { var self = this; - var defs = []; var channelDef = this._getSystrayChannelPreviews(filter); var inboxDef = this._getSystrayInboxPreviews(filter); var failureDef = this._getSystrayMailFailurePreviews(filter); - defs = defs.concat([channelDef, inboxDef, failureDef]); - return $.when.apply($, defs) - .then(function (previewsChannel, previewsInbox, previewsFailure) { + + return Promise.all([channelDef, inboxDef, failureDef]) + .then(function (result) { //previewsChannel, previewsInbox, previewsFailure // order: failures > inbox > channel, each group must be sorted - previewsChannel = self._sortPreviews(previewsChannel); - previewsInbox = self._sortPreviews(previewsInbox); - previewsFailure = self._sortPreviews(previewsFailure); + var previewsChannel = self._sortPreviews(result[0]); + var previewsInbox = self._sortPreviews(result[1]); + var previewsFailure = self._sortPreviews(result[2]); return _.union(previewsFailure, previewsInbox, previewsChannel); }); }, @@ -295,7 +295,7 @@ var MailManager = AbstractService.extend({ * This is the case when it has fetched the initial state from the server, * by means of the route 'mail/init_messaging' * - * @returns {$.Promise} + * @returns {Promise} */ isReady: function () { return this._isReady; @@ -306,24 +306,24 @@ var MailManager = AbstractService.extend({ * * @param {integer} channelID * @param {Object} [options] options to be passed on channel add - * @returns {$.Promise} resolved with channelID joined + * @returns {Promise} resolved with channelID joined */ joinChannel: function (channelID, options) { - var def; + var prom; var channel = this.getChannel(channelID); if (channel) { // channel already joined - def = $.when(channelID); + prom = Promise.resolve(channelID); } else { - def = this._joinAndAddChannel(channelID, options); + prom = this._joinAndAddChannel(channelID, options); } - return def; + return prom; }, /** * Mark messages as read * * @param {Array} messageIDs list of messages IDs - * @returns {$.Promise} resolved when messages have been marked as read on + * @returns {Promise} resolved when messages have been marked as read on * the server. */ markMessagesAsRead: function (messageIDs) { @@ -341,7 +341,7 @@ var MailManager = AbstractService.extend({ args: [ids], }); } else { - return $.when(); + return Promise.resolve(); } }, /** @@ -396,19 +396,19 @@ var MailManager = AbstractService.extend({ * * @param {string} searchVal * @param {integer} limit max number of found partners in the response - * @returns {$.Promise} list of found partners (matching + * @returns {Promise} list of found partners (matching * 'searchVal') */ searchPartner: function (searchVal, limit) { - var def = $.Deferred(); + var self = this; var partners = this._searchPartnerPrefetch(searchVal, limit); - - if (!partners.length) { - def = this._searchPartnerFetch(searchVal, limit); - } else { - def = $.when(partners); - } - return def.then(function (partners) { + return new Promise(function (resolve, reject) { + if (!partners.length) { + resolve(self._searchPartnerFetch(searchVal, limit)); + } else { + resolve(partners); + } + }).then(function (partners) { var suggestions = _.map(partners, function (partner) { return { id: partner.id, @@ -422,7 +422,7 @@ var MailManager = AbstractService.extend({ /** * Unstars all messages from all channels * - * @returns {$.Promise} + * @returns {Promise} */ unstarAll: function () { return this._rpc({ @@ -448,11 +448,13 @@ var MailManager = AbstractService.extend({ * @param {string} [data.state] e.g. 'open', 'folded' * @param {Object|integer} [options=undefined] * @param {boolean} [options.silent=false] - * @returns {integer} the ID of the newly or already existing channel + * @returns {Promise} resolves with the ID of the newly or already + * existing channel */ _addChannel: function (data, options) { options = typeof options === 'object' ? options : {}; var channel = this.getChannel(data.id); + var proms = []; if (!channel) { channel = this._makeChannel(data, options); if (channel.getType() === 'dm_chat') { @@ -462,14 +464,16 @@ var MailManager = AbstractService.extend({ if (data.last_message) { // channel_info in mobile, necessary for showing channel // preview in mobile - this.addMessage(data.last_message); + proms.push(this.addMessage(data.last_message)); } this._sortThreads(); if (!options.silent) { - this._mailBus.trigger('new_channel', channel); + this._mailBus.trigger('new_channel', channel, proms); } } - return channel.getID(); + return Promise.all(proms).then(function () { + return channel.getID(); + }); }, /** * Add a new mailbox @@ -516,21 +520,23 @@ var MailManager = AbstractService.extend({ * @private * @param {Object} data * @param {Object} options - * @returns {mail.model.Message} + * @return {Promise} */ _addNewMessage: function (data, options) { + var self = this; var message = this._makeMessage(data); // Keep the array ordered by ID when inserting the new message var index = _.sortedIndex(this._messages, message, function (msg) { return msg.getID(); }); this._messages.splice(index, 0, message); - this._addNewMessagePostprocessThread(message, options); - this._addMessageToThreads(message, options); - if (!options.silent) { - this._mailBus.trigger('new_message', message); - } - return message; + return this._addNewMessagePostprocessThread(message, options).then(function () { + self._addMessageToThreads(message, options); + if (!options.silent) { + self._mailBus.trigger('new_message', message); + } + return message; + }); }, /** * For newly added message, postprocess threads linked to this message @@ -541,9 +547,11 @@ var MailManager = AbstractService.extend({ * @param {Array} [options.domain] * @param {boolean} [options.incrementUnread] * @param {boolean} [options.showNotification] + * @returns {Promise} */ _addNewMessagePostprocessThread: function (message, options) { var self = this; + var proms = []; _.each(message.getThreadIDs(), function (threadID) { var thread = self.getThread(threadID); if (thread) { @@ -553,6 +561,7 @@ var MailManager = AbstractService.extend({ !message.isSystemNotification() ) { if (thread.isTwoUserThread() && options.showNotification) { + var prom = Promise.resolve(); if ( !self._isDiscussOpen() && !config.device.isMobile && @@ -560,17 +569,21 @@ var MailManager = AbstractService.extend({ ) { // automatically open thread window // while keeping it unread - thread.detach({ passively: true }); - } - var query = { isVisible: false }; - self._mailBus.trigger('is_thread_bottom_visible', thread, query); - if (!self.call('bus_service', 'isOdooFocused') || !query.isVisible) { - self._notifyIncomingMessage(message); + prom = thread.detach({ passively: true }); } + proms.push(prom); + prom.then(function () { + var query = { isVisible: false }; + self._mailBus.trigger('is_thread_bottom_visible', thread, query); + if (!self.call('bus_service', 'isOdooFocused') || !query.isVisible) { + self._notifyIncomingMessage(message); + } + }); } } } }); + return Promise.all(proms); }, /** * Create a Channel (other than a DM) @@ -578,7 +591,7 @@ var MailManager = AbstractService.extend({ * @private * @param {string} name * @param {string} type - * @returns {$.Promise} ID of the created channel + * @returns {Promise} ID of the created channel */ _createChannel: function (name, type) { var context = _.extend({ isMobile: config.device.isMobile }, session.user_context); @@ -595,7 +608,7 @@ var MailManager = AbstractService.extend({ * * @private * @param {string} name - * @returns {$.Promise} ID of the created channel + * @returns {Promise} ID of the created channel */ _createDMChat: function (name) { var context = _.extend({ isMobile: config.device.isMobile }, session.user_context); @@ -610,7 +623,7 @@ var MailManager = AbstractService.extend({ /** * @private * @param {integer[]} channelIDs - * @returns {$.Promise} resolved with list of channel preview + * @returns {Promise} resolved with list of channel preview */ _fetchChannelPreviews: function (channelIDs) { return this._rpc({ @@ -652,7 +665,7 @@ var MailManager = AbstractService.extend({ * * @private * @param {mail.model.Channel[]} channels - * @returns {$.Deferred} resolved with list of channel previews + * @returns {Promise} resolved with list of channel previews */ _getChannelPreviews: function (channels) { var self = this; @@ -667,19 +680,22 @@ var MailManager = AbstractService.extend({ }); fetchDef = this._fetchChannelPreviews(ids); } else { - fetchDef = $.when(); + fetchDef = Promise.resolve(); } return fetchDef.then(function (fetchedPreviews) { // add fetched messages + var proms = []; _.each(fetchedPreviews, function (fetchedPreview) { if (fetchedPreview.last_message) { - self.addMessage(fetchedPreview.last_message); + proms.push(self.addMessage(fetchedPreview.last_message)); } // mark the channel as previewed, so that we do not need to // fetch preview again var channel = self.getChannel(fetchedPreview.id); channel.markAsPreviewed(); }); + return Promise.all(proms); + }).then(function () { return _.map(channels, function (channel) { return channel.getPreview(); }); @@ -694,7 +710,7 @@ var MailManager = AbstractService.extend({ * the documentID is omitted for a model preview, whereas it is set for a * preview of a failure related to a document. * - * @returns {$.Promise} resolved with previews of mail failures + * @returns {Promise} resolved with previews of mail failures */ _getMailFailurePreviews: function () { // items = list of objects: @@ -754,7 +770,7 @@ var MailManager = AbstractService.extend({ * @private * @param {string|undefined} [filter] the filter to apply on channel * previews in systray messaging menu - * @returns {$.Promise} resolved with object valid for template + * @returns {Promise} resolved with object valid for template * mail.Preview on channel previews, based on selected filter */ _getSystrayChannelPreviews: function (filter) { @@ -796,7 +812,7 @@ var MailManager = AbstractService.extend({ * * @private * @param {string|undefined} [filter] - * @returns {$.Deferred} resolved with valid objects for template + * @returns {Promise} resolved with valid objects for template * mail.Preview on inbox message previews */ _getSystrayInboxPreviews: function (filter) { @@ -806,7 +822,7 @@ var MailManager = AbstractService.extend({ var inbox = this.getMailbox('inbox'); return inbox.getMessagePreviews(); } else { - return $.when([]); + return Promise.resolve([]); } }, /** @@ -820,7 +836,7 @@ var MailManager = AbstractService.extend({ if (filter === 'mailbox_inbox' || !filter) { return this._getMailFailurePreviews(); } else { - return $.when([]); + return Promise.resolve([]); } }, /** @@ -863,7 +879,7 @@ var MailManager = AbstractService.extend({ * @private * @param {integer|string} channelID * @param {Object} options options passed on channel add - * @returns {$.Promise} channelID of joined and added channel + * @returns {Promise} channelID of joined and added channel */ _joinAndAddChannel: function (channelID, options) { var self = this; @@ -941,7 +957,7 @@ var MailManager = AbstractService.extend({ this._toNotifyChannelFetchedIDs = []; if (_.isEmpty(channelIDs)) { // no channel to notify fetched - return $.when(); + return Promise.resolve(); } return this._rpc({ model: 'mail.channel', @@ -1098,7 +1114,7 @@ var MailManager = AbstractService.extend({ * @private * @param {string} searchVal * @param {integer} limit - * @returns {$.Promise} fetched partners matching 'searchVal' + * @returns {Promise} fetched partners matching 'searchVal' */ _searchPartnerFetch: function (searchVal, limit) { return this._rpc({ @@ -1194,12 +1210,17 @@ var MailManager = AbstractService.extend({ * (e.g. list of public channel data are stored in 'channel_channel') * @param {Object[]} [data.channel_slots[i] list of data of channel of type * `i` + * @returns {Promise} */ _updateChannelsFromServer: function (data) { var self = this; + var proms = []; _.each(data.channel_slots, function (channels) { - _.each(channels, self._addChannel.bind(self)); + _.each(channels, function (channel) { + proms.push(self._addChannel(channel)); + }); }); + return Promise.all(proms); }, /** * Update commands from mail data fetched from the server @@ -1221,11 +1242,12 @@ var MailManager = AbstractService.extend({ * @param {Array} result.mention_partner_suggestions list of * suggestions. * @param {integer} result.menu_id the menu ID of discuss app + * @returns {Promise} */ _updateInternalStateFromServer: function (result) { // commands are needed for channel instantiation this._updateCommandsFromServer(result); - this._updateChannelsFromServer(result); + var prom = this._updateChannelsFromServer(result); this._updateModerationSettingsFromServer(result); this._updateMailboxesFromServer(result); this._updateMailFailuresFromServer(result); @@ -1233,6 +1255,8 @@ var MailManager = AbstractService.extend({ this._mentionPartnerSuggestions = result.mention_partner_suggestions; this._discussMenuID = result.menu_id; + + return prom; }, /** * Update the mailboxes with mail data fetched from server, namely 'Inbox', diff --git a/addons/mail/static/src/js/services/mail_notification_manager.js b/addons/mail/static/src/js/services/mail_notification_manager.js index fb3527b2b90..75754f375d8 100644 --- a/addons/mail/static/src/js/services/mail_notification_manager.js +++ b/addons/mail/static/src/js/services/mail_notification_manager.js @@ -101,12 +101,12 @@ MailManager.include({ channelAlreadyInCache = !!this.getChannel(messageData.channel_ids[0]); def = this.joinChannel(messageData.channel_ids[0], { autoswitch: false }); } else { - def = $.when(); + def = Promise.resolve(); } def.then(function () { // don't increment unread if channel wasn't in cache yet as // its unread counter has just been fetched - self.addMessage(messageData, { + return self.addMessage(messageData, { showNotification: true, incrementUnread: channelAlreadyInCache }); @@ -179,18 +179,19 @@ MailManager.include({ _handleNeedactionNotification: function (messageData) { var self = this; var inbox = this.getMailbox('inbox'); - var message = this.addMessage(messageData, { + this.addMessage(messageData, { incrementUnread: true, showNotification: true, + }).then(function (message) { + inbox.incrementMailboxCounter(); + _.each(message.getThreadIDs(), function (threadID) { + var channel = self.getChannel(threadID); + if (channel) { + channel.incrementNeedactionCounter(); + } + }); + self._mailBus.trigger('update_needaction', inbox.getMailboxCounter()); }); - inbox.incrementMailboxCounter(); - _.each(message.getThreadIDs(), function (threadID) { - var channel = self.getChannel(threadID); - if (channel) { - channel.incrementNeedactionCounter(); - } - }); - this._mailBus.trigger('update_needaction', inbox.getMailboxCounter()); }, /** * Called when an activity record has been updated on the server @@ -381,8 +382,10 @@ MailManager.include({ * @param {Object} data.message data of message */ _handlePartnerMessageModeratorNotification: function (data) { - this.addMessage(data.message); - this._mailBus.trigger('update_moderation_counter'); + var self = this; + this.addMessage(data.message).then(function () { + self._mailBus.trigger('update_moderation_counter'); + }); }, /** * On receiving a notification that is specific to a user diff --git a/addons/mail/static/src/js/services/mail_status_manager.js b/addons/mail/static/src/js/services/mail_status_manager.js index 5ca151d47bf..4a2df2af94f 100644 --- a/addons/mail/static/src/js/services/mail_status_manager.js +++ b/addons/mail/static/src/js/services/mail_status_manager.js @@ -81,7 +81,7 @@ MailManager.include({ var self = this; var partnerIDs = data.partnerIDs; if (_.isEmpty(partnerIDs)) { - return $.when(); + return Promise.resolve(); } return this._rpc({ model: 'res.partner', diff --git a/addons/mail/static/src/js/services/mail_window_manager.js b/addons/mail/static/src/js/services/mail_window_manager.js index 4e481d6892a..083b46c9ff2 100644 --- a/addons/mail/static/src/js/services/mail_window_manager.js +++ b/addons/mail/static/src/js/services/mail_window_manager.js @@ -94,6 +94,7 @@ MailManager.include({ * is not open yet, and do nothing otherwise. * @param {boolean} [options.keepFoldState=false] if set to true, keep the * fold state of the thread + * @returns {Promise} */ openThreadWindow: function (threadID, options) { var self = this; @@ -108,10 +109,10 @@ MailManager.include({ return; } var threadWindow = this._getThreadWindow(threadID); - var def = $.when(); + var prom = Promise.resolve(); if (!threadWindow) { thread.isCreatingWindow = true; - def = thread.fetchMessages().then(function () { + prom = thread.fetchMessages().then(function () { threadWindow = self._makeNewThreadWindow(thread, options); self._placeNewThreadWindow(threadWindow, options.passively); return threadWindow.appendTo($(self.THREAD_WINDOW_APPENDTO)); @@ -126,12 +127,12 @@ MailManager.include({ ) { thread.markAsRead(); } - }).fail(function () { + thread.isCreatingWindow = false; + }).guardedCatch(function () { // thread window could not be open, which may happen due to // access error while fetching messages to the document. // abort opening the thread window in this case. thread.close(); - }).always(function () { thread.isCreatingWindow = false; }); } else if (!options.passively) { @@ -139,7 +140,7 @@ MailManager.include({ this._makeThreadWindowVisible(threadWindow); } } - def.then(function () { + return prom.then(function () { threadWindow.updateVisualFoldState(); }); }, @@ -155,14 +156,16 @@ MailManager.include({ */ updateThreadWindow: function (threadID, options) { var thread = this.getThread(threadID); + var prom = Promise.resolve(); if (thread) { if (thread.isDetached()) { _.extend(options, { keepFoldState: true }); - this.openThreadWindow(threadID, options); + prom = this.openThreadWindow(threadID, options); } else { this._closeThreadWindow(threadID); } } + return prom; }, //-------------------------------------------------------------------------- @@ -429,7 +432,7 @@ MailManager.include({ * * @private * @param {integer} partnerID - * @returns {$.Promise} resolved with ID of the DM chat + * @returns {Promise} resolved with ID of the DM chat */ _openAndDetachDMChat: function (partnerID) { return this._rpc({ @@ -647,10 +650,16 @@ MailManager.include({ * * @private * @param {mail.model.Channel} channel + * @param {Promise[]} proms used to synchronize async operations on the + * channel (like the rendering of its window) */ - _onNewChannel: function (channel) { + _onNewChannel: function (channel, proms) { if (channel.isDetached()) { - this.openThreadWindow(channel.getID(), { keepFoldState: true, passively: true }); + var prom = this.openThreadWindow(channel.getID(), { + keepFoldState: true, + passively: true, + }); + proms.push(prom); } else { this._closeThreadWindow(channel.getID()); } diff --git a/addons/mail/static/src/js/systray/systray_activity_menu.js b/addons/mail/static/src/js/systray/systray_activity_menu.js index 593cac91506..fae95a78c2f 100644 --- a/addons/mail/static/src/js/systray/systray_activity_menu.js +++ b/addons/mail/static/src/js/systray/systray_activity_menu.js @@ -20,7 +20,7 @@ var ActivityMenu = Widget.extend({ 'show.bs.dropdown': '_onActivityMenuShow', }, willStart: function () { - return $.when(this.call('mail_service', 'isReady')); + return Promise.resolve(this.call('mail_service', 'isReady')); }, start: function () { this._$activitiesPreview = this.$('.o_mail_systray_dropdown_items'); diff --git a/addons/mail/static/src/js/systray/systray_messaging_menu.js b/addons/mail/static/src/js/systray/systray_messaging_menu.js index df35de47174..4dacae435d4 100644 --- a/addons/mail/static/src/js/systray/systray_messaging_menu.js +++ b/addons/mail/static/src/js/systray/systray_messaging_menu.js @@ -30,7 +30,7 @@ var MessagingMenu = Widget.extend({ * @override */ willStart: function () { - return $.when(this._super.apply(this, arguments), this.call('mail_service', 'isReady')); + return Promise.all([this._super.apply(this, arguments), this.call('mail_service', 'isReady')]); }, /** * @override @@ -112,7 +112,7 @@ var MessagingMenu = Widget.extend({ }, /** * @private - * @returns {$.Promise} resolved with list of previews that are + * @returns {Promise} resolved with list of previews that are * compatible with the 'mail.Preview' template. */ _getPreviews: function () { diff --git a/addons/mail/static/src/js/thread_field.js b/addons/mail/static/src/js/thread_field.js index a3807802ad7..ab8023b06ad 100644 --- a/addons/mail/static/src/js/thread_field.js +++ b/addons/mail/static/src/js/thread_field.js @@ -57,7 +57,7 @@ var ThreadField = AbstractField.extend({ var def1 = this._threadWidget.appendTo(this.$el); var def2 = this._super.apply(this, arguments); - return this.alive($.when(def1, def2)).then(function () { + return this.alive(Promise.all([def1, def2])).then(function () { // unwrap the thread to remove an unnecessary level on div self.setElement(self._threadWidget.$el); var mailBus = self.call('mail_service', 'getMailBus'); @@ -86,7 +86,7 @@ var ThreadField = AbstractField.extend({ /** * @param {Object} message * @param {integer[]} message.partner_ids - * @return {$.Promise} + * @return {Promise} */ postMessage: function (message) { var self = this; @@ -95,9 +95,9 @@ var ThreadField = AbstractField.extend({ if (message.partner_ids.length) { self.trigger_up('reload_mail_fields', { followers: true }); } - }) - .fail(function () { + }).guardedCatch(function () { self.do_notify(_t("Sending Error"), _t("Your message has not been sent.")); + return Promise.reject(); }); }, @@ -109,7 +109,7 @@ var ThreadField = AbstractField.extend({ * @private * @param {Object} [options] * @param {boolean} [options.forceFetch] - * @return {$.Deferred} + * @return {Promise} */ _fetchAndRenderThread: function (options) { var self = this; @@ -117,7 +117,7 @@ var ThreadField = AbstractField.extend({ var thread = new CreateModeDocumentThread(); options = { isCreateMode: true }; self._threadWidget.render(thread, options); - return $.when(); + return Promise.resolve(); } else { var fetchDef = this.dp.add(this._documentThread.fetchMessages(options)); return fetchDef.then(function () { @@ -134,7 +134,7 @@ var ThreadField = AbstractField.extend({ /** * @override * @private - * @returns {$.Deferred} + * @returns {Promise} */ _render: function () { return this._fetchAndRenderThread(); diff --git a/addons/mail/static/src/js/thread_widget.js b/addons/mail/static/src/js/thread_widget.js index cbac2c3b417..56945533946 100644 --- a/addons/mail/static/src/js/thread_widget.js +++ b/addons/mail/static/src/js/thread_widget.js @@ -248,16 +248,16 @@ var ThreadWidget = Widget.extend({ */ removeMessageAndRender: function (messageID, thread, options) { var self = this; - var done = $.Deferred(); - this.$('.o_thread_message[data-message-id="' + messageID + '"]') + return new Promise(function (resolve, reject) { + self.$('.o_thread_message[data-message-id="' + messageID + '"]') .fadeOut({ done: function () { self.render(thread, options); - done.resolve(); + resolve(); }, duration: 200, }); - return done; + }); }, /** * Scroll to the bottom of the thread @@ -273,7 +273,7 @@ var ThreadWidget = Widget.extend({ * @param {boolean} [options.onlyIfNecessary] */ scrollToMessage: function (options) { - var $target = this.$('.o_thread_message[data-message-id="' + options.msgID + '"]'); + var $target = this.$('.o_thread_message[data-message-id="' + options.messageID + '"]'); if (options.onlyIfNecessary) { var delta = $target.parent().height() - $target.height(); var offset = delta < 0 ? diff --git a/addons/mail/static/src/js/thread_windows/abstract_thread_window.js b/addons/mail/static/src/js/thread_windows/abstract_thread_window.js index 300bfb11a3e..ff5acfea0d2 100644 --- a/addons/mail/static/src/js/thread_windows/abstract_thread_window.js +++ b/addons/mail/static/src/js/thread_windows/abstract_thread_window.js @@ -95,7 +95,7 @@ var AbstractThreadWindow = Widget.extend({ var def = this._threadWidget.replace(this.$('.o_thread_window_content')).then(function () { self._threadWidget.$el.on('scroll', self, self._debouncedOnScroll); }); - return $.when(this._super(), def); + return Promise.all([this._super(), def]); }, /** * @override diff --git a/addons/mail/static/src/js/thread_windows/thread_window.js b/addons/mail/static/src/js/thread_windows/thread_window.js index e5496e0bc3c..9999014a08d 100644 --- a/addons/mail/static/src/js/thread_windows/thread_window.js +++ b/addons/mail/static/src/js/thread_windows/thread_window.js @@ -83,7 +83,7 @@ var ThreadWindow = AbstractThreadWindow.extend({ } this._updateOutOfOfficeReadMoreLessButton(); - return $.when(superDef, composerDef); + return Promise.all([superDef, composerDef]); }, //-------------------------------------------------------------------------- @@ -241,7 +241,7 @@ var ThreadWindow = AbstractThreadWindow.extend({ autoFocus: true, source: function (request, response) { self.call('mail_service', 'searchPartner', request.term, 10) - .done(response); + .then(response); }, select: function (event, ui) { // remember partner ID so that we can replace this window diff --git a/addons/mail/static/src/js/views/activity/activity_model.js b/addons/mail/static/src/js/views/activity/activity_model.js index 738cdb55ca1..f5a2feee988 100644 --- a/addons/mail/static/src/js/views/activity/activity_model.js +++ b/addons/mail/static/src/js/views/activity/activity_model.js @@ -19,7 +19,7 @@ var ActivityModel = AbstractModel.extend({ * @override * @param {Object} params * @param {Array[]} params.domain - * @returns {Deferred} + * @returns {Promise} */ load: function (params) { this.domain = params.domain; @@ -31,7 +31,7 @@ var ActivityModel = AbstractModel.extend({ * @param {any} handle * @param {Object} params * @param {Array[]} params.domain - * @returns {Deferred} + * @returns {Promise} */ reload: function (handle, params) { if (params && 'domain' in params) { @@ -48,7 +48,7 @@ var ActivityModel = AbstractModel.extend({ * Fetch activity data. * * @private - * @returns {Deferred} + * @returns {Promise} */ _fetchData: function () { var self = this; diff --git a/addons/mail/static/src/js/views/activity/activity_renderer.js b/addons/mail/static/src/js/views/activity/activity_renderer.js index d139a4e84cd..75eb25367e4 100644 --- a/addons/mail/static/src/js/views/activity/activity_renderer.js +++ b/addons/mail/static/src/js/views/activity/activity_renderer.js @@ -5,8 +5,8 @@ var AbstractRenderer = require('web.AbstractRenderer'); var core = require('web.core'); var field_registry = require('web.field_registry'); -var QWeb = core.qweb; var _t = core._t; +var QWeb = core.qweb; var ActivityRenderer = AbstractRenderer.extend({ className: 'o_activity_view', @@ -125,21 +125,22 @@ var ActivityRenderer = AbstractRenderer.extend({ }; var KanbanActivity = field_registry.get('kanban_activity'); var widget = new KanbanActivity(self, "activity_ids", record, {}); - widget.appendTo($td); - // replace clock by closest deadline - var $date = $('
'); - var formated_date = moment(activity_group.o_closest_deadline).format('ll'); - var current_year = (new Date()).getFullYear(); - if (formated_date.endsWith(current_year)) { // Dummy logic to remove year (only if current year), we will maybe need to improve it - formated_date = formated_date.slice(0, -4); - formated_date = formated_date.replace(/( |,)*$/g, ""); - } - $date - .text(formated_date) - .addClass('o_closest_deadline'); - $td.find('a') - .empty() - .append($date); + widget.appendTo($td).then(function() { + // replace clock by closest deadline + var $date = $('
'); + var formated_date = moment(activity_group.o_closest_deadline).format('ll'); + var current_year = (new Date()).getFullYear(); + if (formated_date.endsWith(current_year)) { // Dummy logic to remove year (only if current year), we will maybe need to improve it + formated_date = formated_date.slice(0, -4); + formated_date = formated_date.replace(/( |,)*$/g, ""); + } + $date + .text(formated_date) + .addClass('o_closest_deadline'); + $td.find('a') + .empty() + .append($date); + }); return $td; }); var $tr = $('', {class: 'o_data_row'}) diff --git a/addons/mail/static/tests/activity_tests.js b/addons/mail/static/tests/activity_tests.js index 19f4f6eaf5d..4e3e509936c 100644 --- a/addons/mail/static/tests/activity_tests.js +++ b/addons/mail/static/tests/activity_tests.js @@ -162,19 +162,19 @@ QUnit.module('activity view', { } }); -QUnit.test('activity view: simple activity rendering', function (assert) { +QUnit.test('activity view: simple activity rendering', async function (assert) { assert.expect(7); - var activity = createView({ + var activity = await createView({ View: ActivityView, model: 'task', data: this.data, arch: '', mockRPC: function(route, args) { if (args.method === 'get_activity_data') { - return $.when(ACTIVITY_DATA); + return Promise.resolve(ACTIVITY_DATA); } return this._super(route, args); - } + }, }); assert.containsOnce(activity, 'table', @@ -194,16 +194,16 @@ QUnit.test('activity view: simple activity rendering', function (assert) { activity.destroy(); }); -QUnit.test('activity view: no content rendering', function (assert) { +QUnit.test('activity view: no content rendering', async function (assert) { assert.expect(2); - var activity = createView({ + var activity = await createView({ View: ActivityView, model: 'task', data: this.data, arch: '', mockRPC: function (route, args) { if (args.method === 'get_activity_data') { - return $.when({ activity_types: [] }); + return Promise.resolve({ activity_types: [] }); } return this._super(route, args); }, @@ -218,20 +218,20 @@ QUnit.test('activity view: no content rendering', function (assert) { activity.destroy(); }); -QUnit.test('activity view: batch send mail on activity', function (assert) { +QUnit.test('activity view: batch send mail on activity', async function (assert) { assert.expect(6); - var activity = createView({ + var activity = await createView({ View: ActivityView, model: 'task', data: this.data, arch: '', mockRPC: function(route, args) { if (args.method === 'activity_send_mail'){ - assert.step(args.args); - return $.when(); + assert.step(JSON.stringify(args.args)); + return Promise.resolve(); } if (args.method === 'get_activity_data') { - return $.when(ACTIVITY_DATA); + return Promise.resolve(ACTIVITY_DATA); } return this._super(route, args); }, @@ -250,16 +250,16 @@ QUnit.test('activity view: batch send mail on activity', function (assert) { testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) i.fa-ellipsis-v')); testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show .o_send_mail_template:nth-child(2):contains(Task: Reception Acknowledgment)')); assert.verifySteps([ - [[13, 30], 9], //send mail template 9 on tasl 13 and 30 - [[13, 30], 8] //send mail template 8 on tasl 13 and 30 + '[[13,30],9]', // send mail template 9 on tasks 13 and 30 + '[[13,30],8]', // send mail template 8 on tasks 13 and 30 ]); activity.destroy(); }); -QUnit.test('activity view: activity widget', function (assert) { +QUnit.test('activity view: activity widget', async function (assert) { assert.expect(16); - var activity = createView({ + var activity = await createView({ View: ActivityView, model: 'task', data: this.data, @@ -268,16 +268,16 @@ QUnit.test('activity view: activity widget', function (assert) { if (args.method === 'activity_send_mail'){ assert.deepEqual([[30],8],args.args, "Should send template 8 on record 30"); assert.step('activity_send_mail'); - return $.when(); + return Promise.resolve(); } if (args.method === 'action_feedback_schedule_next'){ assert.deepEqual([[3]],args.args, "Should execute action_feedback_schedule_next on activity 3 only "); assert.equal(args.kwargs.feedback, "feedback2"); assert.step('action_feedback_schedule_next'); - return $.when({serverGeneratedAction: true}); + return Promise.resolve({serverGeneratedAction: true}); } if (args.method === 'get_activity_data') { - return $.when(ACTIVITY_DATA); + return Promise.resolve(ACTIVITY_DATA); } return this._super(route, args); }, @@ -305,12 +305,12 @@ QUnit.test('activity view: activity widget', function (assert) { assert.step("Unexpected action"); } }, - } + }, }); var today = activity.$('table tbody tr:first td:nth-child(2).today'); var dropdown = today.find('.dropdown-menu.o_activity'); - testUtils.dom.click(today.find('.o_closest_deadline')); + await testUtils.dom.click(today.find('.o_closest_deadline')); assert.hasClass(dropdown,'show', "dropdown should be displayed"); assert.ok(dropdown.find('.o_activity_color_today:contains(Today)').length, "Title should be today"); assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first div:contains(template8)').length, @@ -318,20 +318,20 @@ QUnit.test('activity view: activity widget', function (assert) { assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:eq(1) div:contains(template9)').length, "template9 should be available"); - testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_preview')); - testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_send')); + await testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_preview')); + await testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_send')); var overdue = activity.$('table tbody tr:first td:nth-child(3).overdue'); - testUtils.dom.click(overdue.find('.o_closest_deadline')); + await testUtils.dom.click(overdue.find('.o_closest_deadline')); dropdown = overdue.find('.dropdown-menu.o_activity'); assert.notOk(dropdown.find('.o_activity_title div div div:first span').length, "No template should be available"); - testUtils.dom.click(dropdown.find('.o_schedule_activity')); - testUtils.dom.click(overdue.find('.o_closest_deadline')); - testUtils.dom.click(dropdown.find('.o_mark_as_done')); + await testUtils.dom.click(dropdown.find('.o_schedule_activity')); + await testUtils.dom.click(overdue.find('.o_closest_deadline')); + await testUtils.dom.click(dropdown.find('.o_mark_as_done')); dropdown.find('#activity_feedback').val("feedback2"); - testUtils.dom.click(dropdown.find('.o_activity_popover_done_next')); + await testUtils.dom.click(dropdown.find('.o_activity_popover_done_next')); assert.verifySteps([ "do_action_compose", "activity_send_mail", @@ -342,10 +342,10 @@ QUnit.test('activity view: activity widget', function (assert) { activity.destroy(); }); -QUnit.test('activity view: no group_by_menu and no time_range_menu', function (assert) { +QUnit.test('activity view: no group_by_menu and no time_range_menu', async function (assert) { assert.expect(4); - var actionManager = createActionManager({ + var actionManager = await createActionManager({ actions: [{ id: 1, name: 'Task Action', @@ -365,13 +365,13 @@ QUnit.test('activity view: no group_by_menu and no time_range_menu', function (a if (args.method === 'get_activity_data') { assert.deepEqual(args.kwargs.context, {lang: 'zz_ZZ'}, 'The context should have been passed'); - return $.when(ACTIVITY_DATA); + return Promise.resolve(ACTIVITY_DATA); } return this._super(route, args); }, }); - actionManager.doAction(1); + await actionManager.doAction(1); assert.containsN(actionManager, '.o_search_options .o_dropdown button:visible', 2, "only two elements should be available in view search"); diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js index 7aaa35ac4e3..08f5c01fc34 100644 --- a/addons/mail/static/tests/chatter_tests.js +++ b/addons/mail/static/tests/chatter_tests.js @@ -3,13 +3,11 @@ odoo.define('mail.chatter_tests', function (require) { var mailTestUtils = require('mail.testUtils'); -var concurrency = require('web.concurrency'); var core = require('web.core'); var FormView = require('web.FormView'); var KanbanView = require('web.KanbanView'); var testUtils = require('web.test_utils'); -var createAsyncView = testUtils.createAsyncView; var createView = testUtils.createView; var Activity = require('mail.Activity'); @@ -192,13 +190,12 @@ QUnit.module('Chatter', { } }); -QUnit.test('basic rendering', function (assert) { +QUnit.test('basic rendering', async function (assert) { assert.expect(9); var count = 0; var unwanted_read_count = 0; - - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -219,7 +216,7 @@ QUnit.test('basic rendering', function (assert) { } if (route === '/mail/read_followers') { count++; - return $.when({ + return Promise.resolve({ followers: [], subtypes: [], }); @@ -228,25 +225,24 @@ QUnit.test('basic rendering', function (assert) { }, res_id: 2, }); - - assert.ok(form.$('.o_mail_activity').length, "there should be an activity widget"); - assert.ok(form.$('.o_chatter_topbar .o_chatter_button_schedule_activity').length, - "there should be a 'Schedule an activity' button in the chatter's topbar"); - assert.ok(form.$('.o_chatter_topbar .o_followers').length, - "there should be a followers widget, moved inside the chatter's topbar"); - assert.ok(form.$('.o_chatter').length, "there should be a chatter widget"); - assert.ok(form.$('.o_mail_thread').length, "there should be a mail thread"); + assert.containsOnce(form, '.o_mail_activity', "there should be an activity widget"); + assert.containsOnce(form, '.o_chatter_topbar .o_chatter_button_schedule_activity', + "there should be a 'Schedule an activity' button in the chatter's topbar"); + assert.containsOnce(form, '.o_chatter_topbar .o_followers', + "there should be a followers widget, moved inside the chatter's topbar"); + assert.containsOnce(form, '.o_chatter', "there should be a chatter widget"); + assert.containsOnce(form, '.o_mail_thread'); assert.containsOnce(form, '.o_chatter_button_attachment', "should have one attachment button"); - assert.ok(!form.$('.o_chatter_topbar .o_chatter_button_log_note').length, + assert.containsNone(form, '.o_chatter_topbar .o_chatter_button_log_note', "log note button should not be available"); - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.strictEqual(count, 0, "should have done no read_followers rpc as there are no followers"); assert.strictEqual(unwanted_read_count, 0, "followers should only be fetched with read_followers route"); form.destroy(); }); -QUnit.test('Activity Done keep feedback on blur', function (assert) { +QUnit.test('Activity Done keep feedback on blur', async function (assert) { assert.expect(3); var done = assert.async(); @@ -255,8 +251,8 @@ QUnit.test('Activity Done keep feedback on blur', function (assert) { ]; this.data.partner.records[0].activity_ids = [1]; - var shownDef = $.Deferred(); - var hiddenDef = $.Deferred(); + var shownDef = testUtils.makeTestPromise(); + var hiddenDef = testUtils.makeTestPromise(); testUtils.mock.patch(Activity, { _bindPopoverFocusout: function () { this._super.apply(this, arguments); @@ -264,7 +260,7 @@ QUnit.test('Activity Done keep feedback on blur', function (assert) { }, }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -276,7 +272,6 @@ QUnit.test('Activity Done keep feedback on blur', function (assert) { '
' + '', }); - // sanity checks var $activityEl = form.$('.o_mail_activity[name=activity_ids]'); assert.strictEqual($activityEl.find('.o_thread_message').length, 1, @@ -288,18 +283,18 @@ QUnit.test('Activity Done keep feedback on blur', function (assert) { $popoverEl.on('hidden.bs.popover', hiddenDef.resolve.bind(hiddenDef)); // open popover - testUtils.dom.click($popoverEl); + await testUtils.dom.click($popoverEl); shownDef.then(function () { // write a feedback and focusout var $feedbackPopover = $($popoverEl.data('bs.popover').tip); $feedbackPopover.find('#activity_feedback').val('John Shaft').focusout(); - hiddenDef.then(function () { - shownDef = $.Deferred(); + hiddenDef.then(async function () { + shownDef = testUtils.makeTestPromise(); // re-open popover - testUtils.dom.click($popoverEl); + await testUtils.dom.click($popoverEl); shownDef.then(function () { var $feedbackPopover = $($popoverEl.data('bs.popover').tip); @@ -314,7 +309,7 @@ QUnit.test('Activity Done keep feedback on blur', function (assert) { }); }); -QUnit.test('Activity Done by uploading a file', function (assert) { +QUnit.test('Activity Done by uploading a file', async function (assert) { assert.expect(4); // simulate (shortcut) the upload and trigger the event when the @@ -348,7 +343,7 @@ QUnit.test('Activity Done by uploading a file', function (assert) { }]; this.data.partner.records[0].activity_ids = [1]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -365,7 +360,7 @@ QUnit.test('Activity Done by uploading a file', function (assert) { var done_ids = args.args[0]; this.data.partner.records[0].activity_ids = _.difference(current_ids, done_ids); this.data.partner.records[0].activity_state = false; - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -379,7 +374,7 @@ QUnit.test('Activity Done by uploading a file', function (assert) { // click on "upload" button var $uploadBtn = $activity.find('.o_mark_as_done_upload_file'); - testUtils.dom.click($uploadBtn); + await testUtils.dom.click($uploadBtn); assert.containsNone($activity, '.o_thread_message', 'The only activity should be marked as done'); @@ -387,14 +382,14 @@ QUnit.test('Activity Done by uploading a file', function (assert) { form.destroy(); }); -QUnit.test('attachmentBox basic rendering', function (assert) { +QUnit.test('attachmentBox basic rendering', async function (assert) { assert.expect(19); this.data.partner.records.push({ id: 7, display_name: "attachment_test", }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -424,7 +419,7 @@ QUnit.test('attachmentBox basic rendering', function (assert) { }); var $button = form.$('.o_chatter_button_attachment'); assert.strictEqual($button.length, 1, "should have one attachment button"); - testUtils.dom.click($button); + await testUtils.dom.click($button); assert.containsOnce(form, '.o_mail_chatter_attachments', "attachment widget should exist after a first click on the button"); assert.containsOnce(form, '.o_attachment_image', "there should be an image preview"); @@ -455,15 +450,15 @@ QUnit.test('attachmentBox basic rendering', function (assert) { "the download URL of name1 must be correct"); assert.hasAttrValue(form.$('.o_attachment_download').eq(1), 'href', '/web/content/2?download=true', "the download URL of name2 must be correct"); - testUtils.dom.click($button); + await testUtils.dom.click($button); assert.containsNone(form, '.o_mail_chatter_attachments') form.destroy(); }); -QUnit.test('chatter in create mode', function (assert) { +QUnit.test('chatter in create mode', async function (assert) { assert.expect(9); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -479,7 +474,7 @@ QUnit.test('chatter in create mode', function (assert) { res_id: 2, mockRPC: function (route, args) { if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") { - return $.when({2: []}); + return Promise.resolve({2: []}); } return this._super(route, args); }, @@ -489,7 +484,7 @@ QUnit.test('chatter in create mode', function (assert) { "chatter should be displayed"); // entering create mode - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.hasClass(form.$el.find('.o_form_view'),'o_form_editable', "we should be in create mode"); assert.containsOnce(form, '.o_chatter', @@ -509,24 +504,24 @@ QUnit.test('chatter in create mode', function (assert) { "the content of the message should be 'Creating a new record...'"); // getting out of create mode by saving - testUtils.fields.editInput(form.$('.o_field_char'), 'coucou'); - testUtils.form.clickSave(form); + await testUtils.fields.editInput(form.$('.o_field_char'), 'coucou'); + await testUtils.form.clickSave(form); assert.containsOnce(form, '.o_chatter', "chatter should still be displayed after saving from create mode"); // check if chatter buttons still work - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.containsOnce(form, '.o_thread_composer:visible', "chatter should be opened"); form.destroy(); }); -QUnit.test('chatter rendering inside the sheet', function (assert) { +QUnit.test('chatter rendering inside the sheet', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -546,7 +541,7 @@ QUnit.test('chatter rendering inside the sheet', function (assert) { res_id: 2, mockRPC: function (route, args) { if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") { - return $.when({2: []}); + return Promise.resolve({2: []}); } return this._super(route, args); }, @@ -555,32 +550,32 @@ QUnit.test('chatter rendering inside the sheet', function (assert) { assert.containsOnce(form, '.o_chatter', "chatter should be displayed"); - testUtils.form.clickCreate(form); + await testUtils.form.clickCreate(form); assert.hasClass(form.$el.find('.o_form_view'),'o_form_editable', "we should be in create mode"); assert.containsOnce(form, '.o_chatter', "chatter should be displayed"); - testUtils.fields.editInput(form.$('.o_field_char'), 'coucou'); - testUtils.form.clickSave(form); + await testUtils.fields.editInput(form.$('.o_field_char'), 'coucou'); + await testUtils.form.clickSave(form); assert.containsOnce(form, '.o_chatter', "chatter should be displayed"); // check if chatter buttons still work - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.containsOnce(form, '.o_thread_composer:visible', "chatter should be opened"); form.destroy(); }); -QUnit.test('kanban activity widget with no activity', function (assert) { +QUnit.test('kanban activity widget with no activity', async function (assert) { assert.expect(4); var rpcCount = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -603,7 +598,7 @@ QUnit.test('kanban activity widget with no activity', function (assert) { assert.strictEqual(rpcCount, 1, '1 RPC (search_read) should have been done'); // click on the activity button - testUtils.dom.click($record.find('.o_activity_btn')); + await testUtils.dom.click($record.find('.o_activity_btn')); assert.strictEqual(rpcCount, 1, 'no RPC should have been done as there is no activity'); assert.strictEqual($record.find('.o_no_activity').length, 1, "should have no activity scheduled"); @@ -612,7 +607,7 @@ QUnit.test('kanban activity widget with no activity', function (assert) { kanban.destroy(); }); -QUnit.test('kanban activity widget with an activity', function (assert) { +QUnit.test('kanban activity widget with an activity', async function (assert) { assert.expect(12); this.data.partner.records[0].activity_ids = [1]; @@ -628,7 +623,7 @@ QUnit.test('kanban activity widget with an activity', function (assert) { activity_type_id: 1, }]; var rpcCount = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -645,7 +640,7 @@ QUnit.test('kanban activity widget with an activity', function (assert) { var done_ids = args.args[0]; this.data.partner.records[0].activity_ids = _.difference(current_ids, done_ids); this.data.partner.records[0].activity_state = false; - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, @@ -658,7 +653,7 @@ QUnit.test('kanban activity widget with an activity', function (assert) { assert.strictEqual(rpcCount, 1, '1 RPC (search_read) should have been done'); // click on the activity button - testUtils.dom.click($record.find('.o_activity_btn')); + await testUtils.dom.click($record.find('.o_activity_btn')); assert.strictEqual(rpcCount, 2, 'a read should have been done to fetch the activity details'); assert.strictEqual($record.find('.o_activity_title').length, 1, "should have an activity scheduled"); var label = $record.find('.o_activity_log .o_activity_color_today'); @@ -666,16 +661,16 @@ QUnit.test('kanban activity widget with an activity', function (assert) { assert.strictEqual(label.find('.badge-warning').text(), "1", "should display the correct count"); // click on the activity button to close the dropdown - testUtils.dom.click($record.find('.o_activity_btn')); + await testUtils.dom.click($record.find('.o_activity_btn')); assert.strictEqual(rpcCount, 2, 'no RPC should be done when closing the dropdown'); // click on the activity button to re-open dropdown - testUtils.dom.click($record.find('.o_activity_btn')); + await testUtils.dom.click($record.find('.o_activity_btn')); assert.strictEqual(rpcCount, 3, 'should have reloaded the activities'); // mark activity as done - testUtils.dom.click($record.find('.o_mark_as_done')); - testUtils.dom.click($record.find('.o_activity_popover_done')); + await testUtils.dom.click($record.find('.o_mark_as_done')); + await testUtils.dom.click($record.find('.o_activity_popover_done')); $record = kanban.$('.o_kanban_record').first(); // the record widget has been reset assert.strictEqual(rpcCount, 5, 'should have done an RPC to mark activity as done, and a read'); assert.ok($record.find('.o_mail_activity .o_activity_color_default:not(.o_activity_color_today)').length, @@ -687,7 +682,7 @@ QUnit.test('kanban activity widget with an activity', function (assert) { kanban.destroy(); }); -QUnit.test('kanban activity widget popover test', function (assert) { +QUnit.test('kanban activity widget popover test', async function (assert) { assert.expect(3); this.data.partner.records[0].activity_ids = [1]; @@ -703,7 +698,7 @@ QUnit.test('kanban activity widget popover test', function (assert) { activity_type_id: 1, }]; var rpcCount = 0; - var kanban = createView({ + var kanban = await createView({ View: KanbanView, model: 'partner', data: this.data, @@ -721,7 +716,7 @@ QUnit.test('kanban activity widget popover test', function (assert) { var done_ids = args.args[0]; this.data.partner.records[0].activity_ids = _.difference(current_ids, done_ids); this.data.partner.records[0].activity_state = false; - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, @@ -729,25 +724,24 @@ QUnit.test('kanban activity widget popover test', function (assert) { var $record = kanban.$('.o_kanban_record').first(); - testUtils.dom.click($record.find('.o_activity_btn')); + await testUtils.dom.click($record.find('.o_activity_btn')); // Click on button and see popover no RPC call - testUtils.dom.click($record.find('.o_mark_as_done')); + await testUtils.dom.click($record.find('.o_mark_as_done')); assert.equal(rpcCount, 0, ""); // Click on discard no RPC call - testUtils.dom.click($record.find('.o_activity_popover_discard')); + await testUtils.dom.click($record.find('.o_activity_popover_discard')); assert.equal(rpcCount, 0, ""); // Click on button and then on done and schedule next // RPC call - testUtils.dom.click($record.find('.o_activity_popover_done_next')); + await testUtils.dom.click($record.find('.o_activity_popover_done_next')); assert.equal(rpcCount, 1, ""); kanban.destroy(); }); -QUnit.test('chatter: post, receive and star messages', function (assert) { - var done = assert.async(); - assert.expect(27); +QUnit.test('chatter: post, receive and star messages', async function (assert) { + assert.expect(26); this.data.partner.records[0].message_ids = [1]; this.data['mail.message'].records = [{ @@ -763,8 +757,8 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { res_id: 2, }]; - var getSuggestionsDef = $.Deferred(); - var form = createView({ + var getSuggestionsDef = testUtils.makeTestPromise(); + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -780,11 +774,11 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { res_id: 2, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'get_mention_suggestions') { getSuggestionsDef.resolve(); - return $.when([[{email: "test@odoo.com", id: 1, name: "Test User"}], []]); + return Promise.resolve([[{email: "test@odoo.com", id: 1, name: "Test User"}], []]); } if (args.method === 'message_post') { var lastMessageData = _.max(this.data['mail.message'].records, function (messageData) { @@ -804,7 +798,7 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { model: 'partner', res_id: 2, }); - return $.when(messageID); + return Promise.resolve(messageID); } if (args.method === 'toggle_message_starred') { assert.ok(_.contains(args.args[0], 2), @@ -823,7 +817,7 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { }; var notification = [[false, 'res.partner'], data]; form.call('bus_service', 'trigger', 'notification', [notification]); - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, @@ -841,10 +835,10 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { "the message's author should be correct"); // send a message - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.isVisible($('.oe_chatter .o_thread_composer'), "chatter should be opened"); form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); assert.isNotVisible($('.oe_chatter .o_thread_composer'), "chatter should be closed"); assert.containsN(form, '.o_thread_message', 2, "thread should contain two messages"); assert.ok(form.$('.o_thread_message:first().o_mail_discussion').length, @@ -855,10 +849,10 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { "the message's author should be correct"); // log a note - testUtils.dom.click(form.$('.o_chatter_button_log_note')); + await testUtils.dom.click(form.$('.o_chatter_button_log_note')); assert.isVisible($('.oe_chatter .o_thread_composer'), "chatter should be opened"); form.$('.oe_chatter .o_composer_text_field:first()').val("My first note"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); assert.isNotVisible($('.oe_chatter .o_thread_composer'), "chatter should be closed"); assert.containsN(form, '.o_thread_message', 3, "thread should contain three messages"); assert.ok(!form.$('.o_thread_message:first().o_mail_discussion').length, @@ -871,17 +865,17 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { // star message 2 assert.ok(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star.fa-star-o').length, "message 2 should not be starred"); - testUtils.dom.click(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star')); + await testUtils.dom.click(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star')); assert.ok(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star.fa-star').length, "message 2 should be starred"); // unstar message 2 - testUtils.dom.click(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star')); + await testUtils.dom.click(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star')); assert.ok(form.$('.o_thread_message[data-message-id=2] .o_thread_message_star.fa-star-o').length, "message 2 should not be starred"); // very basic test of mention - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); var $input = form.$('.oe_chatter .o_composer_text_field:first()'); $input.val('@'); // the cursor position must be set for the mention manager to detect that we are mentionning @@ -889,30 +883,24 @@ QUnit.test('chatter: post, receive and star messages', function (assert) { $input[0].selectionEnd = 1; $input.trigger('keyup'); - assert.strictEqual(getSuggestionsDef.state(), "pending", - "the mention suggestion RPC should be throttled"); - - getSuggestionsDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - assert.containsOnce(form, '.o_mention_proposition:visible', - "there should be one mention suggestion"); - assert.strictEqual(form.$('.o_mention_proposition').data('id'), 1, - "suggestion's id should be correct"); - assert.strictEqual(form.$('.o_mention_proposition .o_mention_name').text(), 'Test User', - "suggestion should be displayed correctly"); - assert.strictEqual(form.$('.o_mention_proposition .o_mention_info').text(), '(test@odoo.com)', - "suggestion should be displayed correctly"); - - //cleanup - form.destroy(); - done(); - }); + await testUtils.nextTick(); + await getSuggestionsDef; + await testUtils.nextTick(); + assert.containsOnce(form, '.o_mention_proposition:visible', + "there should be one mention suggestion"); + assert.strictEqual(form.$('.o_mention_proposition').data('id'), 1, + "suggestion's id should be correct"); + assert.strictEqual(form.$('.o_mention_proposition .o_mention_name').text(), 'Test User', + "suggestion should be displayed correctly"); + assert.strictEqual(form.$('.o_mention_proposition .o_mention_info').text(), '(test@odoo.com)', + "suggestion should be displayed correctly"); + //cleanup + form.destroy(); }); -QUnit.test('chatter: post a message disable the send button', function(assert) { +QUnit.test('chatter: post a message disable the send button', async function(assert) { assert.expect(3); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -929,15 +917,15 @@ QUnit.test('chatter: post a message disable the send button', function(assert) { session: {}, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'message_post') { assert.ok(form.$('.o_composer_button_send').prop("disabled"), "Send button should be disabled when a message is being sent"); - return $.when(57923); + return Promise.resolve(57923); } if (args.method === 'message_format') { - return $.when([{ + return Promise.resolve([{ author_id: ["42", "Me"], model: 'partner', }]); @@ -946,20 +934,20 @@ QUnit.test('chatter: post a message disable the send button', function(assert) { }, }); - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.notOk(form.$('.o_composer_button_send').prop('disabled'), "Send button should be enabled when posting a message"); form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.notOk(form.$('.o_composer_button_send').prop('disabled'), "Send button should be enabled when posting another message"); form.destroy(); }); -QUnit.test('chatter: post message failure keep message', function(assert) { +QUnit.test('chatter: post message failure keep message', async function(assert) { assert.expect(4); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -976,26 +964,27 @@ QUnit.test('chatter: post message failure keep message', function(assert) { session: {}, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'message_post') { assert.ok(form.$('.o_composer_button_send').prop("disabled"), "Send button should be disabled when a message is being sent"); // simulate failure - return $.Deferred().reject(); + return Promise.reject(); + } if (args.method === 'message_format') { - return $.when([]); + return Promise.resolve([]); } return this._super(route, args); }, }); - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.notOk(form.$('.o_composer_button_send').prop('disabled'), "Send button should be enabled initially"); - form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.fields.editInput(form.$('.oe_chatter .o_composer_text_field:first()'), "My first message"); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); assert.strictEqual(form.$('.o_composer_text_field').val(), "My first message", "Should keep unsent message in the composer on failure"); assert.notOk(form.$('.o_composer_button_send').prop('disabled'), @@ -1003,10 +992,10 @@ QUnit.test('chatter: post message failure keep message', function(assert) { form.destroy(); }); -QUnit.test('chatter: receive notif when document is open', function (assert) { +QUnit.test('chatter: receive notif when document is open', async function (assert) { assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1043,14 +1032,14 @@ QUnit.test('chatter: receive notif when document is open', function (assert) { this.data['mail.message'].records.push(needactionMessageData); var notification = [[false, 'mail.channel', 1], needactionMessageData]; form.call('bus_service', 'trigger', 'notification', [notification]); - + await testUtils.nextMicrotaskTick(); assert.strictEqual(thread.getUnreadCounter(), 1, "document thread should now have one unread message"); form.destroy(); }); -QUnit.test('chatter: access document with some notifs', function (assert) { +QUnit.test('chatter: access document with some notifs', async function (assert) { assert.expect(3); // simulate received needaction message on this document thread @@ -1067,7 +1056,7 @@ QUnit.test('chatter: access document with some notifs', function (assert) { this.data['mail.message'].records.push(needactionMessageData); this.data['partner'].records[0].message_ids = [5]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1101,10 +1090,10 @@ QUnit.test('chatter: access document with some notifs', function (assert) { form.destroy(); }); -QUnit.test('chatter: post a message and switch in edit mode', function (assert) { +QUnit.test('chatter: post a message and switch in edit mode', async function (assert) { assert.expect(5); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1121,7 +1110,7 @@ QUnit.test('chatter: post a message and switch in edit mode', function (assert) session: {}, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'message_post') { this.data['mail.message'].records.push({ @@ -1136,7 +1125,7 @@ QUnit.test('chatter: post a message and switch in edit mode', function (assert) model: 'partner', res_id: 2, }); - return $.when(42); + return Promise.resolve(42); } return this._super(route, args); @@ -1146,15 +1135,15 @@ QUnit.test('chatter: post a message and switch in edit mode', function (assert) assert.containsNone(form, '.o_thread_message', "thread should not contain messages"); // send a message - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); assert.containsOnce(form, '.o_thread_message', "thread should contain a message"); assert.ok(form.$('.o_thread_message:first() .o_thread_message_core').text().indexOf('My first message') >= 0, "the message's body should be correct"); // switch in edit mode - testUtils.form.clickEdit(form); + await testUtils.form.clickEdit(form); assert.containsOnce(form, '.o_thread_message', "thread should contain a message"); assert.ok(form.$('.o_thread_message:first() .o_thread_message_core').text().indexOf('My first message') >= 0, "the message's body should be correct"); @@ -1162,13 +1151,13 @@ QUnit.test('chatter: post a message and switch in edit mode', function (assert) form.destroy(); }); -QUnit.test('chatter: discard changes on message post with post_refresh "always"', function (assert) { +QUnit.test('chatter: discard changes on message post with post_refresh "always"', async function (assert) { // After posting a message that always reloads the record, if the record // is dirty (= has some unsaved changes), we should warn the user that // these changes will be lost if he proceeds. assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1186,7 +1175,7 @@ QUnit.test('chatter: discard changes on message post with post_refresh "always"' session: {}, mockRPC: function (route, args) { if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") { - return $.when({2: []}); + return Promise.resolve({2: []}); } return this._super(route, args); }, @@ -1196,12 +1185,12 @@ QUnit.test('chatter: discard changes on message post with post_refresh "always"' }); // Make record dirty - testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); + await testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); // Send a message - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); var $modal = $('.modal-dialog'); assert.strictEqual($modal.length, 1, "should have a modal opened"); @@ -1212,14 +1201,14 @@ QUnit.test('chatter: discard changes on message post with post_refresh "always"' form.destroy(); }); -QUnit.test('chatter: discard changes on message post without post_refresh', function (assert) { +QUnit.test('chatter: discard changes on message post without post_refresh', async function (assert) { // After posting a message, if the record is dirty and there are no // post_refresh rule, it will not discard the changes on the record. assert.expect(2); var hasDiscardChanges = false; // set if `discard_changes` has been triggered up var messages = []; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1237,13 +1226,13 @@ QUnit.test('chatter: discard changes on message post without post_refresh', func session: {}, mockRPC: function (route, args) { if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'message_format') { var requested_msgs = _.filter(messages, function (msg) { return _.contains(args.args[0], msg.id); }); - return $.when(requested_msgs); + return Promise.resolve(requested_msgs); } if (args.method === 'message_post') { messages.push({ @@ -1258,7 +1247,7 @@ QUnit.test('chatter: discard changes on message post without post_refresh', func model: 'partner', res_id: 2, }); - return $.when(42); + return Promise.resolve(42); } return this._super(route, args); }, @@ -1273,12 +1262,12 @@ QUnit.test('chatter: discard changes on message post without post_refresh', func }); // Make record dirty - testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); + await testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); // Send a message - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); var $modal = $('.modal-dialog'); assert.strictEqual($modal.length, 0, "should have no modal opened"); @@ -1287,18 +1276,17 @@ QUnit.test('chatter: discard changes on message post without post_refresh', func form.destroy(); }); -QUnit.test('chatter: discard changes on message post with post_refresh "recipients"', function (assert) { +QUnit.test('chatter: discard changes on message post with post_refresh "recipients"', async function (assert) { // After posting a message with mentions, the record will be reloaded, // as the rpc `message_post` may make changes on some fields of the record. // If the record is dirty (= has some unsaved changes), we should warn the // user that these changes will be lost if he proceeds. assert.expect(2); - var done = assert.async(); - var getSuggestionsDef = $.Deferred(); + var getSuggestionsDef = testUtils.makeTestPromise(); var messages = []; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1316,17 +1304,17 @@ QUnit.test('chatter: discard changes on message post with post_refresh "recipien session: {}, mockRPC: function (route, args) { if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") { - return $.when({2: [[42, "Me"]]}); + return Promise.resolve({2: [[42, "Me"]]}); } if (args.method === 'get_mention_suggestions') { getSuggestionsDef.resolve(); - return $.when([[{email: "me@odoo.com", id: 42, name: "Me"}], []]); + return Promise.resolve([[{email: "me@odoo.com", id: 42, name: "Me"}], []]); } if (args.method === 'message_format') { var requested_msgs = _.filter(messages, function (msg) { return _.contains(args.args[0], msg.id); }); - return $.when(requested_msgs); + return Promise.resolve(requested_msgs); } if (args.method === 'message_post') { messages.push({ @@ -1341,7 +1329,7 @@ QUnit.test('chatter: discard changes on message post with post_refresh "recipien model: 'partner', res_id: 2, }); - return $.when(42); + return Promise.resolve(42); } return this._super(route, args); }, @@ -1351,13 +1339,13 @@ QUnit.test('chatter: discard changes on message post with post_refresh "recipien }); // Make record dirty - testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); + await testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); // create a new message - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); // Add a user as mention - form.$('.oe_chatter .o_composer_text_field:first()').val("@"); + await testUtils.fields.editInput(form.$('.oe_chatter .o_composer_text_field:first()'), "@"); var $input = form.$('.oe_chatter .o_composer_text_field:first()'); $input.val('@'); @@ -1365,38 +1353,35 @@ QUnit.test('chatter: discard changes on message post with post_refresh "recipien $input[0].selectionStart = 1; $input[0].selectionEnd = 1; $input.trigger('keyup'); + await getSuggestionsDef; + await testUtils.nextTick(); + // click on mention + $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER})); + await testUtils.nextTick(); - getSuggestionsDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - // click on mention - $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER})); + // untick recipient as follower (prompts a res.partner form otherwise) + form.$('input[type="checkbox"]').prop('checked', false); + await testUtils.nextTick(); + // send message + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); - // untick recipient as follower (prompts a res.partner form otherwise) - form.$('input[type="checkbox"]').prop('checked', false); + var $modal = $('.modal-dialog'); + assert.strictEqual($modal.length, 1, "should have a modal opened"); + assert.strictEqual($modal.find('.modal-body').text(), + "The record has been modified, your changes will be discarded. Do you want to proceed?", + "should warn the user that any unsaved changes will be lost"); - // send message - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); - - var $modal = $('.modal-dialog'); - assert.strictEqual($modal.length, 1, "should have a modal opened"); - assert.strictEqual($modal.find('.modal-body').text(), - "The record has been modified, your changes will be discarded. Do you want to proceed?", - "should warn the user that any unsaved changes will be lost"); - - form.destroy(); - done(); - }); + form.destroy(); }); -QUnit.test('chatter: discard changes on opening full-composer', function (assert) { +QUnit.test('chatter: discard changes on opening full-composer', async function (assert) { // When we open the full-composer, any following operations by the user // will reload the record (even closing the full-composer). Therefore, // we should warn the user when we open the full-composer if the record // is dirty (= has some unsaved changes). assert.expect(2); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1415,7 +1400,7 @@ QUnit.test('chatter: discard changes on opening full-composer', function (assert session: {}, mockRPC: function (route, args) { if (route === "/web/dataset/call_kw/partner/message_get_suggested_recipients") { - return $.when({2: []}); + return Promise.resolve({2: []}); } return this._super(route, args); }, @@ -1425,11 +1410,11 @@ QUnit.test('chatter: discard changes on opening full-composer', function (assert }); // Make record dirty - testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); + await testUtils.fields.editInput(form.$('.o_form_sheet input'), 'trululu'); // Open full-composer - testUtils.dom.click(form.$('.o_chatter_button_new_message')); - testUtils.dom.click(form.$('.o_composer_button_full_composer')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_composer_button_full_composer')); var $modal = $('.modal-dialog'); assert.strictEqual($modal.length, 1, "should have a modal opened"); @@ -1440,7 +1425,7 @@ QUnit.test('chatter: discard changes on opening full-composer', function (assert form.destroy(); }); -QUnit.test('chatter in x2many form view', function (assert) { +QUnit.test('chatter in x2many form view', async function (assert) { // the purpose of this test is to ensure that it doesn't crash when a x2many // record is opened in form view (thus in a dialog), and when there is a // chatter in the arch (typically, this may occur when the view used for the @@ -1453,7 +1438,7 @@ QUnit.test('chatter in x2many form view', function (assert) { }; this.data.partner.records[0].o2m = [2]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1474,7 +1459,7 @@ QUnit.test('chatter in x2many form view', function (assert) { }, }); - testUtils.dom.click(form.$('.o_data_row:first')); + await testUtils.dom.click(form.$('.o_data_row:first')); assert.strictEqual($('.modal .o_form_view').length, 1, "should have open a form view in a modal"); @@ -1484,7 +1469,7 @@ QUnit.test('chatter in x2many form view', function (assert) { form.destroy(); }); -QUnit.test('chatter: Attachment viewer', function (assert) { +QUnit.test('chatter: Attachment viewer', async function (assert) { assert.expect(6); this.data.partner.records[0].message_ids = [1]; this.data['mail.message'].records = [{ @@ -1527,7 +1512,7 @@ QUnit.test('chatter: Attachment viewer', function (assert) { res_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1545,7 +1530,7 @@ QUnit.test('chatter: Attachment viewer', function (assert) { if (_.str.contains(route, '/mail/attachment/preview/') || _.str.contains(route, '/web/static/lib/pdfjs/web/viewer.html')){ var canvas = document.createElement('canvas'); - return $.when(canvas.toDataURL()); + return Promise.resolve(canvas.toDataURL()); } return this._super.apply(this, arguments); }, @@ -1555,29 +1540,29 @@ QUnit.test('chatter: Attachment viewer', function (assert) { assert.hasAttrValue(form.$('.o_thread_message .o_attachment a').first(), 'href', '/web/content/1?download=true', "image caption should have correct download link"); // click on first image attachement - testUtils.dom.click(form.$('.o_thread_message .o_attachment .o_image_box .o_image_overlay').first()); + await testUtils.dom.click(form.$('.o_thread_message .o_attachment .o_image_box .o_image_overlay').first()); assert.strictEqual($('.o_modal_fullscreen img.o_viewer_img[data-src="/web/image/1?unique=1&signature=999&model=ir.attachment"]').length, 1, "Modal popup should open with first image src"); // click on next button - testUtils.dom.click($('.modal .arrow.arrow-right.move_next span')); + await testUtils.dom.click($('.modal .arrow.arrow-right.move_next span')); assert.strictEqual($('.o_modal_fullscreen img.o_viewer_img[data-src="/web/image/2?unique=1&signature=999&model=ir.attachment"]').length, 1, "Modal popup should have now second image src"); assert.strictEqual($('.o_modal_fullscreen .o_viewer_toolbar .o_download_btn').length, 1, "Modal popup should have download button"); // close attachment popup - testUtils.dom.click($('.o_modal_fullscreen .o_viewer-header .o_close_btn')); + await testUtils.dom.click($('.o_modal_fullscreen .o_viewer-header .o_close_btn')); // click on pdf attachement - testUtils.dom.click(form.$('span:contains(Test PDF 1)')); + await testUtils.dom.click(form.$('span:contains(Test PDF 1)')); assert.strictEqual($('.o_modal_fullscreen iframe[data-src*="/web/content/4"]').length, 1, "Modal popup should open with the pdf preview"); // close attachment popup - testUtils.dom.click($('.o_modal_fullscreen .o_viewer-header .o_close_btn')); + await testUtils.dom.click($('.o_modal_fullscreen .o_viewer-header .o_close_btn')); form.destroy(); }); -QUnit.test('chatter: keep context when sending a message', function(assert) { +QUnit.test('chatter: keep context when sending a message', async function(assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1596,7 +1581,7 @@ QUnit.test('chatter: keep context when sending a message', function(assert) { }, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'message_post') { assert.deepEqual(args.kwargs.context, { @@ -1606,10 +1591,10 @@ QUnit.test('chatter: keep context when sending a message', function(assert) { mail_post_autofollow: true, }, "the context is incorrect"); - return $.when(57923); + return Promise.resolve(57923); } if (args.method === 'message_format') { - return $.when([{ + return Promise.resolve([{ author_id: [42, "Me"], model: 'partner', }]); @@ -1618,13 +1603,13 @@ QUnit.test('chatter: keep context when sending a message', function(assert) { }, }); - testUtils.dom.click(form.$('.o_chatter_button_new_message')); - testUtils.fields.editInput(form.$('.oe_chatter .o_composer_text_field:first()'), 'Pouet'); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.fields.editInput(form.$('.oe_chatter .o_composer_text_field:first()'), 'Pouet'); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); form.destroy(); }); -QUnit.test('form activity widget: read RPCs', function (assert) { +QUnit.test('form activity widget: read RPCs', async function (assert) { // Records of model 'mail.activity' may be updated in business flows (e.g. // the date of a 'Meeting' activity is updated when the associated meeting // is dragged&dropped in the Calendar view). Because of that, the activities @@ -1645,7 +1630,7 @@ QUnit.test('form activity widget: read RPCs', function (assert) { }]; var nbReads = 0; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1670,15 +1655,15 @@ QUnit.test('form activity widget: read RPCs', function (assert) { assert.strictEqual(form.$('.o_mail_activity .o_thread_message .o_activity_date').text(), 'Today', "the activity should be today"); - testUtils.form.clickEdit(form); - testUtils.form.clickSave(form); + await testUtils.form.clickEdit(form); + await testUtils.form.clickSave(form); assert.strictEqual(nbReads, 1, "should not have re-read the activities"); // simulate a date change, and a reload of the form view var tomorrow = moment().add(1, 'day').format("YYYY-MM-DD"); this.data['mail.activity'].records[0].date_deadline = tomorrow; - form.reload(); + await form.reload(); assert.strictEqual(nbReads, 2, "should have re-read the activities"); assert.strictEqual(form.$('.o_mail_activity .o_thread_message .o_activity_date').text(), @@ -1687,10 +1672,10 @@ QUnit.test('form activity widget: read RPCs', function (assert) { form.destroy(); }); -QUnit.test('form activity widget on a new record', function (assert) { +QUnit.test('form activity widget on a new record', async function (assert) { assert.expect(0); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1711,7 +1696,7 @@ QUnit.test('form activity widget on a new record', function (assert) { form.destroy(); }); -QUnit.test('form activity widget with another x2many field in view', function (assert) { +QUnit.test('form activity widget with another x2many field in view', async function (assert) { assert.expect(1); this.data.partner.fields.m2m = {string: "M2M", type: 'many2many', relation: 'partner'}; @@ -1730,7 +1715,7 @@ QUnit.test('form activity widget with another x2many field in view', function (a activity_type_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1750,7 +1735,7 @@ QUnit.test('form activity widget with another x2many field in view', function (a form.destroy(); }); -QUnit.test('form activity widget: schedule next activity', function (assert) { +QUnit.test('form activity widget: schedule next activity', async function (assert) { assert.expect(4); this.data.partner.records[0].activity_ids = [1]; this.data.partner.records[0].activity_state = 'today'; @@ -1765,7 +1750,7 @@ QUnit.test('form activity widget: schedule next activity', function (assert) { activity_type_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1785,7 +1770,7 @@ QUnit.test('form activity widget: schedule next activity', function (assert) { assert.ok(_.isEqual(args.args[0], [1]), "should call 'action_feedback_schedule_next' for id 1"); assert.strictEqual(args.kwargs.feedback, 'everything is ok', "the feedback should be sent correctly"); - return $.when('test_result'); + return Promise.resolve('test_result'); } return this._super.apply(this, arguments); }, @@ -1797,16 +1782,16 @@ QUnit.test('form activity widget: schedule next activity', function (assert) { }, }); //Schedule next activity - testUtils.dom.click(form.$('.o_mail_activity .o_mark_as_done[data-activity-id=1]')); + await testUtils.dom.click(form.$('.o_mail_activity .o_mark_as_done[data-activity-id=1]')); assert.containsOnce(form, '.o_mail_activity_feedback.popover', "a feedback popover should be visible"); $('.o_mail_activity_feedback.popover textarea').val('everything is ok'); // write a feedback - testUtils.dom.click(form.$('.o_activity_popover_done_next')); + await testUtils.dom.click(form.$('.o_activity_popover_done_next')); form.destroy(); }); -QUnit.test('form activity widget: edit next activity', function (assert) { +QUnit.test('form activity widget: edit next activity', async function (assert) { assert.expect(3); var self = this; this.data.partner.records[0].activity_ids = [1]; @@ -1822,7 +1807,7 @@ QUnit.test('form activity widget: edit next activity', function (assert) { activity_type_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1866,13 +1851,13 @@ QUnit.test('form activity widget: edit next activity', function (assert) { }); assert.strictEqual(form.$('.o_mail_activity .o_mail_info strong:eq(1)').text(), " Type 2", "Initial type should be Type 2"); - testUtils.dom.click(form.$('.o_mail_activity .o_edit_activity[data-activity-id=1]')); + await testUtils.dom.click(form.$('.o_mail_activity .o_edit_activity[data-activity-id=1]')); assert.strictEqual(form.$('.o_mail_activity .o_mail_info strong:eq(1)').text(), " Type 1", "After edit type should be Type 1"); form.destroy(); }); -QUnit.test('form activity widget: clic mail template', function (assert) { +QUnit.test('form activity widget: clic mail template', async function (assert) { assert.expect(4); this.data.partner.records[0].activity_ids = [1]; this.data.partner.records[0].activity_state = 'today'; @@ -1886,7 +1871,7 @@ QUnit.test('form activity widget: clic mail template', function (assert) { activity_type_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1935,16 +1920,16 @@ QUnit.test('form activity widget: clic mail template', function (assert) { "we should have one activity"); assert.containsOnce(form, '.o_activity_template_preview', "Activity should contains one mail template"); - testUtils.dom.click(form.$('.o_activity_template_preview[data-template-id=100]')); + await testUtils.dom.click(form.$('.o_activity_template_preview[data-template-id=100]')); assert.containsOnce(form, '.o_mail_activity .o_thread_message', "activity should still be there"); form.destroy(); }); -QUnit.test('form activity widget: schedule activity does not discard changes', function (assert) { +QUnit.test('form activity widget: schedule activity does not discard changes', async function (assert) { assert.expect(1); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -1976,18 +1961,18 @@ QUnit.test('form activity widget: schedule activity does not discard changes', f }); // update value of foo field - testUtils.fields.editInput(form.$('.o_field_widget[name=foo]'), 'new value'); + await testUtils.fields.editInput(form.$('.o_field_widget[name=foo]'), 'new value'); // schedule an activity (this triggers a do_action) - testUtils.dom.click(form.$('.o_chatter_button_schedule_activity')); + await testUtils.dom.click(form.$('.o_chatter_button_schedule_activity')); // save the record - testUtils.form.clickSave(form); + await testUtils.form.clickSave(form); form.destroy(); }); -QUnit.test('form activity widget: mark as done and remove', function (assert) { +QUnit.test('form activity widget: mark as done and remove', async function (assert) { assert.expect(15); var self = this; @@ -2015,7 +2000,7 @@ QUnit.test('form activity widget: mark as done and remove', function (assert) { activity_type_id: 1, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2072,7 +2057,7 @@ QUnit.test('form activity widget: mark as done and remove', function (assert) { "there should be two activities"); // remove activity 1 - testUtils.dom.click(form.$('.o_mail_activity .o_unlink_activity[data-activity-id=1]')); + await testUtils.dom.click(form.$('.o_mail_activity .o_unlink_activity[data-activity-id=1]')); assert.containsOnce(form, '.o_mail_activity .o_thread_message', "there should be one remaining activity"); assert.ok(!form.$('.o_mail_activity .o_unlink_activity[data-activity-id=1]').length, @@ -2081,11 +2066,11 @@ QUnit.test('form activity widget: mark as done and remove', function (assert) { // mark activity done assert.ok(!form.$('.o_mail_thread .o_thread_message').length, "there should be no chatter message"); - testUtils.dom.click(form.$('.o_mail_activity .o_mark_as_done[data-activity-id=2]')); + await testUtils.dom.click(form.$('.o_mail_activity .o_mark_as_done[data-activity-id=2]')); assert.containsOnce(form, '.o_mail_activity_feedback.popover', "a feedback popover should be visible"); $('.o_mail_activity_feedback.popover textarea').val('everything is ok'); // write a feedback - testUtils.dom.click(form.$('.o_activity_popover_done')); + await testUtils.dom.click(form.$('.o_activity_popover_done')); assert.containsNone(form, '.o_mail_activity_feedback.popover') assert.ok(!form.$('.o_mail_activity .o_thread_message').length, "there should be no more activity"); @@ -2096,7 +2081,7 @@ QUnit.test('form activity widget: mark as done and remove', function (assert) { form.destroy(); }); -QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) { +QUnit.test('followers widget: follow/unfollow, edit subtypes', async function (assert) { assert.expect(24); var resID = 2; @@ -2108,7 +2093,7 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) {id: 2, name: "Second subtype", followed: true}, {id: 3, name: "Third subtype", followed: false}, ]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2146,10 +2131,10 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) res_model: 'partner', }); } - return $.when(true); + return Promise.resolve(true); } if (route === '/mail/read_followers') { - return $.when({ + return Promise.resolve({ followers: followers, subtypes: subtypes, // caution, subtype will only be returned if current user is in args follower list @@ -2160,7 +2145,7 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) assert.ok(_.isEqual(args.args[1], [partnerID]), 'should call route for correct partner'); this.data.partner.records[0].message_follower_ids = []; followers = []; - return $.when(true); + return Promise.resolve(true); } if (route === '/web/dataset/call_kw/partner/read') { nbReads++; @@ -2182,7 +2167,7 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) 'should display the "Follow" button'); // click to follow the document - testUtils.dom.click(form.$('.o_followers_follow_button')); + await testUtils.dom.click(form.$('.o_followers_follow_button')); assert.strictEqual(form.$('.o_followers_count').text(), "1", 'should have one follower'); assert.ok(form.$('.o_followers_follow_button.o_followers_following').length, 'should display the "Following/Unfollow" button'); @@ -2194,17 +2179,17 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) 'subtype list should contain 3 subtypes'); assert.containsN(form, '.o_subtypes_list .o_subtype_checkbox:checked', 2, 'two subtypes should be checked by default'); - testUtils.dom.click(form.$('.o_subtypes_list .dropdown-toggle')); + await testUtils.dom.click(form.$('.o_subtypes_list .dropdown-toggle')); assert.ok(form.$('.o_subtypes_list.show').length, 'dropdown should be opened'); - testUtils.dom.click(form.$('.o_subtypes_list .o_subtype input[data-id=2]')); + await testUtils.dom.click(form.$('.o_subtypes_list .o_subtype input[data-id=2]')); assert.ok(form.$('.o_subtypes_list.show').length, 'dropdown should remain opened'); assert.ok(!form.$('.o_subtypes_list .o_subtype_checkbox[data-id=2]:checked').length, 'second subtype should now be unchecked'); // click to unfollow - testUtils.dom.click(form.$('.o_followers_follow_button')); + await testUtils.dom.click(form.$('.o_followers_follow_button')); assert.ok($('.modal').length, 'a confirm modal should be opened'); - testUtils.dom.click($('.modal .modal-footer .btn-primary')); + await testUtils.dom.click($('.modal .modal-footer .btn-primary')); assert.strictEqual(form.$('.o_followers_count').text(), "0", 'should have no followers'); assert.ok(form.$('.o_followers_follow_button.o_followers_notfollow').length, 'should display the "Follow" button'); @@ -2212,7 +2197,7 @@ QUnit.test('followers widget: follow/unfollow, edit subtypes', function (assert) form.destroy(); }); -QUnit.test('followers widget: do not display follower duplications', function (assert) { +QUnit.test('followers widget: do not display follower duplications', async function (assert) { assert.expect(2); this.data.partner.records[0].message_follower_ids = [1]; @@ -2225,7 +2210,7 @@ QUnit.test('followers widget: do not display follower duplications', function (a res_model: 'partner', }]; var def; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2238,7 +2223,7 @@ QUnit.test('followers widget: do not display follower duplications', function (a '', mockRPC: function (route, args) { if (route === '/mail/read_followers') { - return $.when(def).then(function () { + return Promise.resolve(def).then(function () { return { followers: _.filter(followers, function (follower) { return _.contains(args.follower_ids, follower.id); @@ -2266,11 +2251,11 @@ QUnit.test('followers widget: do not display follower duplications', function (a // simulate concurrent calls to read_followers and check that those followers // are not added twice in the dropdown - def = $.Deferred(); + def = testUtils.makeTestPromise(); form.reload(); form.reload(); - def.resolve(); - + await def.resolve(); + await testUtils.nextTick(); assert.strictEqual(form.$('.o_followers_count').text(), '2', "should have 2 followers"); assert.containsN(form, '.o_followers_list .o_partner', 2, @@ -2279,12 +2264,12 @@ QUnit.test('followers widget: do not display follower duplications', function (a form.destroy(); }); -QUnit.test('does not render and crash when destroyed before chat system is ready', function (assert) { +QUnit.test('does not render and crash when destroyed before chat system is ready', async function (assert) { assert.expect(0); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2302,7 +2287,7 @@ QUnit.test('does not render and crash when destroyed before chat system is ready res_id: 2, mockRPC: function (route, args) { if (args.method === 'message_format') { - return $.when([{ + return Promise.resolve([{ attachment_ids: [], body: "", date: "2016-12-20 09:35:40", @@ -2312,7 +2297,7 @@ QUnit.test('does not render and crash when destroyed before chat system is ready }]); } if (route === '/mail/read_followers') { - return $.when({ + return Promise.resolve({ followers: [], subtypes: [], }); @@ -2332,10 +2317,10 @@ QUnit.test('does not render and crash when destroyed before chat system is ready // itself. We simply make sure here that no crashes occur (since the form // view is destroyed, all rpcs will be dropped, and many other mechanisms // relying on events will not work, such as the chat bus) - def.resolve(); + await def.resolve(); }); -QUnit.test('chatter: do not duplicate messages on (un)star message', function (assert) { +QUnit.test('chatter: do not duplicate messages on (un)star message', async function (assert) { assert.expect(4); this.data.partner.records[0].message_ids = [1]; @@ -2352,7 +2337,7 @@ QUnit.test('chatter: do not duplicate messages on (un)star message', function (a res_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2382,7 +2367,7 @@ QUnit.test('chatter: do not duplicate messages on (un)star message', function (a }; var notification = [[false, 'res.partner'], data]; form.call('bus_service', 'trigger', 'notification', [notification]); - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, @@ -2395,12 +2380,12 @@ QUnit.test('chatter: do not duplicate messages on (un)star message', function (a "message should not be starred"); // star message - testUtils.dom.click(form.$('.o_thread_message .o_thread_message_star')); + await testUtils.dom.click(form.$('.o_thread_message .o_thread_message_star')); assert.containsOnce(form, '.o_thread_message', "there should still be a single message in the chatter after starring the message"); // unstar message - testUtils.dom.click(form.$('.o_thread_message .o_thread_message_star')); + await testUtils.dom.click(form.$('.o_thread_message .o_thread_message_star')); assert.containsOnce(form, '.o_thread_message', "there should still be a single message in the chatter after unstarring the message"); @@ -2408,7 +2393,7 @@ QUnit.test('chatter: do not duplicate messages on (un)star message', function (a form.destroy(); }); -QUnit.test('chatter: new messages on document without any "display_name"', function (assert) { +QUnit.test('chatter: new messages on document without any "display_name"', async function (assert) { assert.expect(5); this.data.partner.records[0].message_ids = [1]; @@ -2426,7 +2411,7 @@ QUnit.test('chatter: new messages on document without any "display_name"', funct res_id: 2, }]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2463,7 +2448,7 @@ QUnit.test('chatter: new messages on document without any "display_name"', funct }); this.data.partner.records[0].message_ids.push(2); - form.reload(); + await form.reload(); assert.containsN(form, '.o_thread_message', 2, "should have a two messages in the chatter after reload"); @@ -2476,7 +2461,7 @@ QUnit.test('chatter: new messages on document without any "display_name"', funct form.destroy(); }); -QUnit.test('chatter: suggested partner auto-follow on message post', function (assert) { +QUnit.test('chatter: suggested partner auto-follow on message post', async function (assert) { // need post_refresh 'recipient' to auto-follow suggested recipients // whose checkbox is checked. assert.expect(20); @@ -2507,7 +2492,7 @@ QUnit.test('chatter: suggested partner auto-follow on message post', function (a res_model: 'partner', }); - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2524,7 +2509,7 @@ QUnit.test('chatter: suggested partner auto-follow on message post', function (a res_id: 2, mockRPC: function (route, args) { if (args.method === 'message_get_suggested_recipients') { - return $.when({2: [ + return Promise.resolve({2: [ [ 8, 'DemoUser ', @@ -2567,10 +2552,10 @@ QUnit.test('chatter: suggested partner auto-follow on message post', function (a model: 'partner', res_id: 2, }); - return $.when(messageID); + return Promise.resolve(messageID); } if (route === '/mail/read_followers') { - return $.when({ + return Promise.resolve({ followers: followers, }); } @@ -2596,7 +2581,7 @@ QUnit.test('chatter: suggested partner auto-follow on message post', function (a "should have 'Admin' as follower"); // open composer - testUtils.dom.click(form.$('.o_chatter_button_new_message')); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); assert.isVisible($('.oe_chatter .o_thread_composer'), "chatter should be opened"); assert.strictEqual($('.o_composer_suggested_partners').length, 1, "should display suggested partners"); @@ -2613,7 +2598,7 @@ QUnit.test('chatter: suggested partner auto-follow on message post', function (a // send message form.$('.oe_chatter .o_composer_text_field:first()').val("My first message"); - testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); + await testUtils.dom.click(form.$('.oe_chatter .o_composer_button_send')); assert.strictEqual(form.$('.o_followers_count').text(), "2", "should have a two followers (widget counter)"); @@ -2630,7 +2615,7 @@ QUnit.test('chatter: suggested partner auto-follow on message post', function (a form.destroy(); }); -QUnit.test('chatter: mention prefetched partners (followers & employees)', function (assert) { +QUnit.test('chatter: mention prefetched partners (followers & employees)', async function (assert) { // Note: employees are in prefeteched partner for mentions in chatter when // the module hr is installed. assert.expect(10); @@ -2663,7 +2648,7 @@ QUnit.test('chatter: mention prefetched partners (followers & employees)', funct mention_partner_suggestions: [followerSuggestions.concat(nonFollowerSuggestions)], }; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2680,7 +2665,7 @@ QUnit.test('chatter: mention prefetched partners (followers & employees)', funct res_id: 2, mockRPC: function (route, args) { if (route === '/mail/read_followers') { - return $.when({ + return Promise.resolve({ followers: [{ id: 10, name: 'FollowerUser1', @@ -2698,7 +2683,7 @@ QUnit.test('chatter: mention prefetched partners (followers & employees)', funct }); } if (args.method === 'message_get_suggested_recipients') { - return $.when({2: []}); + return Promise.resolve({2: []}); } if (args.method === 'get_mention_suggestions') { throw new Error('should not fetch partners for mentions'); @@ -2716,14 +2701,14 @@ QUnit.test('chatter: mention prefetched partners (followers & employees)', funct assert.strictEqual(form.$('.o_composer_mention_dropdown').length, 0, "should not show the mention suggestion dropdown"); - form.$('.o_chatter_button_new_message').click(); + await testUtils.dom.click(form.$('.o_chatter_button_new_message')); var $input = form.$('.oe_chatter .o_composer_text_field:first()'); $input.val('@'); // the cursor position must be set for the mention manager to detect that we are mentionning $input[0].selectionStart = 1; $input[0].selectionEnd = 1; $input.trigger('keyup'); - + await testUtils.nextTick(); assert.strictEqual(form.$('.o_composer_mention_dropdown').length, 1, "should show the mention suggestion dropdown"); @@ -2783,7 +2768,7 @@ QUnit.test('fieldmany2many tags email', function (assert) { this.data.partner.records[0].timmy = [12, 14]; // the modals need to be closed before the form view rendering - createAsyncView({ + createView({ View: FormView, model: 'partner', data: this.data, @@ -2799,7 +2784,7 @@ QUnit.test('fieldmany2many tags email', function (assert) { }, mockRPC: function (route, args) { if (args.method ==='read' && args.model === 'partner_type') { - assert.step(args.args[0]); + assert.step(JSON.stringify(args.args[0])); assert.deepEqual(args.args[1] , ['display_name', 'email'], "should read the email"); } return this._super.apply(this, arguments); @@ -2807,9 +2792,10 @@ QUnit.test('fieldmany2many tags email', function (assert) { archs: { 'partner_type,false,form': '
', }, - }).then(function (form) { + }).then(async function (form) { // should read it 3 times (1 with the form view, one with the form dialog and one after save) - assert.verifySteps([[12, 14], [14], [14]]); + assert.verifySteps(['[12,14]', '[14]', '[14]']); + await testUtils.nextTick(); assert.containsN(form, '.o_field_many2manytags[name="timmy"] .badge.o_tag_color_0', 2, "two tags should be present"); var firstTag = form.$('.o_field_many2manytags[name="timmy"] .badge.o_tag_color_0').first(); @@ -2820,25 +2806,27 @@ QUnit.test('fieldmany2many tags email', function (assert) { form.destroy(); done(); }); + testUtils.nextTick().then(function() { + assert.strictEqual($('.modal-body.o_act_window').length, 1, + "there should be one modal opened to edit the empty email"); + assert.strictEqual($('.modal-body.o_act_window input[name="display_name"]').val(), "silver", + "the opened modal should be a form view dialog with the partner_type 14"); + assert.strictEqual($('.modal-body.o_act_window input[name="email"]').length, 1, + "there should be an email field in the modal"); - assert.strictEqual($('.modal-body.o_act_window').length, 1, - "there should be one modal opened to edit the empty email"); - assert.strictEqual($('.modal-body.o_act_window input[name="display_name"]').val(), "silver", - "the opened modal should be a form view dialog with the partner_type 14"); - assert.strictEqual($('.modal-body.o_act_window input[name="email"]').length, 1, - "there should be an email field in the modal"); + // set the email and save the modal (will render the form view) + testUtils.fields.editInput($('.modal-body.o_act_window input[name="email"]'), 'coucou@petite.perruche'); + testUtils.dom.click($('.modal-footer .btn-primary')); + }); - // set the email and save the modal (will render the form view) - testUtils.fields.editInput($('.modal-body.o_act_window input[name="email"]'), 'coucou@petite.perruche'); - testUtils.dom.click($('.modal-footer .btn-primary')); }); -QUnit.test('fieldmany2many tags email (edition)', function (assert) { +QUnit.test('fieldmany2many tags email (edition)', async function (assert) { assert.expect(15); this.data.partner.records[0].timmy = [12]; - var form = createView({ + var form = await createView({ View: FormView, model: 'partner', data: this.data, @@ -2854,7 +2842,7 @@ QUnit.test('fieldmany2many tags email (edition)', function (assert) { }, mockRPC: function (route, args) { if (args.method ==='read' && args.model === 'partner_type') { - assert.step(args.args[0]); + assert.step(JSON.stringify(args.args[0])); assert.deepEqual(args.args[1] , ['display_name', 'email'], "should read the email"); } return this._super.apply(this, arguments); @@ -2864,13 +2852,13 @@ QUnit.test('fieldmany2many tags email (edition)', function (assert) { }, }); - assert.verifySteps([[12]]); + assert.verifySteps(['[12]']); assert.containsOnce(form, '.o_field_many2manytags[name="timmy"] .badge.o_tag_color_0', "should contain one tag"); // add an other existing tag - testUtils.fields.many2one.clickOpenDropdown('timmy'); - testUtils.fields.many2one.clickHighlightedItem('timmy'); + await testUtils.fields.many2one.clickOpenDropdown('timmy'); + await testUtils.fields.many2one.clickHighlightedItem('timmy'); assert.strictEqual($('.modal-body.o_act_window').length, 1, "there should be one modal opened to edit the empty email"); @@ -2880,14 +2868,14 @@ QUnit.test('fieldmany2many tags email (edition)', function (assert) { "there should be an email field in the modal"); // set the email and save the modal (will rerender the form view) - testUtils.fields.editInput($('.modal-body.o_act_window input[name="email"]'), 'coucou@petite.perruche'); - testUtils.dom.click($('.modal-footer .btn-primary')); + await testUtils.fields.editInput($('.modal-body.o_act_window input[name="email"]'), 'coucou@petite.perruche'); + await testUtils.dom.click($('.modal-footer .btn-primary')); assert.containsN(form, '.o_field_many2manytags[name="timmy"] .badge.o_tag_color_0', 2, "should contain the second tag"); // should have read [14] three times: when opening the dropdown, when opening the modal, and // after the save - assert.verifySteps([[12], [14], [14], [14]]); + assert.verifySteps(['[14]', '[14]', '[14]']); form.destroy(); }); diff --git a/addons/mail/static/tests/discuss_mobile_tests.js b/addons/mail/static/tests/discuss_mobile_tests.js index 19e4c8bdd7f..84fd155522c 100644 --- a/addons/mail/static/tests/discuss_mobile_tests.js +++ b/addons/mail/static/tests/discuss_mobile_tests.js @@ -20,68 +20,64 @@ QUnit.module('Discuss in mobile', { }, }); -QUnit.test('mobile basic rendering', function (assert) { +QUnit.test('mobile basic rendering', async function (assert) { // This is a very basic first test for the client action. However, with // the chat_service, it is hard to override RPCs (for instance, the // /mail/client_action route is always called when the test suite is // launched), and we must wait for this RPC to be done before starting to // test the interface. This should be refactored to facilitate the testing. assert.expect(16); - var done = assert.async(); - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, - debug: true, - }).then(function (discuss) { - // test basic rendering in mobile - assert.containsOnce(discuss, '.o_mail_discuss_content .o_mail_no_content', - "should display the no content message"); - assert.containsOnce(discuss, '.o_mail_mobile_tabs', - "should have rendered the tabs"); - assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=mailbox_inbox]'),'active', - "should be in inbox tab"); - assert.containsOnce(discuss, '.o_mail_discuss_mobile_mailboxes_buttons:visible', - "inbox/starred buttons should be visible"); - assert.hasClass(discuss.$('.o_mail_discuss_mobile_mailboxes_buttons .o_mailbox_inbox_item[data-type=mailbox_inbox]'),'btn-primary', - "should be in inbox"); - assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none', - "should have invisible button 'New Message'"); - assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none', - "should have invisible button 'New Channel'"); - - // move to 'Chat' tab - testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]')); - assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]'),'active', - "should be in 'Chat' tab"); - assert.containsNone(discuss, '.o_mail_discuss_content .o_mail_no_content', - "should display the no content message"); - assert.strictEqual($('.o_mail_discuss_button_dm_chat').length, 1, - "should have a button to open DM chat in 'Chat' tab"); - assert.doesNotHaveClass($('.o_mail_discuss_button_dm_chat'), 'd-none', - "should be visible in 'Chat' tab"); - assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none', - "should have invisible button 'New Channel' in 'Chat' tab"); - testUtils.dom.click($('.o_mail_discuss_button_dm_chat')); - assert.containsOnce(discuss, '.o_mail_add_thread input:visible', - "should display the input to add a channel"); - - // move to 'Channels' tab - testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=multi_user_channel]')); - assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none', - "should have invisible button 'New Message' in 'Channels' tab"); - assert.doesNotHaveClass($('.o_mail_discuss_button_multi_user_channel'), 'd-none', - "should have visible button 'New Channel' in 'Channels' tab"); - testUtils.dom.click($('.o_mail_discuss_button_multi_user_channel')); - assert.containsOnce(discuss, '.o_mail_add_thread input:visible', - "should display the input to add a channel"); - - discuss.destroy(); - done(); }); + // test basic rendering in mobile + assert.containsOnce(discuss, '.o_mail_discuss_content .o_mail_no_content', + "should display the no content message"); + assert.containsOnce(discuss, '.o_mail_mobile_tabs', + "should have rendered the tabs"); + assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=mailbox_inbox]'),'active', + "should be in inbox tab"); + assert.containsOnce(discuss, '.o_mail_discuss_mobile_mailboxes_buttons:visible', + "inbox/starred buttons should be visible"); + assert.hasClass(discuss.$('.o_mail_discuss_mobile_mailboxes_buttons .o_mailbox_inbox_item[data-type=mailbox_inbox]'),'btn-primary', + "should be in inbox"); + assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none', + "should have invisible button 'New Message'"); + assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none', + "should have invisible button 'New Channel'"); + + // move to 'Chat' tab + await testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]')); + assert.hasClass(discuss.$('.o_mail_mobile_tab[data-type=dm_chat]'),'active', + "should be in 'Chat' tab"); + assert.containsNone(discuss, '.o_mail_discuss_content .o_mail_no_content', + "should display the no content message"); + assert.strictEqual($('.o_mail_discuss_button_dm_chat').length, 1, + "should have a button to open DM chat in 'Chat' tab"); + assert.doesNotHaveClass($('.o_mail_discuss_button_dm_chat'), 'd-none', + "should be visible in 'Chat' tab"); + assert.hasClass($('.o_mail_discuss_button_multi_user_channel'),'d-none', + "should have invisible button 'New Channel' in 'Chat' tab"); + await testUtils.dom.click($('.o_mail_discuss_button_dm_chat')); + assert.containsOnce(discuss, '.o_mail_add_thread input:visible', + "should display the input to add a channel"); + + // move to 'Channels' tab + await testUtils.dom.click(discuss.$('.o_mail_mobile_tab[data-type=multi_user_channel]')); + assert.hasClass($('.o_mail_discuss_button_dm_chat'),'d-none', + "should have invisible button 'New Message' in 'Channels' tab"); + assert.doesNotHaveClass($('.o_mail_discuss_button_multi_user_channel'), 'd-none', + "should have visible button 'New Channel' in 'Channels' tab"); + await testUtils.dom.click($('.o_mail_discuss_button_multi_user_channel') ); + assert.containsOnce(discuss, '.o_mail_add_thread input:visible', + "should display the input to add a channel"); + + discuss.destroy(); }); QUnit.test('on_{attach/detach}_callback', function (assert) { diff --git a/addons/mail/static/tests/discuss_moderation_tests.js b/addons/mail/static/tests/discuss_moderation_tests.js index 9c3807527ce..8b50aae0b53 100644 --- a/addons/mail/static/tests/discuss_moderation_tests.js +++ b/addons/mail/static/tests/discuss_moderation_tests.js @@ -145,7 +145,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct data: this.data, services: this.services, }) - .then(function (discuss) { + .then(async function (discuss) { var $moderationBox = discuss.$( '.o_mail_discuss_item' + '[data-thread-id="mailbox_moderation"]'); @@ -156,7 +156,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct "the mailbox counter of the moderation mailbox should display '1'"); // 1. go to moderation mailbox - testUtils.dom.click($moderationBox); + await testUtils.dom.click($moderationBox); var $message = discuss.$('.o_thread_message'); // check message assert.strictEqual($message.length, 1, @@ -210,7 +210,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct 'the moderate button "Discard" should be invisible by default'); // click on message moderation checkbox - testUtils.dom.click($message.find('.moderation_checkbox')); + await testUtils.dom.click($message.find('.moderation_checkbox')); assert.ok($message.find('.moderation_checkbox').prop('checked'), "the moderation checkbox should become checked after click"); // check select all (disabled) / unselect all buttons (enabled) @@ -227,7 +227,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct 'the moderate button "Discard" should become visible'); // 2. go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); $message = discuss.$('.o_thread_message'); // check correct message assert.strictEqual($message.length, 1, @@ -289,12 +289,12 @@ QUnit.test('moderator: accept pending moderation message', function (assert) { return this._super.apply(this, arguments); }, }) - .then(function (discuss) { + .then(async function (discuss) { // 1. go to moderation box var $moderationBox = discuss.$( '.o_mail_discuss_item' + '[data-thread-id="mailbox_moderation"]'); - testUtils.dom.click($moderationBox); + await testUtils.dom.click($moderationBox); // check there is a message to moderate var $message = discuss.$('.o_thread_message'); assert.strictEqual($message.length, 1, @@ -304,7 +304,7 @@ QUnit.test('moderator: accept pending moderation message', function (assert) { assert.strictEqual($message.find('.moderation_checkbox').length, 1, "the message should have a moderation checkbox"); // accept the message pending moderation - testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="accept"]')); + await testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="accept"]')); assert.verifySteps(['moderate']); // stop the fadeout animation and immediately remove the element @@ -313,7 +313,7 @@ QUnit.test('moderator: accept pending moderation message', function (assert) { "should now have no message displayed in moderation box"); // 2. go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); $message = discuss.$('.o_thread_message'); // check message is there and has no moderate checkbox assert.strictEqual($message.length, 1, @@ -379,12 +379,12 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio return this._super.apply(this, arguments); }, }) - .then(function (discuss) { + .then(async function (discuss) { // 1. go to moderation box var $moderationBox = discuss.$( '.o_mail_discuss_item' + '[data-thread-id="mailbox_moderation"]'); - testUtils.dom.click($moderationBox); + await testUtils.dom.click($moderationBox); // check there is a message to moderate var $message = discuss.$('.o_thread_message'); assert.strictEqual($message.length, 1, @@ -394,7 +394,7 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio assert.strictEqual($message.find('.moderation_checkbox').length, 1, "the message should have a moderation checkbox"); // reject the message pending moderation - testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="reject"]')); + await testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="reject"]')); // check reject dialog prompt assert.strictEqual($('.modal-dialog').length, 1, @@ -419,7 +419,7 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio "should have a send button on the reject dialog"); // send mesage - testUtils.dom.click($('.modal-footer button')); + await testUtils.dom.click($('.modal-footer button')); assert.verifySteps(['moderate']); // // stop the fadeout animation and immediately remove the element @@ -428,7 +428,7 @@ QUnit.test('moderator: reject pending moderation message (reject with explanatio "should now have no message displayed in moderation box"); // 2. go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); assert.containsNone(discuss, '.o_thread_message') discuss.destroy(); @@ -482,12 +482,12 @@ QUnit.test('moderator: discard pending moderation message (reject without explan return this._super.apply(this, arguments); }, }) - .then(function (discuss) { + .then(async function (discuss) { // 1. go to moderation box var $moderationBox = discuss.$( '.o_mail_discuss_item' + '[data-thread-id="mailbox_moderation"]'); - testUtils.dom.click($moderationBox); + await testUtils.dom.click($moderationBox); // check there is a message to moderate var $message = discuss.$('.o_thread_message'); assert.strictEqual($message.length, 1, @@ -497,7 +497,7 @@ QUnit.test('moderator: discard pending moderation message (reject without explan assert.strictEqual($message.find('.moderation_checkbox').length, 1, "the message should have a moderation checkbox"); // discard the message pending moderation - testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="discard"]')); + await testUtils.dom.click(discuss.$('.o_thread_message_moderation[data-decision="discard"]')); // check discard dialog prompt assert.strictEqual($('.modal-dialog').length, 1, @@ -513,7 +513,7 @@ QUnit.test('moderator: discard pending moderation message (reject without explan "should have a cancel button in the dialog for discard"); // discard mesage - testUtils.dom.click($('.modal-footer button.btn-primary')); + await testUtils.dom.click($('.modal-footer button.btn-primary')); assert.verifySteps(['moderate']); // stop the fadeout animation and immediately remove the element @@ -522,7 +522,7 @@ QUnit.test('moderator: discard pending moderation message (reject without explan "should now have no message displayed in moderation box"); // 2. go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); assert.containsNone(discuss, '.o_thread_message') discuss.destroy(); @@ -534,7 +534,7 @@ QUnit.test('author: send message in moderated channel', function (assert) { assert.expect(4); var done = assert.async(); - var messagePostDef = $.Deferred(); + var messagePostDef = testUtils.makeTestPromise(); this.data.initMessaging = { channel_slots: { @@ -554,7 +554,7 @@ QUnit.test('author: send message in moderated channel', function (assert) { params: {}, data: this.data, services: this.services, - mockRPC: function (route, args) { + mockRPC: async function (route, args) { if (args.method === 'message_post') { var message = { id: 100, @@ -572,9 +572,10 @@ QUnit.test('author: send message in moderated channel', function (assert) { }; var notification = [metaData, notifData]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); messagePostDef.resolve(); - return $.when(message.id); + return Promise.resolve(message.id); } return this._super.apply(this, arguments); }, @@ -582,14 +583,14 @@ QUnit.test('author: send message in moderated channel', function (assert) { partner_id: 2, }, }) - .then(function (discuss) { + .then(async function (discuss) { objectDiscuss = discuss; // go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); // post a message discuss.$('.o_composer_input textarea').first().val("some text"); - testUtils.dom.click(discuss.$('.o_composer_send button')); + await testUtils.dom.click(discuss.$('.o_composer_send button')); messagePostDef .then(function () { @@ -646,10 +647,10 @@ QUnit.test('author: sent message accepted in moderated channel', function (asser partner_id: 2, }, }) - .then(function (discuss) { + .then(async function (discuss) { // go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); // check message is pending var $message = discuss.$('.o_thread_message'); assert.strictEqual($message.length, 1, @@ -674,7 +675,7 @@ QUnit.test('author: sent message accepted in moderated channel', function (asser var metaData = [dbName, 'mail.channel']; var notification = [metaData, messageData]; discuss.call('bus_service', 'trigger', 'notification', [notification]); - + await testUtils.nextTick(); // check message is accepted $message = discuss.$('.o_thread_message'); assert.strictEqual($message.length, 1, @@ -727,10 +728,10 @@ QUnit.test('author: sent message rejected in moderated channel', function (asser partner_id: 2, }, }) - .then(function (discuss) { + .then(async function (discuss) { // go to channel 'general' - testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); + await testUtils.dom.click(discuss.$('.o_mail_discuss_item[data-thread-id="1"]')); // check message is pending var $message = discuss.$('.o_thread_message'); assert.strictEqual($message.length, 1, @@ -751,7 +752,7 @@ QUnit.test('author: sent message rejected in moderated channel', function (asser var metaData = [dbName, 'res.partner']; var notification = [metaData, notifData]; discuss.call('bus_service', 'trigger', 'notification', [notification]); - + await testUtils.nextTick(); // // check no message assert.containsNone(discuss, '.o_thread_message', "message should be removed from channel after reject"); @@ -761,12 +762,11 @@ QUnit.test('author: sent message rejected in moderated channel', function (asser }); }); -QUnit.test('no crash when load-more fetching "accepted" message twice', function (assert) { +QUnit.test('no crash when load-more fetching "accepted" message twice', async function (assert) { // This tests requires discuss not loading more messages due to having less // messages to fetch than available height. This justifies we simply do not // patch FETCH_LIMIT to 1, as it would detect that more messages could fit // the empty space (it behaviour is linked to "auto load more"). - var done = assert.async(); assert.expect(2); var FETCH_LIMIT = Thread.prototype._FETCH_LIMIT; @@ -799,7 +799,7 @@ QUnit.test('no crash when load-more fetching "accepted" message twice', function }; var count = 0; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -811,33 +811,32 @@ QUnit.test('no crash when load-more fetching "accepted" message twice', function count++; if (count === 1) { // inbox message_fetch - return $.when([]); + return Promise.resolve([]); } // general message_fetch - return $.when(messageData); + return Promise.resolve(messageData); } return this._super.apply(this, arguments); }, - }) - .then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.strictEqual($general.attr('title'), 'general', - "should have the title 'general'"); - - // click on general - $general.click(); - - // simulate search - discuss.trigger_up('search', { - domains: [['author_id', '=', 100]], - }); - - discuss.destroy(); - done(); }); + + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.strictEqual($general.attr('title'), 'general', + "should have the title 'general'"); + + // click on general + await testUtils.dom.click($general); + + // simulate search + discuss.trigger_up('search', { + domains: [['author_id', '=', 100]], + }); + await testUtils.nextTick(); + + discuss.destroy(); }); }); diff --git a/addons/mail/static/tests/discuss_seen_indicator_tests.js b/addons/mail/static/tests/discuss_seen_indicator_tests.js index 4db61a56ed1..46f5e0b5723 100644 --- a/addons/mail/static/tests/discuss_seen_indicator_tests.js +++ b/addons/mail/static/tests/discuss_seen_indicator_tests.js @@ -72,13 +72,13 @@ QUnit.module('Discuss (Seen Indicator)', { }; var notification = [[false, 'mail.channel', params.channelID], data]; params.widget.call('bus_service', 'trigger', 'notification', [notification]); + return testUtils.nextTick(); }; }, }); -QUnit.test('2 members: 1 myself message not received initially', function (assert) { +QUnit.test('2 members: 1 myself message not received initially', async function (assert) { assert.expect(2); - var done = assert.async(); var dmChat = { id: 1, @@ -123,30 +123,27 @@ QUnit.test('2 members: 1 myself message not received initially', function (asser res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, - }).then(function (discuss) { - // click on DM chat - var $dmChat = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($dmChat); - - assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - discuss.destroy(); - done(); }); + // click on general + var $dmChat = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($dmChat); + + assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + discuss.destroy(); }); -QUnit.test('2 members: 1 myself message received by everyone initially', function (assert) { +QUnit.test('2 members: 1 myself message received by everyone initially', async function (assert) { assert.expect(4); - var done = assert.async(); var dmChat = { id: 1, @@ -191,35 +188,32 @@ QUnit.test('2 members: 1 myself message received by everyone initially', functio res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, - }).then(function (discuss) { - // click on DM chat - var $dmChat = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($dmChat); - var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should display seen icon on message"); - assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().trim(), "Received by Everyone"); - - discuss.destroy(); - done(); }); + // click on DM chat + var $dmChat = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($dmChat); + var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should display seen icon on message"); + assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().trim(), "Received by Everyone"); + + discuss.destroy(); }); -QUnit.test('2 members: 1 myself message seen by everyone initially', function (assert) { +QUnit.test('2 members: 1 myself message seen by everyone initially', async function (assert) { assert.expect(4); - var done = assert.async(); var dmChat = { id: 1, @@ -264,37 +258,33 @@ QUnit.test('2 members: 1 myself message seen by everyone initially', function (a res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, - }).then(function (discuss) { - // click on DM chat - var $dmChat = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($dmChat); - var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should display seen icon on message"); - assert.containsN($seenIcon, '.fa-check', 2, "should display 2 checks"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().trim(), "Seen by Everyone"); - - discuss.destroy(); - done(); }); + // click on DM chat + var $dmChat = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($dmChat); + var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should display seen icon on message"); + assert.containsN($seenIcon, '.fa-check', 2, "should display 2 checks"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().trim(), "Seen by Everyone"); + + discuss.destroy(); }); -QUnit.test('2 members: 1 myself message received by everyone (initially not received)', function (assert) { +QUnit.test('2 members: 1 myself message received by everyone (initially not received)', async function (assert) { assert.expect(6); - var done = assert.async(); - var self = this; var dmChat = { id: 1, channel_type: "chat", @@ -338,49 +328,45 @@ QUnit.test('2 members: 1 myself message received by everyone (initially not rece res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, - }).then(function (discuss) { - // click on DM chat - var $dmChat = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($dmChat); - - assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 2, - lastMessageID: 100, - widget: discuss, - }); - - var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should display seen icon on message"); - assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().trim(), "Received by Everyone"); - - discuss.destroy(); - done(); }); + // click on DM chat + var $dmChat = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($dmChat); + + assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 2, + lastMessageID: 100, + widget: discuss, + }); + + var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should display seen icon on message"); + assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().trim(), "Received by Everyone"); + + discuss.destroy(); }); -QUnit.test('2 members: 1 myself message seen by everyone (initially not received)', function (assert) { +QUnit.test('2 members: 1 myself message seen by everyone (initially not received)', async function (assert) { assert.expect(6); - var done = assert.async(); - var self = this; var dmChat = { id: 1, channel_type: "chat", @@ -424,49 +410,45 @@ QUnit.test('2 members: 1 myself message seen by everyone (initially not received res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, - }).then(function (discuss) { - // click on DM chat - var $dmChat = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($dmChat); - - assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 2, - lastMessageID: 100, - widget: discuss, - }); - - var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should display seen icon on message"); - assert.containsN($seenIcon, '.fa-check', 2, "should display 2 checks"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().trim(), "Seen by Everyone"); - - discuss.destroy(); - done(); }); + // click on DM chat + var $dmChat = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($dmChat); + + assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 2, + lastMessageID: 100, + widget: discuss, + }); + + var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should display seen icon on message"); + assert.containsN($seenIcon, '.fa-check', 2, "should display 2 checks"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().trim(), "Seen by Everyone"); + + discuss.destroy(); }); -QUnit.test('2 members: 1 other message seen by me (initially not received)', function (assert) { +QUnit.test('2 members: 1 other message seen by me (initially not received)', async function (assert) { assert.expect(3); - var done = assert.async(); - var self = this; var dmChat = { id: 1, channel_type: "chat", @@ -510,44 +492,41 @@ QUnit.test('2 members: 1 other message seen by me (initially not received)', fun res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, session: { partner_id: 3 }, - }).then(function (discuss) { - // click on DM chat - var $dmChat = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($dmChat); - - assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 3, - lastMessageID: 100, - widget: discuss, - }); - - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - discuss.destroy(); - done(); }); + // click on DM chat + var $dmChat = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($dmChat); + + assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 3, + lastMessageID: 100, + widget: discuss, + }); + + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + discuss.destroy(); }); -QUnit.skip('3 members: 1 myself message received by some initially', function (assert) { +QUnit.skip('3 members: 1 myself message received by some initially', async function (assert) { // IMPORTANT: This test make sense if this feature is on multi user channels, // which was the case in case at some point. It has been disabled for // performance reasons. We are unsure if this will come back in the future, // so we keep to tests in 'skip' mode in the mean time. assert.expect(4); - var done = assert.async(); var members = [{ id: 2, @@ -601,7 +580,7 @@ QUnit.skip('3 members: 1 myself message received by some initially', function (a res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -610,31 +589,29 @@ QUnit.skip('3 members: 1 myself message received by some initially', function (a session: { partner_id: 3 }, mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { - return $.when(members); + return Promise.resolve(members); } return this._super.apply(this, arguments); }, - }).then(function (discuss) { - // click on general - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should display seen icon on message"); - assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), - "Receivedby:Someone(someone@example.com)"); - - discuss.destroy(); - done(); }); + // click on general + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should display seen icon on message"); + assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), + "Receivedby:Someone(someone@example.com)"); + + discuss.destroy(); }); -QUnit.skip('several members: 1 myself message seen by everyone (initially not received)', function (assert) { +QUnit.skip('several members: 1 myself message seen by everyone (initially not received)', async function (assert) { // IMPORTANT: This test make sense if this feature is on multi user channels, // which was the case in case at some point. It has been disabled for // performance reasons. We are unsure if this will come back in the future, @@ -643,9 +620,7 @@ QUnit.skip('several members: 1 myself message seen by everyone (initially not re // seen icon and content should change for my messages based on other users // actions (received and/or seen the message) assert.expect(26); - var done = assert.async(); - var self = this; var members = [{ id: 2, name: "Someone", @@ -706,7 +681,7 @@ QUnit.skip('several members: 1 myself message seen by everyone (initially not re res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -715,133 +690,131 @@ QUnit.skip('several members: 1 myself message seen by everyone (initially not re session: { partner_id: 3 }, mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { - return $.when(members); + return Promise.resolve(members); } return this._super.apply(this, arguments); }, - }).then(function (discuss) { - // click on general - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - - assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 2, - lastMessageID: 100, - widget: discuss, - }); - - var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should display seen icon on message"); - assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), - "Receivedby:Someone(someone@example.com)"); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 4, - lastMessageID: 100, - widget: discuss, - }); - - $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should still display seen icon on message"); - assert.containsOnce($seenIcon, '.fa-check', "should still display a single check"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), - "Receivedby:Someone(someone@example.com)Other(other@example.com)"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 2, - lastMessageID: 100, - widget: discuss, - }); - - $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should still display seen icon on message"); - assert.containsN($seenIcon, '.fa-check', 2, "should now display 2 checks"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), - "Seenby:Someone(someone@example.com)Receivedby:Other(other@example.com)"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 4, - lastMessageID: 100, - widget: discuss, - }); - - $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should still display seen icon on message"); - assert.containsN($seenIcon, '.fa-check', 2, "should still display 2 checks"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), - "Seenby:Someone(someone@example.com)Other(other@example.com)"); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 5, - lastMessageID: 100, - widget: discuss, - }); - - $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should still display seen icon on message"); - assert.containsN($seenIcon, '.fa-check', 2, "should still display 2 checks"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), - "Seenby:Someone(someone@example.com)Other(other@example.com)ReceivedbyEveryone"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 5, - lastMessageID: 100, - widget: discuss, - }); - - $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); - assert.isVisible($seenIcon, "should still display seen icon on message"); - assert.containsN($seenIcon, '.fa-check', 2, "should still display 2 checks"); - - testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); - $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); - assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); - assert.strictEqual($seenIconContent.text().trim(), - "Seen by Everyone"); - - discuss.destroy(); - done(); }); + // click on general + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + + assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 2, + lastMessageID: 100, + widget: discuss, + }); + + var $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should display seen icon on message"); + assert.containsOnce($seenIcon, '.fa-check', "should display a single check"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + var $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), + "Receivedby:Someone(someone@example.com)"); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 4, + lastMessageID: 100, + widget: discuss, + }); + + $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should still display seen icon on message"); + assert.containsOnce($seenIcon, '.fa-check', "should still display a single check"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), + "Receivedby:Someone(someone@example.com)Other(other@example.com)"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 2, + lastMessageID: 100, + widget: discuss, + }); + + $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should still display seen icon on message"); + assert.containsN($seenIcon, '.fa-check', 2, "should now display 2 checks"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), + "Seenby:Someone(someone@example.com)Receivedby:Other(other@example.com)"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 4, + lastMessageID: 100, + widget: discuss, + }); + + $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should still display seen icon on message"); + assert.containsN($seenIcon, '.fa-check', 2, "should still display 2 checks"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), + "Seenby:Someone(someone@example.com)Other(other@example.com)"); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 5, + lastMessageID: 100, + widget: discuss, + }); + + $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should still display seen icon on message"); + assert.containsN($seenIcon, '.fa-check', 2, "should still display 2 checks"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().replace(/\s/g, ""), + "Seenby:Someone(someone@example.com)Other(other@example.com)ReceivedbyEveryone"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 5, + lastMessageID: 100, + widget: discuss, + }); + + $seenIcon = discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'); + assert.isVisible($seenIcon, "should still display seen icon on message"); + assert.containsN($seenIcon, '.fa-check', 2, "should still display 2 checks"); + + await testUtils.dom.triggerMouseEvent($seenIcon, 'mouseover'); + $seenIconContent = $('.o_mail_thread_message_seen_icon_content'); + assert.isVisible($seenIconContent, "should still display content on seen icon mouseover"); + assert.strictEqual($seenIconContent.text().trim(), + "Seen by Everyone"); + + discuss.destroy(); }); -QUnit.skip('several members: other message seen by everyone (initially not received)', function (assert) { +QUnit.skip('several members: other message seen by everyone (initially not received)', async function (assert) { // IMPORTANT: This test make sense if this feature is on multi user channels, // which was the case in case at some point. It has been disabled for // performance reasons. We are unsure if this will come back in the future, @@ -849,9 +822,7 @@ QUnit.skip('several members: other message seen by everyone (initially not recei // seen icon should never be visible for message of others assert.expect(9); - var done = assert.async(); - var self = this; var members = [{ id: 2, name: "Someone", @@ -912,7 +883,7 @@ QUnit.skip('several members: other message seen by everyone (initially not recei res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -921,103 +892,99 @@ QUnit.skip('several members: other message seen by everyone (initially not recei session: { partner_id: 3 }, mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { - return $.when(members); + return Promise.resolve(members); } return this._super.apply(this, arguments); }, - }).then(function (discuss) { - // click on general - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - - assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 3, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 3, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 4, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 2, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 4, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - self.simulate({ - action: 'received', - channelID: 1, - partnerID: 5, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 5, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), - "should not display seen icon on message of other users"); - - discuss.destroy(); - done(); }); + // click on general + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + + assert.isVisible(discuss.$('.o_thread_message[data-message-id=100]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 3, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 3, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 4, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 2, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 4, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + await this.simulate({ + action: 'received', + channelID: 1, + partnerID: 5, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 5, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]'), + "should not display seen icon on message of other users"); + + discuss.destroy(); }); -QUnit.skip('several members: only show seen icons from last message seen by everyone', function (assert) { +QUnit.skip('several members: only show seen icons from last message seen by everyone', async function (assert) { // IMPORTANT: This test make sense if this feature is on multi user channels, // which was the case in case at some point. It has been disabled for // performance reasons. We are unsure if this will come back in the future, // so we keep to tests in 'skip' mode in the mean time. assert.expect(12); - var done = assert.async(); - var self = this; var members = [{ id: 2, name: "Someone", @@ -1092,7 +1059,7 @@ QUnit.skip('several members: only show seen icons from last message seen by ever res_id: 1, }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -1101,55 +1068,53 @@ QUnit.skip('several members: only show seen icons from last message seen by ever session: { partner_id: 3 }, mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { - return $.when(members); + return Promise.resolve(members); } return this._super.apply(this, arguments); }, - }).then(function (discuss) { - // click on general - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); - assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 2, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); - assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); - assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 4, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); - assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); - assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - self.simulate({ - action: 'seen', - channelID: 1, - partnerID: 5, - lastMessageID: 100, - widget: discuss, - }); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); - assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); - assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); - - discuss.destroy(); - done(); }); + // click on general + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); + assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 2, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); + assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); + assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 4, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); + assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); + assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + await this.simulate({ + action: 'seen', + channelID: 1, + partnerID: 5, + lastMessageID: 100, + widget: discuss, + }); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=98]')); + assert.containsNone(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=99]')); + assert.isVisible(discuss.$('.o_mail_thread_message_seen_icon[data-message-id=100]')); + + discuss.destroy(); }); }); diff --git a/addons/mail/static/tests/discuss_tests.js b/addons/mail/static/tests/discuss_tests.js index 02cb977b246..7dc8be4320c 100644 --- a/addons/mail/static/tests/discuss_tests.js +++ b/addons/mail/static/tests/discuss_tests.js @@ -83,73 +83,66 @@ QUnit.module('Discuss', { } }); -QUnit.test('basic rendering', function (assert) { +QUnit.test('basic rendering', async function (assert) { assert.expect(5); - var done = assert.async(); - createDiscuss({ + var discuss = await 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.containsOnce(discuss, '.o_mail_discuss_content', - "should have rendered the content"); - assert.containsOnce(discuss, '.o_mail_no_content', - "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(); }); + + // test basic rendering + var $sidebar = discuss.$('.o_mail_discuss_sidebar'); + assert.strictEqual($sidebar.length, 1, + "should have rendered a sidebar"); + + assert.containsOnce(discuss, '.o_mail_discuss_content', + "should have rendered the content"); + assert.containsOnce(discuss, '.o_mail_no_content', + "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(); }); -QUnit.test('searchview options visibility', function (assert) { +QUnit.test('searchview options visibility', async function (assert) { assert.expect(5); - var done = assert.async(); - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, - }) - .then(function (discuss) { - assert.containsOnce(discuss, '.o_control_panel .o_search_options', - "should have search options"); - assert.hasClass(discuss.$('.o_control_panel .o_searchview_more.fa'), 'fa-search-minus', - "should have a button to toggle search options"); - - assert.isVisible(discuss.$('.o_control_panel .o_search_options'), - "search options should be visible by default"); - testUtils.dom.click(discuss.$('.o_control_panel .o_searchview_more.fa')); - assert.hasClass(discuss.$('.o_control_panel .o_searchview_more.fa'), 'fa-search-plus', - "should have a button to toggle search options"); - assert.isNotVisible(discuss.$('.o_control_panel .o_search_options'), - "search options should be hidden after clicking on search option toggler"); - - discuss.destroy(); - done(); }); + + assert.containsOnce(discuss, '.o_control_panel .o_search_options', + "should have search options"); + assert.hasClass(discuss.$('.o_control_panel .o_searchview_more.fa'), 'fa-search-minus', + "should have a button to toggle search options"); + + assert.isVisible(discuss.$('.o_control_panel .o_search_options'), + "search options should be visible by default"); + await testUtils.dom.click(discuss.$('.o_control_panel .o_searchview_more.fa')); + assert.hasClass(discuss.$('.o_control_panel .o_searchview_more.fa'), 'fa-search-plus', + "should have a button to toggle search options"); + assert.isNotVisible(discuss.$('.o_control_panel .o_search_options'), + "search options should be hidden after clicking on search option toggler"); + + discuss.destroy(); }); -QUnit.test('searchview filter messages', function (assert) { +QUnit.test('searchview filter messages', async function (assert) { assert.expect(10); - var done = assert.async(); this.data['mail.message'].records = [{ author_id: [5, 'Demo User'], @@ -165,7 +158,7 @@ QUnit.test('searchview filter messages', function (assert) { needaction_partner_ids: [3], }]; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -180,8 +173,7 @@ QUnit.test('searchview filter messages', function (assert) { '' + '', }, - }) - .then(function (discuss) { + }); assert.containsN(discuss, '.o_thread_message', 2, "there should be two messages in the inbox mailbox"); assert.strictEqual($('.o_searchview_input').length, 1, @@ -191,8 +183,9 @@ QUnit.test('searchview filter messages', function (assert) { // interact with searchview so that there is only once message $('.o_searchview_input').val("ab").trigger('keyup'); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.ENTER })); - + await testUtils.nextTick(); assert.strictEqual($('.o_searchview_facet').length, 1, "the searchview should have a facet"); assert.strictEqual($('.o_facet_values').text().trim(), 'ab', @@ -201,9 +194,11 @@ QUnit.test('searchview filter messages', function (assert) { "there should be a single message after filter"); // interact with search view so that there are no matching messages - testUtils.dom.click($('.o_facet_remove')); + await testUtils.dom.click($('.o_facet_remove')); $('.o_searchview_input').val("abcd").trigger('keyup'); + await testUtils.nextTick(); $('.o_searchview_input').trigger($.Event('keydown', { which: $.ui.keyCode.ENTER })); + await testUtils.nextTick(); assert.strictEqual($('.o_searchview_facet').length, 1, "the searchview should have a facet"); @@ -216,17 +211,15 @@ QUnit.test('searchview filter messages', function (assert) { "should display that there are no matching messages"); discuss.destroy(); - done(); - }); + }); -QUnit.test('unescape channel name in the sidebar', function (assert) { +QUnit.test('unescape channel name in the sidebar', async function (assert) { // When the user creates a channel, the channel's name is escaped, this in // order to prevent XSS attacks. However, the user should see visually the // unescaped name of the channel. For instance, when the user creates a // channel named "R&D", he should see "R&D" and not "R&D". assert.expect(2); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -238,34 +231,31 @@ QUnit.test('unescape channel name in the sidebar', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, - }) - .then(function (discuss) { - var $sidebar = discuss.$('.o_mail_discuss_sidebar'); - - var $channel = $sidebar.find('.o_mail_discuss_item[data-thread-id=1]'); - assert.strictEqual($channel.length, 1, - "should have the channel item for channel 1 in the sidebar"); - assert.strictEqual($channel.find('.o_thread_name').text().replace(/\s/g, ''), - "#R&D", - "should have unescaped channel name in the sidebar"); - - discuss.destroy(); - done(); }); + + var $sidebar = discuss.$('.o_mail_discuss_sidebar'); + + var $channel = $sidebar.find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($channel.length, 1, + "should have the channel item for channel 1 in the sidebar"); + assert.strictEqual($channel.find('.o_thread_name').text().replace(/\s/g, ''), + "#R&D", + "should have unescaped channel name in the sidebar"); + + discuss.destroy(); }); -QUnit.test('@ mention in channel', function (assert) { +QUnit.test('@ mention in channel', async function (assert) { assert.expect(34); - var done = assert.async(); - var fetchListenersDef = $.Deferred(); - var receiveMessageDef = $.Deferred(); + var fetchListenersDef = testUtils.makeTestPromise(); + var receiveMessageDef = testUtils.makeTestPromise(); this.data.initMessaging = { channel_slots: { @@ -276,9 +266,8 @@ QUnit.test('@ mention in channel', function (assert) { }], }, }; - var objectDiscuss; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -287,7 +276,7 @@ QUnit.test('@ mention in channel', function (assert) { mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { fetchListenersDef.resolve(); - return $.when([ + return Promise.resolve([ {id: 1, name: 'Admin'}, {id: 2, name: 'TestUser'}, {id: 3, name: 'DemoUser'} @@ -302,142 +291,133 @@ QUnit.test('@ mention in channel', function (assert) { var notification = [[false, 'mail.channel', 1], data]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); receiveMessageDef.resolve(); - return $.when(42); + return Promise.resolve(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]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.hasAttrValue($general, 'title', 'general', - "should have the title 'general'"); - - // click on general - testUtils.dom.click($general); - var $input = discuss.$('textarea.o_composer_text_field').first(); - assert.ok($input.length, "should display a composer input"); - - // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' - $input.focus(); - $input.val("@"); - $input.trigger('keyup'); - - fetchListenersDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - assert.containsOnce(discuss, '.dropup.o_composer_mention_dropdown.show', - "dropup menu for partner mentions should be open"); - - var $mentionPropositions = discuss.$('.o_mention_proposition'); - assert.strictEqual($mentionPropositions.length, 3, - "should display 3 partner mention propositions"); - - var $mention1 = $mentionPropositions.first(); - var $mention2 = $mentionPropositions.first().next(); - var $mention3 = $mentionPropositions.first().next().next(); - - // correct 1st mention proposition - assert.hasClass($mention1,'active', - "first partner mention should be active"); - assert.strictEqual($mention1.data('id'), 1, - "first partner mention should link to the correct partner id"); - assert.strictEqual($mention1.find('.o_mention_name').text(), "Admin", - "first partner mention should display the correct partner name"); - // correct 2nd mention proposition - assert.doesNotHaveClass($mention2, 'active', - "second partner mention should not be active"); - assert.strictEqual($mention2.data('id'), 2, - "second partner mention should link to the correct partner id"); - assert.strictEqual($mention2.find('.o_mention_name').text(), "TestUser", - "second partner mention should display the correct partner name"); - // correct 3rd mention proposition - assert.doesNotHaveClass($mention3, 'active', - "third partner mention should not be active"); - assert.strictEqual($mention3.data('id'), 3, - "third partner mention should link to the correct partner id"); - assert.strictEqual($mention3.find('.o_mention_name').text(), "DemoUser", - "third partner mention should display the correct partner name"); - - // check DOWN event - $input.trigger($.Event('keyup', {which: $.ui.keyCode.DOWN})); - assert.doesNotHaveClass($mention1, 'active', - "first partner mention should not be active"); - assert.hasClass($mention2,'active', - "second partner mention should be active"); - assert.doesNotHaveClass($mention3, 'active', - "third partner mention should not be active"); - - // check UP event - $input.trigger($.Event('keyup', {which: $.ui.keyCode.UP})); - assert.hasClass($mention1,'active', - "first partner mention should be active"); - assert.doesNotHaveClass($mention2, 'active', - "second partner mention should not be active"); - assert.doesNotHaveClass($mention3, 'active', - "third partner mention should not be active"); - - // check TAB event (full cycle, hence 3 TABs) - $input.trigger($.Event('keyup', {which: $.ui.keyCode.TAB})); - assert.doesNotHaveClass($mention1, 'active', - "first partner mention should not be active"); - assert.hasClass($mention2,'active', - "second partner mention should be active"); - assert.doesNotHaveClass($mention3, 'active', - "third partner mention should not be active"); - - $input.trigger($.Event('keyup', {which: $.ui.keyCode.TAB})); - assert.doesNotHaveClass($mention1, 'active', - "first partner mention should not be active"); - assert.doesNotHaveClass($mention2, 'active', - "second partner mention should not be active"); - assert.hasClass($mention3,'active', - "third partner mention should be active"); - - $input.trigger($.Event('keyup', {which: $.ui.keyCode.TAB})); - assert.hasClass($mention1,'active', - "first partner mention should be active"); - assert.doesNotHaveClass($mention2, 'active', - "second partner mention should not be active"); - assert.doesNotHaveClass($mention3, 'active', - "third partner mention should not be active"); - -// testUtils.dom.click( equivalent to $mentionPropositions.find('active')); - $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER})); - assert.containsNone(discuss, '.o_mention_proposition', - "should not have any partner mention proposition after ENTER"); - assert.strictEqual($input.val().trim() , "@Admin", - "should have the correct mention link in the composer input"); - - // send message - $input.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); - - receiveMessageDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - assert.containsOnce(discuss, '.o_thread_message_content', - "should display one message with some content"); - assert.containsOnce(discuss, '.o_thread_message_content a', - "should contain a link in the message content"); - assert.strictEqual(discuss.$('.o_thread_message_content a').text(), - "@Admin", "should have correct mention link in the message content"); - - discuss.destroy(); - done(); - }); - }); }); + objectDiscuss = discuss; + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.hasAttrValue($general, 'title', 'general', + "should have the title 'general'"); + + // click on general + await testUtils.dom.click($general); + var $input = discuss.$('textarea.o_composer_text_field').first(); + assert.ok($input.length, "should display a composer input"); + + // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' + $input.focus(); + $input.val("@"); + $input.trigger('keyup'); + + fetchListenersDef; + await testUtils.nextTick(); + assert.containsOnce(discuss, '.dropup.o_composer_mention_dropdown.show', + "dropup menu for partner mentions should be open"); + + var $mentionPropositions = discuss.$('.o_mention_proposition'); + assert.strictEqual($mentionPropositions.length, 3, + "should display 3 partner mention propositions"); + + var $mention1 = $mentionPropositions.first(); + var $mention2 = $mentionPropositions.first().next(); + var $mention3 = $mentionPropositions.first().next().next(); + + // correct 1st mention proposition + assert.hasClass($mention1,'active', + "first partner mention should be active"); + assert.strictEqual($mention1.data('id'), 1, + "first partner mention should link to the correct partner id"); + assert.strictEqual($mention1.find('.o_mention_name').text(), "Admin", + "first partner mention should display the correct partner name"); + // correct 2nd mention proposition + assert.doesNotHaveClass($mention2, 'active', + "second partner mention should not be active"); + assert.strictEqual($mention2.data('id'), 2, + "second partner mention should link to the correct partner id"); + assert.strictEqual($mention2.find('.o_mention_name').text(), "TestUser", + "second partner mention should display the correct partner name"); + // correct 3rd mention proposition + assert.doesNotHaveClass($mention3, 'active', + "third partner mention should not be active"); + assert.strictEqual($mention3.data('id'), 3, + "third partner mention should link to the correct partner id"); + assert.strictEqual($mention3.find('.o_mention_name').text(), "DemoUser", + "third partner mention should display the correct partner name"); + + // check DOWN event + await testUtils.fields.triggerKey('up', $input, 'down'); + assert.doesNotHaveClass($mention1, 'active', + "first partner mention should not be active"); + assert.hasClass($mention2,'active', + "second partner mention should be active"); + assert.doesNotHaveClass($mention3, 'active', + "third partner mention should not be active"); + + // check UP event + await testUtils.fields.triggerKey('up', $input, 'up'); + assert.hasClass($mention1,'active', + "first partner mention should be active"); + assert.doesNotHaveClass($mention2, 'active', + "second partner mention should not be active"); + assert.doesNotHaveClass($mention3, 'active', + "third partner mention should not be active"); + + // check TAB event (full cycle, hence 3 TABs) + await testUtils.fields.triggerKey('up', $input, 'tab'); + assert.doesNotHaveClass($mention1, 'active', + "first partner mention should not be active"); + assert.hasClass($mention2,'active', + "second partner mention should be active"); + assert.doesNotHaveClass($mention3, 'active', + "third partner mention should not be active"); + + await testUtils.fields.triggerKey('up', $input, 'tab'); + assert.doesNotHaveClass($mention1, 'active', + "first partner mention should not be active"); + assert.doesNotHaveClass($mention2, 'active', + "second partner mention should not be active"); + assert.hasClass($mention3,'active', + "third partner mention should be active"); + + await testUtils.fields.triggerKey('up', $input, 'tab'); + assert.hasClass($mention1,'active', + "first partner mention should be active"); + assert.doesNotHaveClass($mention2, 'active', + "second partner mention should not be active"); + assert.doesNotHaveClass($mention3, 'active', + "third partner mention should not be active"); + + await testUtils.fields.triggerKey('up', $input, 'enter'); + assert.containsNone(discuss, '.o_mention_proposition', + "should not have any partner mention proposition after ENTER"); + assert.strictEqual($input.val().trim() , "@Admin", + "should have the correct mention link in the composer input"); + + // send message + await testUtils.fields.triggerKeydown($input, 'enter'); + + await receiveMessageDef; + await testUtils.nextTick(); + + assert.containsOnce(discuss, '.o_thread_message_content', + "should display one message with some content"); + assert.containsOnce(discuss, '.o_thread_message_content a', + "should contain a link in the message content"); + assert.strictEqual(discuss.$('.o_thread_message_content a').text(), + "@Admin", "should have correct mention link in the message content"); + + discuss.destroy(); }); -QUnit.test('@ mention with special chars', function (assert) { +QUnit.test('@ mention with special chars', async function (assert) { assert.expect(11); - var done = assert.async(); - var fetchListenersDef = $.Deferred(); - var receiveMessageDef = $.Deferred(); + var fetchListenersDef = testUtils.makeTestPromise(); + var receiveMessageDef = testUtils.makeTestPromise(); this.data.initMessaging = { channel_slots: { channel_channel: [{ @@ -448,7 +428,7 @@ QUnit.test('@ mention with special chars', function (assert) { }, }; var objectDiscuss; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -457,7 +437,7 @@ QUnit.test('@ mention with special chars', function (assert) { mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { fetchListenersDef.resolve(); - return $.when([ + return Promise.resolve([ {id: 1, name: '\u0405pëciãlUser<&>"`\' \u30C4'}, ]); } @@ -473,70 +453,64 @@ QUnit.test('@ mention with special chars', function (assert) { var notification = [[false, 'mail.channel', 1], data]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); receiveMessageDef.resolve(); - return $.when(42); + return Promise.resolve(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(); - assert.ok($input.length, "should display a composer input"); - // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' - $input.focus(); - $input.val("@"); - $input.trigger('keyup'); - fetchListenersDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - var $mention = discuss.$('.o_mention_proposition'); - // correct mention proposition - assert.ok($mention.hasClass('active'), - "first partner mention should be active"); - assert.strictEqual($mention.data('id'), 1, - "first partner mention should link to the correct partner id"); - assert.strictEqual($mention.find('.o_mention_name').text(), '\u0405pëciãlUser<&>"`\' \u30C4', - "first partner mention should display the correct partner name"); - // equivalent to $mentionPropositions.find('active').click(); - $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER})); - assert.strictEqual(discuss.$('.o_mention_proposition').length, 0, - "should not have any partner mention proposition after ENTER"); - assert.strictEqual($input.val().trim() , "@\u0405pëciãlUser<&>\"`'\u00A0\u30C4", - "should have the correct mention link in the composer input"); - // send message - $input.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); - receiveMessageDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - assert.strictEqual(discuss.$('.o_thread_message_content').length, 1, - "should display one message with some content"); - assert.strictEqual(discuss.$('.o_thread_message_content a').length, 1, - "should contain a link in the message content"); - assert.strictEqual(discuss.$('.o_thread_message_content a').text(), - "@\u0405pëciãlUser<&>\"`' \u30C4", - "should have correct mention link in the message content"); - $input.val("@"); - $input.trigger('keyup'); - var $mention = discuss.$('.o_mention_proposition'); - assert.strictEqual($mention.find('.o_mention_name').text(), - '\u0405pëciãlUser<&>"`\' \u30C4', - "first partner mention should still display the correct partner name"); - discuss.destroy(); - done(); - }); - }); }); + objectDiscuss = discuss; + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + // click on general + $general.click(); + await testUtils.nextTick(); + var $input = discuss.$('textarea.o_composer_text_field').first(); + assert.ok($input.length, "should display a composer input"); + // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' + $input.focus(); + $input.val("@"); + $input.trigger('keyup'); + await fetchListenersDef; + await testUtils.nextTick(); + var $mention = discuss.$('.o_mention_proposition'); + // correct mention proposition + assert.ok($mention.hasClass('active'), + "first partner mention should be active"); + assert.strictEqual($mention.data('id'), 1, + "first partner mention should link to the correct partner id"); + assert.strictEqual($mention.find('.o_mention_name').text(), '\u0405pëciãlUser<&>"`\' \u30C4', + "first partner mention should display the correct partner name"); + // equivalent to $mentionPropositions.find('active').click(); + await testUtils.fields.triggerKey('up', $input, 'enter'); + assert.strictEqual(discuss.$('.o_mention_proposition').length, 0, + "should not have any partner mention proposition after ENTER"); + assert.strictEqual($input.val().trim() , "@\u0405pëciãlUser<&>\"`'\u00A0\u30C4", + "should have the correct mention link in the composer input"); + // send message + await testUtils.fields.triggerKeydown($input, 'enter'); + await receiveMessageDef; + await testUtils.nextTick(); + assert.strictEqual(discuss.$('.o_thread_message_content').length, 1, + "should display one message with some content"); + assert.strictEqual(discuss.$('.o_thread_message_content a').length, 1, + "should contain a link in the message content"); + assert.strictEqual(discuss.$('.o_thread_message_content a').text(), + "@\u0405pëciãlUser<&>\"`' \u30C4", + "should have correct mention link in the message content"); + $input.val("@"); + $input.trigger('keyup'); + await testUtils.nextTick(); + var $mention = discuss.$('.o_mention_proposition'); + assert.strictEqual($mention.find('.o_mention_name').text(), + '\u0405pëciãlUser<&>"`\' \u30C4', + "first partner mention should still display the correct partner name"); + discuss.destroy(); }); -QUnit.test('@ mention in mailing channel', function (assert) { +QUnit.test('@ mention in mailing channel', async function (assert) { assert.expect(8); - var done = assert.async(); - var fetchListenersDef = $.Deferred(); + var fetchListenersDef = testUtils.makeTestPromise(); this.data.initMessaging = { channel_slots: { @@ -549,7 +523,7 @@ QUnit.test('@ mention in mailing channel', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -558,62 +532,58 @@ QUnit.test('@ mention in mailing channel', function (assert) { mockRPC: function (route, args) { if (args.method === 'channel_fetch_listeners') { fetchListenersDef.resolve(); - return $.when([ + return Promise.resolve([ {id: 1, name: 'Admin'}, ]); } return this._super.apply(this, arguments); }, - }) - .then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - - // click on general - $general.click(); - // 1st composer: basic composer (hidden), 2nd composer: extended (shown) - var $input = discuss.$('textarea.o_composer_text_field').eq(1); - assert.ok($input.length, "should display a composer input"); - - // Simulate '@' typed by user with mocked Window.getSelection - // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' - $input.focus(); - $input.val("@"); - $input.trigger('keyup'); - - fetchListenersDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { - assert.strictEqual(discuss.$('.dropup.o_composer_mention_dropdown.show').length, 1, - "dropup menu for partner mentions should be open"); - - var $mention = discuss.$('.o_mention_proposition'); - assert.strictEqual($mention.length, 1, - "should display 1 partner mention proposition"); - - // correct mention proposition - assert.ok($mention.hasClass('active'), - "partner mention should be active"); - assert.strictEqual($mention.data('id'), 1, - "partner mention should link to the correct partner id"); - assert.strictEqual($mention.find('.o_mention_name').text(), "Admin", - "partner mention should display the correct partner name"); - - // equivalent to $mentionPropositions.find('active').click(); - $input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER})); - - assert.ok($input.is(':focus'), "composer body should have focus"); - assert.notOk(discuss.$('.o_composer_subject').is(':focus')); - - discuss.destroy(); - done(); - }); }); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + + // click on general + $general.click(); + await testUtils.nextTick(); + // 1st composer: basic composer (hidden), 2nd composer: extended (shown) + var $input = discuss.$('textarea.o_composer_text_field').eq(1); + assert.ok($input.length, "should display a composer input"); + + // Simulate '@' typed by user with mocked Window.getSelection + // Note: focus is needed in order to trigger rpc 'channel_fetch_listeners' + $input.focus(); + $input.val("@"); + $input.trigger('keyup'); + + await fetchListenersDef; + await testUtils.nextTick(); + + assert.strictEqual(discuss.$('.dropup.o_composer_mention_dropdown.show').length, 1, + "dropup menu for partner mentions should be open"); + + var $mention = discuss.$('.o_mention_proposition'); + assert.strictEqual($mention.length, 1, + "should display 1 partner mention proposition"); + + // correct mention proposition + assert.ok($mention.hasClass('active'), + "partner mention should be active"); + assert.strictEqual($mention.data('id'), 1, + "partner mention should link to the correct partner id"); + assert.strictEqual($mention.find('.o_mention_name').text(), "Admin", + "partner mention should display the correct partner name"); + + // equivalent to $mentionPropositions.find('active').click(); + await testUtils.fields.triggerKey('up', $input, 'enter'); + + assert.ok($input.is(':focus'), "composer body should have focus"); + assert.notOk(discuss.$('.o_composer_subject').is(':focus')); + + discuss.destroy(); }); -QUnit.test('no crash focusout emoji button', function (assert) { +QUnit.test('no crash focusout emoji button', async function (assert) { assert.expect(3); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -625,40 +595,37 @@ QUnit.test('no crash focusout emoji button', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, }) - .then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.hasAttrValue($general, 'title', 'general', - "should have the title 'general'"); - // click on general - testUtils.dom.click($general); - discuss.$('.o_composer_button_emoji').focus(); - try { - discuss.$('.o_composer_button_emoji').focusout(); - assert.ok(true, "should not crash on focusout of emoji button"); - } finally { - discuss.destroy(); - done(); - } - }); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.hasAttrValue($general, 'title', 'general', + "should have the title 'general'"); + + // click on general + await testUtils.dom.click($general); + discuss.$('.o_composer_button_emoji').focus(); + try { + discuss.$('.o_composer_button_emoji').focusout(); + assert.ok(true, "should not crash on focusout of emoji button"); + } finally { + discuss.destroy(); + } }); -QUnit.test('older messages are loaded on scroll', function (assert) { +QUnit.test('older messages are loaded on scroll', async function (assert) { assert.expect(10); - var done = assert.async(); var fetchCount = 0; - var loadMoreDef = $.Deferred(); + var loadMoreDef = testUtils.makeTestPromise(); var messageData = []; for (var i = 0; i < 35; i++) { messageData.push({ @@ -681,7 +648,7 @@ QUnit.test('older messages are loaded on scroll', function (assert) { }; this.data['mail.message'].records = messageData; - createDiscuss({ + var discuss = await createDiscuss({ context: {}, data: this.data, params: {}, @@ -703,44 +670,39 @@ QUnit.test('older messages are loaded on scroll', function (assert) { } 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' - testUtils.dom.click($general); - - assert.verifySteps(['message_fetch', 'message_fetch'], - "should fetch a second time for general channel messages (30 last messages)"); - - assert.containsN(discuss, '.o_thread_message', 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.containsN(discuss, '.o_thread_message', 35, - "all messages should now be loaded"); - - discuss.destroy(); - done(); - }); }); + + 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' + await testUtils.dom.click($general); + + assert.verifySteps(['message_fetch'], + "should fetch a second time for general channel messages (30 last messages)"); + + assert.containsN(discuss, '.o_thread_message', 30, + "should display the 30 messages"); + + // simulate a scroll to top to load more messages + discuss.$('.o_mail_thread').scrollTop(0); + + await loadMoreDef; + await testUtils.nextTick(); + assert.verifySteps(['message_fetch'], + "should fetch a third time for general channel messages (5 remaining messages)"); + assert.containsN(discuss, '.o_thread_message', 35, + "all messages should now be loaded"); + + discuss.destroy(); }); -QUnit.test('"Unstar all" button should reset the starred counter', function (assert) { +QUnit.test('"Unstar all" button should reset the starred counter', async function (assert) { assert.expect(2); - var done = assert.async(); var messageData = []; _.each(_.range(1, 41), function (num) { @@ -768,7 +730,7 @@ QUnit.test('"Unstar all" button should reset the starred counter', function (ass this.data['mail.message'].records = messageData; var objectDiscuss; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -783,37 +745,34 @@ QUnit.test('"Unstar all" button should reset the starred counter', function (ass }; var notification = [[false, 'res.partner'], data]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); - return $.when(42); + return Promise.resolve(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'); + objectDiscuss = discuss; - // Go to Starred channel - testUtils.dom.click($starred); - // Test Initial Value - assert.strictEqual($starredCounter.text().trim(), "40", "40 messages should be starred"); + var $starred = discuss.$('.o_mail_discuss_sidebar').find('.o_mail_mailbox_title_starred'); + var $starredCounter = $('.o_mail_mailbox_title_starred > .o_mail_sidebar_needaction'); - // Unstar all and wait 'update_starred' - testUtils.dom.click($('.o_control_panel .o_mail_discuss_button_unstar_all')); - $starredCounter = $('.o_mail_mailbox_title_starred > .o_mail_sidebar_needaction'); - assert.strictEqual($starredCounter.text().trim(), "0", - "All messages should be unstarred"); + // Go to Starred channel + await testUtils.dom.click($starred); + // Test Initial Value + assert.strictEqual($starredCounter.text().trim(), "40", "40 messages should be starred"); - discuss.destroy(); - done(); - }); + // Unstar all and wait 'update_starred' + await testUtils.dom.click($('.o_control_panel .o_mail_discuss_button_unstar_all')); + $starredCounter = $('.o_mail_mailbox_title_starred > .o_mail_sidebar_needaction'); + assert.strictEqual($starredCounter.text().trim(), "0", + "All messages should be unstarred"); + + discuss.destroy(); }); -QUnit.test('confirm dialog when administrator leave (not chat) channel', function (assert) { +QUnit.test('confirm dialog when administrator leave (not chat) channel', async function (assert) { assert.expect(2); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -826,7 +785,7 @@ QUnit.test('confirm dialog when administrator leave (not chat) channel', functio }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -835,30 +794,27 @@ QUnit.test('confirm dialog when administrator leave (not chat) channel', functio session: { uid: 3, }, - }) - .then(function (discuss) { - // Unsubscribe on MyChannel as administrator - // The unpin icon is only shown when hovering on sidebar item. - // helpter dom.click needs the element to be visible, hence the - // hack on display of this icon. - discuss.$('.o_mail_partner_unpin').css('display', 'block'); - testUtils.dom.click(discuss.$('.o_mail_partner_unpin')); - - 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(); }); + + // Unsubscribe on MyChannel as administrator + // The unpin icon is only shown when hovering on sidebar item. + // helpter dom.click needs the element to be visible, hence the + // hack on display of this icon. + discuss.$('.o_mail_partner_unpin').css('display', 'block'); + await testUtils.dom.click(discuss.$('.o_mail_partner_unpin')); + + 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(); }); -QUnit.test('convert emoji sources to unicodes on message_post', function (assert) { +QUnit.test('convert emoji sources to unicodes on message_post', async function (assert) { assert.expect(2); - var done = assert.async(); - var receiveMessageDef = $.Deferred(); + var receiveMessageDef = testUtils.makeTestPromise(); this.data.initMessaging = { channel_slots: { @@ -871,7 +827,7 @@ QUnit.test('convert emoji sources to unicodes on message_post', function (assert }; var objectDiscuss; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -890,40 +846,34 @@ QUnit.test('convert emoji sources to unicodes on message_post', function (assert var notification = [[false, 'mail.channel', 1], data]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); receiveMessageDef.resolve(); - return $.when(42); + return Promise.resolve(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]'); + objectDiscuss= discuss; - // click on general - testUtils.dom.click($general); - var $input = discuss.$('textarea.o_composer_text_field').first(); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); - $input.focus(); - $input.val(":) x'D"); - $input.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); + // click on general + await testUtils.dom.click($general); + var $input = discuss.$('textarea.o_composer_text_field').first(); - receiveMessageDef - .then(concurrency.delay.bind(concurrency, 0)) - .then(function () { + $input.focus(); + $input.val(":) x'D"); + await testUtils.fields.triggerKeydown($input, 'enter'); - 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(); - }); + await receiveMessageDef.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(); }); }); -QUnit.test('mark all messages as read from Inbox', function (assert) { - var done = assert.async(); +QUnit.test('mark all messages as read from Inbox', async function (assert) { assert.expect(9); this.data['mail.message'].records = [{ @@ -944,10 +894,10 @@ QUnit.test('mark all messages as read from Inbox', function (assert) { needaction_inbox_counter: 2, }; - var markAllReadDef = $.Deferred(); + var markAllReadDef = testUtils.makeTestPromise(); var objectDiscuss; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -966,53 +916,50 @@ QUnit.test('mark all messages as read from Inbox', function (assert) { var notification = [[false, 'res.partner', 3], notificationData]; objectDiscuss.call('bus_service', 'trigger', 'notification', [notification]); markAllReadDef.resolve(); - return $.when(); + return Promise.resolve(); } 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.hasClass($inbox,'o_active', - "'Inbox' should be the currently active thread"); - assert.containsN(discuss, '.o_thread_message', 2, - "there should be 2 messages in inbox"); + objectDiscuss = discuss; - var $markAllReadButton = $('.o_mail_discuss_button_mark_all_read'); - assert.strictEqual($markAllReadButton.length, 1, - "there should be a 'Mark All As Read' button"); - assert.hasAttrValue($markAllReadButton, '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"); + 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.hasClass($inbox,'o_active', + "'Inbox' should be the currently active thread"); + assert.containsN(discuss, '.o_thread_message', 2, + "there should be 2 messages in inbox"); - testUtils.dom.click($markAllReadButton); + var $markAllReadButton = $('.o_mail_discuss_button_mark_all_read'); + assert.strictEqual($markAllReadButton.length, 1, + "there should be a 'Mark All As Read' button"); + assert.hasAttrValue($markAllReadButton, '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"); - 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.containsNone(discuss, '.o_thread_message', - "there should no message in inbox anymore"); + await testUtils.dom.click($markAllReadButton); - discuss.destroy(); - done(); - }); + 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.containsNone(discuss, '.o_thread_message', + "there should no message in inbox anymore"); + + discuss.destroy(); }); }); -QUnit.test('drag and drop file in composer', function (assert) { +QUnit.test('drag and drop file in composer [REQUIRE NON-INCOGNITO WINDOW]', async function (assert) { assert.expect(8); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -1024,60 +971,57 @@ QUnit.test('drag and drop file in composer', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, }) - .then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - // click on general - testUtils.dom.click($general); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); - // first composer is active (basic), 2nd is hidden (extended) - var $composer = discuss.$('.o_thread_composer').first(); - assert.containsNone($composer, '.o_attachments_list', - "should not display any attachment on the composer initially"); - assert.containsOnce($composer, '.o_file_drop_zone_container', - "should have a dropzone to drag-and-drop files"); + // click on general + await testUtils.dom.click($general); - var $dropZoneContainer = $composer.find('.o_file_drop_zone_container'); - assert.isNotVisible($dropZoneContainer, - "dropzone should not be visible"); + // first composer is active (basic), 2nd is hidden (extended) + var $composer = discuss.$('.o_thread_composer').first(); + assert.containsNone($composer, '.o_attachments_list', + "should not display any attachment on the composer initially"); + assert.containsOnce($composer, '.o_file_drop_zone_container', + "should have a dropzone to drag-and-drop files"); - testUtils.file.createFile({ - name: 'text.txt', - content: 'hello, world', - contentType: 'text/plain', - }).then(function (file) { - testUtils.file.dragoverFile($dropZoneContainer, file); - assert.isVisible($dropZoneContainer, - "dropzone should be visible"); + var $dropZoneContainer = $composer.find('.o_file_drop_zone_container'); + assert.isNotVisible($dropZoneContainer, + "dropzone should not be visible"); - testUtils.file.dropFile($dropZoneContainer, file); - assert.containsOnce($composer, '.o_attachments_list', - "should display some attachments on the composer"); - assert.containsOnce($composer, '.o_attachment', - "should display one attachment on the composer"); - - var filename = $('.o_attachment').find('.caption').first().text().trim(); - assert.strictEqual(filename, 'text.txt', - "should display the correct filename"); - assert.hasClass($('.o_attachment_uploaded i'), 'fa-check', - "text file should have been uploaded"); - - discuss.destroy(); - done(); - }); + var file = await testUtils.file.createFile({ + name: 'text.txt', + content: 'hello, world', + contentType: 'text/plain', }); + + await testUtils.file.dragoverFile($dropZoneContainer, file); + assert.isVisible($dropZoneContainer, + "dropzone should be visible"); + + await testUtils.file.dropFile($dropZoneContainer, file); + assert.containsOnce($composer, '.o_attachments_list', + "should display some attachments on the composer"); + assert.containsOnce($composer, '.o_attachment', + "should display one attachment on the composer"); + + var filename = $('.o_attachment').find('.caption').first().text().trim(); + assert.strictEqual(filename, 'text.txt', + "should display the correct filename"); + assert.hasClass($('.o_attachment_uploaded i'), 'fa-check', + "text file should have been uploaded"); + + discuss.destroy(); }); -QUnit.test('reply to message from inbox', function (assert) { - var done = assert.async(); +QUnit.test('reply to message from inbox', async function (assert) { assert.expect(11); var self = this; @@ -1095,7 +1039,7 @@ QUnit.test('reply to message from inbox', function (assert) { needaction_inbox_counter: 1, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -1118,45 +1062,41 @@ QUnit.test('reply to message from inbox', function (assert) { model: 'some.document', record_name: 'SomeDocument', }); - return $.when(2); + return Promise.resolve(2); } return this._super.apply(this, arguments); }, }) - .then(function (discuss) { - assert.strictEqual(discuss.$('.o_mail_discuss_item.o_active').data('thread-id'), - 'mailbox_inbox', - "Inbox should be selected by default"); - assert.containsOnce(discuss, '.o_thread_message', - "should display a single message in inbox"); - assert.strictEqual(discuss.$('.o_thread_message').data('message-id'), 1, - "message should be linked to correct message"); - assert.containsOnce(discuss.$('.o_thread_message'), '.o_thread_message_reply', - "should display the reply icon for message linked to a document"); + assert.strictEqual(discuss.$('.o_mail_discuss_item.o_active').data('thread-id'), + 'mailbox_inbox', + "Inbox should be selected by default"); + assert.containsOnce(discuss, '.o_thread_message', + "should display a single message in inbox"); + assert.strictEqual(discuss.$('.o_thread_message').data('message-id'), 1, + "message should be linked to correct message"); + assert.containsOnce(discuss.$('.o_thread_message'), '.o_thread_message_reply', + "should display the reply icon for message linked to a document"); - testUtils.dom.click(discuss.$('.o_thread_message_reply')); - var $composer = discuss.$('.o_thread_composer_extended'); - assert.isVisible($composer, - "extended composer should become visible"); - assert.strictEqual($composer.find('.o_composer_subject input').val(), - 'Re: SomeDocument', - "composer should have copied document name as subject of message"); + await testUtils.dom.click(discuss.$('.o_thread_message_reply')); + var $composer = discuss.$('.o_thread_composer_extended'); + assert.isVisible($composer, + "extended composer should become visible"); + assert.strictEqual($composer.find('.o_composer_subject input').val(), + 'Re: SomeDocument', + "composer should have copied document name as subject of message"); - var $textarea = $composer.find('.o_composer_input textarea').first(); - testUtils.fields.editInput($textarea, 'someContent'); - assert.containsOnce($composer, '.o_composer_button_send', - "should have button to send reply message"); - testUtils.dom.click($composer.find('.o_composer_button_send')); + var $textarea = $composer.find('.o_composer_input textarea').first(); + await testUtils.fields.editInput($textarea, 'someContent'); + assert.containsOnce($composer, '.o_composer_button_send', + "should have button to send reply message"); + await testUtils.dom.click($composer.find('.o_composer_button_send')); - assert.verifySteps(['message_post']); + assert.verifySteps(['message_post']); - discuss.destroy(); - done(); - }); + discuss.destroy(); }); -QUnit.test('discard replying to message from inbox', function (assert) { - var done = assert.async(); +QUnit.test('discard replying to message from inbox', async function (assert) { assert.expect(4); var self = this; @@ -1174,7 +1114,7 @@ QUnit.test('discard replying to message from inbox', function (assert) { needaction_inbox_counter: 1, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -1197,34 +1137,31 @@ QUnit.test('discard replying to message from inbox', function (assert) { model: 'some.document', record_name: 'SomeDocument', }); - return $.when(2); + return Promise.resolve(2); } return this._super.apply(this, arguments); }, }) - .then(function (discuss) { - testUtils.dom.click(discuss.$('.o_thread_message_reply')); - assert.containsOnce(discuss, '.o_thread_selected_message', - "should have a message selected"); - var $composer = discuss.$('.o_thread_composer_extended'); - assert.containsOnce($composer, '.o_composer_button_discard', - "should have button to discard replying to message"); + await testUtils.dom.click(discuss.$('.o_thread_message_reply')); + assert.containsOnce(discuss, '.o_thread_selected_message', + "should have a message selected"); - testUtils.dom.click($composer.find('.o_composer_button_discard')); - assert.isNotVisible($composer, - "extended composer should become hidden on discard"); - assert.containsNone(discuss, '.o_thread_selected_message', - "should not longer have a message selected"); + var $composer = discuss.$('.o_thread_composer_extended'); + assert.containsOnce($composer, '.o_composer_button_discard', + "should have button to discard replying to message"); - discuss.destroy(); - done(); - }); + await testUtils.dom.click($composer.find('.o_composer_button_discard')); + assert.isNotVisible($composer, + "extended composer should become hidden on discard"); + assert.containsNone(discuss, '.o_thread_selected_message', + "should not longer have a message selected"); + + discuss.destroy(); }); -QUnit.test('no quick search channels in the sidebar with less than 20 channels', function (assert) { +QUnit.test('no quick search channels in the sidebar with less than 20 channels', async function (assert) { assert.expect(3); - var done = assert.async(); var channelsData = []; _.each(_.range(0, 19), function (num) { @@ -1241,32 +1178,29 @@ QUnit.test('no quick search channels in the sidebar with less than 20 channels', }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, }) - .then(function (discuss) { - var $sidebar = discuss.$('.o_mail_discuss_sidebar'); - var $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); - assert.strictEqual($channelsSidebar.length, 1, - "should show channels and dms in sidebar"); - assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 19, - "should show 19 channels in sidebar"); - assert.strictEqual(discuss.$('.o_discuss_sidebar_quick_search').length, 0, - "should not display a quick search"); + var $sidebar = discuss.$('.o_mail_discuss_sidebar'); - discuss.destroy(); - done(); - }); + var $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); + assert.strictEqual($channelsSidebar.length, 1, + "should show channels and dms in sidebar"); + assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 19, + "should show 19 channels in sidebar"); + assert.strictEqual(discuss.$('.o_discuss_sidebar_quick_search').length, 0, + "should not display a quick search"); + + discuss.destroy(); }); -QUnit.test('quick search channels in the sidebar with more than 20 channels', function (assert) { +QUnit.test('quick search channels in the sidebar with more than 20 channels', async function (assert) { assert.expect(7); - var done = assert.async(); var channelsData = []; var channelANum = 15; @@ -1294,52 +1228,49 @@ QUnit.test('quick search channels in the sidebar with more than 20 channels', fu }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, }) - .then(function (discuss) { - var $sidebar = discuss.$('.o_mail_discuss_sidebar'); - var $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); - assert.strictEqual($channelsSidebar.length, 1, - "should show channels in sidebar"); - assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 20, - "should show 20 channels in sidebar"); - assert.strictEqual(discuss.$('.o_discuss_sidebar_quick_search').length, 1, - "should display a quick search"); + var $sidebar = discuss.$('.o_mail_discuss_sidebar'); - discuss.$('.o_discuss_sidebar_quick_search input').val('channelA').trigger('input'); - $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); - assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 15, - "should now show 15 channels in sidebar"); + var $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); + assert.strictEqual($channelsSidebar.length, 1, + "should show channels in sidebar"); + assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 20, + "should show 20 channels in sidebar"); + assert.strictEqual(discuss.$('.o_discuss_sidebar_quick_search').length, 1, + "should display a quick search"); - discuss.$('.o_discuss_sidebar_quick_search input').val('channelB').trigger('input'); + discuss.$('.o_discuss_sidebar_quick_search input').val('channelA').trigger('input'); + $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); + assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 15, + "should now show 15 channels in sidebar"); - $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); - assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 5, - "should now show 5 channels in sidebar"); + discuss.$('.o_discuss_sidebar_quick_search input').val('channelB').trigger('input'); - discuss.$('.o_discuss_sidebar_quick_search input').val('channelB4').trigger('input'); + $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); + assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 5, + "should now show 5 channels in sidebar"); - $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); - assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 1, - "should now show a single channel in sidebar"); - assert.strictEqual($channelsSidebar.find('.o_thread_name').text().replace(/\s/g, ''), - '#ChannelB4', - "should have searched the correct channel B4"); + discuss.$('.o_discuss_sidebar_quick_search input').val('channelB4').trigger('input'); - discuss.destroy(); - done(); - }); + $channelsSidebar = $sidebar.find('.o_mail_discuss_sidebar_channels'); + assert.strictEqual($channelsSidebar.find('.o_mail_discuss_item').length, 1, + "should now show a single channel in sidebar"); + assert.strictEqual($channelsSidebar.find('.o_thread_name').text().replace(/\s/g, ''), + '#ChannelB4', + "should have searched the correct channel B4"); + + discuss.destroy(); }); -QUnit.test('select emoji replaces cursor position', function (assert) { +QUnit.test('select emoji replaces cursor position', async function (assert) { assert.expect(5); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -1351,55 +1282,51 @@ QUnit.test('select emoji replaces cursor position', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, data: this.data, services: this.services, - }) - .then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.hasAttrValue($general, 'title', 'general', - "should have the title 'general'"); - - // click on general - testUtils.dom.click($general); - - // general uses basic composer, so this is the 1st composer - // (2nd composer is hidden) - var $composer = discuss.$('.o_composer_text_field').first(); - - testUtils.fields.editInput($composer, 'abcdefgh'); - - assert.strictEqual($composer.val(), "abcdefgh"); - - // cursor position: ab|cd|efgh (selecting c and d) - $composer[0].setSelectionRange(2, 4); - - testUtils.dom.click(discuss.$('.o_composer_button_emoji')); - testUtils.dom.triggerMouseEvent(discuss.$('.o_mail_emoji[data-emoji=":)"]'), 'mousedown'); - - assert.strictEqual($composer.val(), "ab :) efgh", - "should have inserted emoji "); - - testUtils.dom.click(discuss.$('.o_composer_button_emoji')); - testUtils.dom.triggerMouseEvent(discuss.$('.o_mail_emoji[data-emoji=":)"]'), 'mousedown'); - - assert.strictEqual($composer.val(), "ab :) :) efgh", - "should have inserted emoji after previously inserted emoji"); - - discuss.destroy(); - done(); }); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.hasAttrValue($general, 'title', 'general', + "should have the title 'general'"); + + // click on general + await testUtils.dom.click($general); + + // general uses basic composer, so this is the 1st composer + // (2nd composer is hidden) + var $composer = discuss.$('.o_composer_text_field').first(); + + await testUtils.fields.editInput($composer, 'abcdefgh'); + + assert.strictEqual($composer.val(), "abcdefgh"); + + // cursor position: ab|cd|efgh (selecting c and d) + $composer[0].setSelectionRange(2, 4); + + await testUtils.dom.click(discuss.$('.o_composer_button_emoji')); + await testUtils.dom.triggerMouseEvent(discuss.$('.o_mail_emoji[data-emoji=":)"]'), 'mousedown'); + + assert.strictEqual($composer.val(), "ab :) efgh", + "should have inserted emoji "); + + await testUtils.dom.click(discuss.$('.o_composer_button_emoji')); + await testUtils.dom.triggerMouseEvent(discuss.$('.o_mail_emoji[data-emoji=":)"]'), 'mousedown'); + + assert.strictEqual($composer.val(), "ab :) :) efgh", + "should have inserted emoji after previously inserted emoji"); + + discuss.destroy(); }); -QUnit.test('rename DM conversation', function (assert) { +QUnit.test('rename DM conversation', async function (assert) { assert.expect(11); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -1416,7 +1343,7 @@ QUnit.test('rename DM conversation', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -1428,41 +1355,38 @@ QUnit.test('rename DM conversation', function (assert) { assert.step(args.method); assert.strictEqual(args.args[0], 1); assert.strictEqual(args.kwargs.name, "Demo"); - return $.when("Demo"); + return Promise.resolve("Demo"); } return this._super.apply(this, arguments); }, - }) - .then(function (discuss) { - var $dm = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); - assert.isVisible($dm, "should display DM in the discuss sidebar"); - assert.strictEqual($dm.find('.o_thread_name').text().trim(), "Demo User"); - - // The settings icon is only shown when hovering on sidebar item. - // helpter dom.click needs the element to be visible, hence the - // hack on display of this icon. - discuss.$('.o_mail_channel_settings').css('display', 'block'); - testUtils.dom.click(discuss.$('.o_mail_channel_settings')); - - assert.isVisible($('.modal-dialog')); - assert.strictEqual($('.modal-title').text(), "Rename conversation"); - assert.isVisible($('.o_mail_discuss_rename_channel_input')); - assert.strictEqual($('.o_mail_discuss_rename_channel_input').val(), "Demo User"); - - testUtils.fields.editInput($('.o_mail_discuss_rename_channel_input'), "Demo"); - testUtils.dom.click($('.o_mail_conversation_rename')); - $dm = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); - assert.verifySteps(['channel_set_custom_name']); - assert.strictEqual($dm.find('.o_thread_name').text().trim(), "Demo"); - - discuss.destroy(); - done(); }); + + var $dm = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); + assert.isVisible($dm, "should display DM in the discuss sidebar"); + assert.strictEqual($dm.find('.o_thread_name').text().trim(), "Demo User"); + + // The settings icon is only shown when hovering on sidebar item. + // helpter dom.click needs the element to be visible, hence the + // hack on display of this icon. + discuss.$('.o_mail_channel_settings').css('display', 'block'); + await testUtils.dom.click(discuss.$('.o_mail_channel_settings')); + + assert.isVisible($('.modal-dialog')); + assert.strictEqual($('.modal-title').text(), "Rename conversation"); + assert.isVisible($('.o_mail_discuss_rename_channel_input')); + assert.strictEqual($('.o_mail_discuss_rename_channel_input').val(), "Demo User"); + + await testUtils.fields.editInput($('.o_mail_discuss_rename_channel_input'), "Demo"); + await testUtils.dom.click($('.o_mail_conversation_rename')); + $dm = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); + assert.verifySteps(['channel_set_custom_name']); + assert.strictEqual($dm.find('.o_thread_name').text().trim(), "Demo"); + + discuss.destroy(); }); -QUnit.test('custom-named DM conversation', function (assert) { +QUnit.test('custom-named DM conversation', async function (assert) { assert.expect(2); - var done = assert.async(); this.data.initMessaging = { channel_slots: { @@ -1480,7 +1404,7 @@ QUnit.test('custom-named DM conversation', function (assert) { }, }; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -1488,14 +1412,12 @@ QUnit.test('custom-named DM conversation', function (assert) { services: this.services, session: { partner_id: 3 }, }) - .then(function (discuss) { - var $dm = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); - assert.isVisible($dm, "should display DM in the discuss sidebar"); - assert.strictEqual($dm.find('.o_thread_name').text().trim(), "My Buddy"); - discuss.destroy(); - done(); - }); + var $dm = discuss.$('.o_mail_discuss_item[data-thread-id=1]'); + assert.isVisible($dm, "should display DM in the discuss sidebar"); + assert.strictEqual($dm.find('.o_thread_name').text().trim(), "My Buddy"); + + discuss.destroy(); }); }); diff --git a/addons/mail/static/tests/discuss_typing_notification_tests.js b/addons/mail/static/tests/discuss_typing_notification_tests.js index 2d1eb679c65..d77d32b9f1f 100644 --- a/addons/mail/static/tests/discuss_typing_notification_tests.js +++ b/addons/mail/static/tests/discuss_typing_notification_tests.js @@ -153,15 +153,14 @@ QUnit.module('Discuss (Typing Notifications)', { } }); -QUnit.test('notify is typing', function (assert) { +QUnit.test('notify is typing', async function (assert) { assert.expect(11); - var done = assert.async(); // this is to track the step that we are testing in the `notify_typing` // mocked RPC var step = 1; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -188,44 +187,42 @@ QUnit.test('notify is typing', function (assert) { return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.hasAttrValue($general, 'title', 'general', - "should have the title 'general'"); - - // click on general - testUtils.dom.click($general); - - var $input = discuss.$('textarea.o_composer_text_field').first(); - assert.ok($input.length, "should display a composer input"); - - // STEP 1: current user is typing something - step = 1; - $input.focus(); - testUtils.fields.editInput($input, '1'); - - assert.verifySteps(['notify_typing']); - - // STEP 2: current user clears input - step = 2; - testUtils.fields.editInput($input, ''); - - assert.verifySteps(['notify_typing', 'notify_typing']); - - discuss.destroy(); - done(); }); + + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.hasAttrValue($general, 'title', 'general', + "should have the title 'general'"); + + // click on general + await testUtils.dom.click($general); + + var $input = discuss.$('textarea.o_composer_text_field').first(); + assert.ok($input.length, "should display a composer input"); + + // STEP 1: current user is typing something + step = 1; + $input.focus(); + testUtils.fields.editInput($input, '1'); + + assert.verifySteps(['notify_typing']); + + // STEP 2: current user clears input + step = 2; + testUtils.fields.editInput($input, ''); + + assert.verifySteps(['notify_typing']); + + discuss.destroy(); }); -QUnit.test('receive is typing notification', function (assert) { +QUnit.test('receive is typing notification', async function (assert) { assert.expect(10); var self = this; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -235,7 +232,7 @@ QUnit.test('receive is typing notification', function (assert) { // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: self.myPartnerID, name: self.myName }, { id: 42, name: "Someone" }, ]); @@ -243,67 +240,68 @@ QUnit.test('receive is typing notification', function (assert) { return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.hasAttrValue($general, 'title', 'general', - "should have the title 'general'"); - - // pick 1st composer (basic), not 2nd composer (extended, hidden) - var $composer = discuss.$('.o_thread_composer').first(); - - // click on general - testUtils.dom.click($general); - assert.containsNone($general, '.o_mail_thread_typing_icon', - "should no longer show someone is typing in sidebar of discuss"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", - "should no longer show someone is typing on the composer of active thread"); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsOnce($general, '.o_mail_thread_typing_icon', - "should have a thread typing icon next to general icon in the sidebar"); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "Someone is typing...", - "should show who is typing on hover on this thread typing icon"); - assert.containsOnce($composer, '.o_composer_thread_typing', - "should show typing info on the composer of active thread"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "Someone is typing...", - "should show who is typing on the composer of active thread"); - - self.simulateIsTyping({ - channelID: 1, - isTyping: false, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsNone($general, '.o_mail_thread_typing_icon', - "should no longer show someone is typing in sidebar of discuss"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", - "should no longer show someone is typing on the composer of active thread"); - - discuss.destroy(); }); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.hasAttrValue($general, 'title', 'general', + "should have the title 'general'"); + + // pick 1st composer (basic), not 2nd composer (extended, hidden) + var $composer = discuss.$('.o_thread_composer').first(); + + // click on general + await testUtils.dom.click($general); + assert.containsNone($general, '.o_mail_thread_typing_icon', + "should no longer show someone is typing in sidebar of discuss"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", + "should no longer show someone is typing on the composer of active thread"); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsOnce($general, '.o_mail_thread_typing_icon', + "should have a thread typing icon next to general icon in the sidebar"); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "Someone is typing...", + "should show who is typing on hover on this thread typing icon"); + assert.containsOnce($composer, '.o_composer_thread_typing', + "should show typing info on the composer of active thread"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "Someone is typing...", + "should show who is typing on the composer of active thread"); + + self.simulateIsTyping({ + channelID: 1, + isTyping: false, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsNone($general, '.o_mail_thread_typing_icon', + "should no longer show someone is typing in sidebar of discuss"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", + "should no longer show someone is typing on the composer of active thread"); + + discuss.destroy(); }); -QUnit.test('receive message of someone that was typing something', function (assert) { +QUnit.test('receive message of someone that was typing something', async function (assert) { assert.expect(6); var self = this; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -313,7 +311,7 @@ QUnit.test('receive message of someone that was typing something', function (ass // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: self.myPartnerID, name: self.myName }, { id: 42, name: "Someone" }, ]); @@ -321,62 +319,63 @@ QUnit.test('receive message of someone that was typing something', function (ass return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - // click on general channel - var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - - // pick 1st composer (basic), not 2nd composer (extended, hidden) - var $composer = discuss.$('.o_thread_composer').first(); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsOnce($general, '.o_mail_thread_typing_icon', - "should have a thread typing icon next to general icon in the sidebar"); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "Someone is typing...", - "should show who is typing on hover on this thread typing icon"); - assert.containsOnce($composer, '.o_composer_thread_typing', - "should show typing info on the composer of active thread"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "Someone is typing...", - "should show who is typing on the composer of active thread"); - - // Simulate receive message from the person typing something - var messageData = { - author_id: [42, 'Someone'], - body: "

test

", - channel_ids: [1], - id: 101, - model: 'mail.channel', - res_id: 1, - }; - var notification = [[false, 'mail.channel', 1], messageData]; - discuss.call('bus_service', 'trigger', 'notification', [notification]); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsNone($general, '.o_mail_thread_typing_icon', - "should no longer show someone is typing in sidebar of discuss, after receiving message"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", - "should no longer show someone is typing on the composer of active thread, after receiving message"); - - discuss.destroy(); }); + // click on general channel + var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + + // pick 1st composer (basic), not 2nd composer (extended, hidden) + var $composer = discuss.$('.o_thread_composer').first(); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsOnce($general, '.o_mail_thread_typing_icon', + "should have a thread typing icon next to general icon in the sidebar"); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "Someone is typing...", + "should show who is typing on hover on this thread typing icon"); + assert.containsOnce($composer, '.o_composer_thread_typing', + "should show typing info on the composer of active thread"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "Someone is typing...", + "should show who is typing on the composer of active thread"); + + // Simulate receive message from the person typing something + var messageData = { + author_id: [42, 'Someone'], + body: "

test

", + channel_ids: [1], + id: 101, + model: 'mail.channel', + res_id: 1, + }; + var notification = [[false, 'mail.channel', 1], messageData]; + await discuss.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsNone($general, '.o_mail_thread_typing_icon', + "should no longer show someone is typing in sidebar of discuss, after receiving message"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", + "should no longer show someone is typing on the composer of active thread, after receiving message"); + + discuss.destroy(); }); -QUnit.test('do not display myself as typing', function (assert) { +QUnit.test('do not display myself as typing', async function (assert) { assert.expect(2); var self = this; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -386,40 +385,39 @@ QUnit.test('do not display myself as typing', function (assert) { // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: self.myPartnerID, name: self.myName }, ]); } return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - // click on general channel - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - // pick 1st composer (basic), not 2nd composer (extended, hidden) - var $composer = discuss.$('.o_thread_composer').first(); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: self.myPartnerID, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsNone($general, '.o_mail_thread_typing_icon', - "should not show current user is typing in sidebar of discuss"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", - "should not show current user is typing on the composer of active thread"); - - discuss.destroy(); }); + // click on general channel + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + // pick 1st composer (basic), not 2nd composer (extended, hidden) + var $composer = discuss.$('.o_thread_composer').first(); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: self.myPartnerID, + widget: discuss, + }); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsNone($general, '.o_mail_thread_typing_icon', + "should not show current user is typing in sidebar of discuss"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), "", + "should not show current user is typing on the composer of active thread"); + + discuss.destroy(); }); -QUnit.test('several users typing something at the same time', function (assert) { +QUnit.test('several users typing something at the same time', async function (assert) { // The order to display several typing partners name is not random: // it shows at most 2 typing partners, which are the typing partners that // have been typing something for the longest time. @@ -428,7 +426,7 @@ QUnit.test('several users typing something at the same time', function (assert) assert.expect(10); var self = this; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -438,7 +436,7 @@ QUnit.test('several users typing something at the same time', function (assert) // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: self.myPartnerID, name: self.myName }, { id: 42, name: "Someone" }, { id: 43, name: "Anonymous" }, @@ -448,98 +446,101 @@ QUnit.test('several users typing something at the same time', function (assert) return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - // click on general channel - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - - // click on general - testUtils.dom.click($general); - - // pick 1st composer (basic), not 2nd composer (extended, hidden) - var $composer = discuss.$('.o_thread_composer').first(); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsOnce($general, '.o_mail_thread_typing_icon', - "should have a thread typing icon next to general icon in the sidebar"); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "Someone is typing...", - "should show someone is typing on hover on this thread typing icon"); - assert.containsOnce($composer, '.o_composer_thread_typing', - "should show typing info on the composer of active thread"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "Someone is typing...", - "should show someone is typing on the composer of active thread"); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 43, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "Someone and Anonymous are typing...", - "should show someone and anonymous are simultaneously typing on hover on this thread typing icon"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "Someone and Anonymous are typing...", - "should show someone and anonymous are simultaneously typing on the composer of active thread"); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 44, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "Someone, Anonymous and more are typing...", - "should show 2 and more users are simultaneously typing on hover on this thread typing icon"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "Someone, Anonymous and more are typing...", - "should show 2 and more users are simultaneously typing on the composer of active thread"); - - self.simulateIsTyping({ - channelID: 1, - isTyping: false, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "Anonymous and Shy Guy are typing...", - "should no longer show than someone is typing on hover on this thread typing icon"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "Anonymous and Shy Guy are typing...", - "should no longer show than someone is typing on the composer of active thread"); - - discuss.destroy(); }); + // click on general channel + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + + // click on general + await testUtils.dom.click($general); + + // pick 1st composer (basic), not 2nd composer (extended, hidden) + var $composer = discuss.$('.o_thread_composer').first(); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsOnce($general, '.o_mail_thread_typing_icon', + "should have a thread typing icon next to general icon in the sidebar"); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "Someone is typing...", + "should show someone is typing on hover on this thread typing icon"); + assert.containsOnce($composer, '.o_composer_thread_typing', + "should show typing info on the composer of active thread"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "Someone is typing...", + "should show someone is typing on the composer of active thread"); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 43, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "Someone and Anonymous are typing...", + "should show someone and anonymous are simultaneously typing on hover on this thread typing icon"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "Someone and Anonymous are typing...", + "should show someone and anonymous are simultaneously typing on the composer of active thread"); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 44, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "Someone, Anonymous and more are typing...", + "should show 2 and more users are simultaneously typing on hover on this thread typing icon"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "Someone, Anonymous and more are typing...", + "should show 2 and more users are simultaneously typing on the composer of active thread"); + + self.simulateIsTyping({ + channelID: 1, + isTyping: false, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "Anonymous and Shy Guy are typing...", + "should no longer show than someone is typing on hover on this thread typing icon"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "Anonymous and Shy Guy are typing...", + "should no longer show than someone is typing on the composer of active thread"); + + discuss.destroy(); }); -QUnit.test('long typing partner A and in-between short typing partner B', function (assert) { +QUnit.test('long typing partner A and in-between short typing partner B', async function (assert) { // Let's suppose that A is typing a very long message. If B types a short // and sends it, it should display that B is typing, remove B from display // when receiving the message, but still keep A in the typing notification. assert.expect(8); var self = this; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -549,7 +550,7 @@ QUnit.test('long typing partner A and in-between short typing partner B', functi // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: self.myPartnerID, name: self.myName }, { id: 42, name: "A" }, { id: 43, name: "B" }, @@ -558,76 +559,78 @@ QUnit.test('long typing partner A and in-between short typing partner B', functi return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - // click on general channel - var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]'); - testUtils.dom.click($general); - // pick 1st composer (basic), not 2nd composer (extended, hidden) - var $composer = discuss.$('.o_thread_composer').first(); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.containsOnce($general, '.o_mail_thread_typing_icon', - "should have a thread typing icon next to general icon in the sidebar"); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "A is typing...", - "should show A is typing on hover on this thread typing icon"); - assert.containsOnce($composer, '.o_composer_thread_typing', - "should show typing info on the composer of active thread"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "A is typing...", - "should show A is typing on the composer of active thread"); - - self.simulateIsTyping({ - channelID: 1, - isTyping: true, - partnerID: 43, - widget: discuss, - }); - - // B comes after A in the display, because A is oldest typing partner. - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "A and B are typing...", - "should show A and B are typing on hover on this thread typing icon"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "A and B are typing...", - "should show A and B are typing on the composer of active thread"); - - // Simulate receive message from B - var messageData = { - author_id: [43, 'Someone'], - body: "

test

", - channel_ids: [1], - id: 101, - model: 'mail.channel', - res_id: 1, - }; - var notification = [[false, 'mail.channel', 1], messageData]; - discuss.call('bus_service', 'trigger', 'notification', [notification]); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', - "A is typing...", - "should show A is still typing on hover on this thread typing icon"); - assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), - "A is typing...", - "should show A is still typing on the composer of active thread"); - - discuss.destroy(); }); + // click on general channel + var $general = discuss.$('.o_mail_discuss_sidebar .o_mail_discuss_item[data-thread-id=1]'); + await testUtils.dom.click($general); + // pick 1st composer (basic), not 2nd composer (extended, hidden) + var $composer = discuss.$('.o_thread_composer').first(); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.containsOnce($general, '.o_mail_thread_typing_icon', + "should have a thread typing icon next to general icon in the sidebar"); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "A is typing...", + "should show A is typing on hover on this thread typing icon"); + assert.containsOnce($composer, '.o_composer_thread_typing', + "should show typing info on the composer of active thread"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "A is typing...", + "should show A is typing on the composer of active thread"); + + self.simulateIsTyping({ + channelID: 1, + isTyping: true, + partnerID: 43, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + // B comes after A in the display, because A is oldest typing partner. + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "A and B are typing...", + "should show A and B are typing on hover on this thread typing icon"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "A and B are typing...", + "should show A and B are typing on the composer of active thread"); + + // Simulate receive message from B + var messageData = { + author_id: [43, 'Someone'], + body: "

test

", + channel_ids: [1], + id: 101, + model: 'mail.channel', + res_id: 1, + }; + var notification = [[false, 'mail.channel', 1], messageData]; + await discuss.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + assert.hasAttrValue($general.find('.o_mail_thread_typing_icon'), 'title', + "A is typing...", + "should show A is still typing on hover on this thread typing icon"); + assert.strictEqual($composer.find('.o_composer_thread_typing').text().trim(), + "A is typing...", + "should show A is still typing on the composer of active thread"); + + discuss.destroy(); }); -QUnit.test('receive is typing notification from unselected thread', function (assert) { +QUnit.test('receive is typing notification from unselected thread', async function (assert) { assert.expect(7); this.data.initMessaging.channel_slots.channel_channel.push({ @@ -637,7 +640,7 @@ QUnit.test('receive is typing notification from unselected thread', function (as }); var self = this; - createDiscuss({ + var discuss = await createDiscuss({ id: 1, context: {}, params: {}, @@ -647,7 +650,7 @@ QUnit.test('receive is typing notification from unselected thread', function (as // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: self.myPartnerID, name: self.myName }, { id: 42, name: "Someone" }, ]); @@ -655,44 +658,44 @@ QUnit.test('receive is typing notification from unselected thread', function (as return this._super.apply(this, arguments); }, session: { partner_id: this.myPartnerID }, - }).then(function (discuss) { - var $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - var $other = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=2]'); - assert.strictEqual($general.length, 1, - "should have the channel item with id 1"); - assert.hasAttrValue($general, 'title', 'general', - "should have the title 'general'"); - assert.strictEqual($other.length, 1, - "should have the channel item with id 2"); - assert.hasAttrValue($other, 'title', 'other', - "should have the title 'other'"); - - // click on general - testUtils.dom.click($general); - - assert.containsNone($general, '.o_mail_thread_typing_icon', - "should no longer show someone is typing in sidebar of discuss"); - - self.simulateIsTyping({ - channelID: 2, - isTyping: true, - partnerID: 42, - widget: discuss, - }); - - $general = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=1]'); - $other = discuss.$('.o_mail_discuss_sidebar') - .find('.o_mail_discuss_item[data-thread-id=2]'); - assert.containsNone($general, '.o_mail_thread_typing_icon', - "should not have a thread typing icon next to general icon in the sidebar"); - assert.containsOnce($other, '.o_mail_thread_typing_icon', - "should have a thread typing icon next to other icon in the sidebar"); - - discuss.destroy(); }); + var $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + var $other = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=2]'); + assert.strictEqual($general.length, 1, + "should have the channel item with id 1"); + assert.hasAttrValue($general, 'title', 'general', + "should have the title 'general'"); + assert.strictEqual($other.length, 1, + "should have the channel item with id 2"); + assert.hasAttrValue($other, 'title', 'other', + "should have the title 'other'"); + + // click on general + await testUtils.dom.click($general); + + assert.containsNone($general, '.o_mail_thread_typing_icon', + "should no longer show someone is typing in sidebar of discuss"); + + self.simulateIsTyping({ + channelID: 2, + isTyping: true, + partnerID: 42, + widget: discuss, + }); + await testUtils.nextMicrotaskTick(); + + $general = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=1]'); + $other = discuss.$('.o_mail_discuss_sidebar') + .find('.o_mail_discuss_item[data-thread-id=2]'); + assert.containsNone($general, '.o_mail_thread_typing_icon', + "should not have a thread typing icon next to general icon in the sidebar"); + assert.containsOnce($other, '.o_mail_thread_typing_icon', + "should have a thread typing icon next to other icon in the sidebar"); + + discuss.destroy(); }); }); diff --git a/addons/mail/static/tests/document_thread_window_tests.js b/addons/mail/static/tests/document_thread_window_tests.js index 93095362306..51daf3a23b1 100644 --- a/addons/mail/static/tests/document_thread_window_tests.js +++ b/addons/mail/static/tests/document_thread_window_tests.js @@ -87,7 +87,7 @@ QUnit.module('Document Thread', { }, }); -QUnit.test('open a document thread in a thread window', function (assert) { +QUnit.test('open a document thread in a thread window', async function (assert) { assert.expect(6); var messagingMenu = new MessagingMenu(); @@ -107,13 +107,13 @@ QUnit.test('open a document thread in a thread window', function (assert) { return this._super.apply(this, arguments); }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); // toggle the messaging menu and open the documentThread - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display one preview"); - testUtils.dom.click(messagingMenu.$('.o_mail_preview')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); assert.strictEqual($('.o_thread_window').length, 1, "should have open the DocumentThread in a thread window"); @@ -125,7 +125,7 @@ QUnit.test('open a document thread in a thread window', function (assert) { messagingMenu.destroy(); }); -QUnit.test('expand a document thread window', function (assert) { +QUnit.test('expand a document thread window', async function (assert) { assert.expect(4); var messagingMenu = new MessagingMenu(); @@ -144,13 +144,13 @@ QUnit.test('expand a document thread window', function (assert) { }, }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); // toggle the messaging menu and open the documentThread - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display one preview"); - testUtils.dom.click(messagingMenu.$('.o_mail_preview')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); assert.strictEqual($('.o_thread_window').length, 1, "should have open the DocumentThread in a thread window"); @@ -158,12 +158,12 @@ QUnit.test('expand a document thread window', function (assert) { 'Open document', "button should have correct title"); // click to expand - testUtils.dom.click($('.o_thread_window .o_thread_window_expand')); + await testUtils.dom.click($('.o_thread_window .o_thread_window_expand')); messagingMenu.destroy(); }); -QUnit.test('post messages in a document thread window', function (assert) { +QUnit.test('post messages in a document thread window', async function (assert) { assert.expect(8); var newMessageBody = 'Some message'; @@ -193,7 +193,7 @@ QUnit.test('post messages in a document thread window', function (assert) { // add the message to the fake DB this.data['mail.message'].records.push(newMessage); this.data['some.res.model'].records[0].message_ids.push(6783); - return $.when(6783); + return Promise.resolve(6783); } return this._super.apply(this, arguments); }, @@ -205,73 +205,74 @@ QUnit.test('post messages in a document thread window', function (assert) { "should write sent message in local storage, to share info with other tabs"); } }, true); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); // toggle the messaging menu and open the documentThread - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display one preview"); - testUtils.dom.click(messagingMenu.$('.o_mail_preview')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); assert.strictEqual($('.o_thread_window').length, 1, "should have open the DocumentThread in a thread window"); assert.strictEqual($('.o_thread_window .o_thread_message').length, 2, "should display 2 messages in the thread window"); // post a message - $('.o_thread_window .o_composer_text_field') - .val(newMessageBody) - .trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); - + await testUtils.fields.editAndTrigger($('.o_thread_window .o_composer_text_field'), newMessageBody, + [$.Event('keydown', {which: $.ui.keyCode.ENTER})]); assert.strictEqual($('.o_thread_window .o_thread_message').length, 3, "should display 3 messages in the thread window"); messagingMenu.destroy(); }); -QUnit.test('open, fold, unfold and close a document thread window', function (assert) { +QUnit.test('open, fold, unfold and close a document thread window', async function (assert) { assert.expect(8); - var messagingMenu = new MessagingMenu(); testUtils.mock.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, session: this.session, }); + await testUtils.nextTick(); testUtils.mock.intercept(messagingMenu, 'call_service', function (ev) { if (ev.data.service === 'local_storage' && ev.data.method === 'setItem') { - assert.step(ev.data.args); + assert.step(JSON.stringify(ev.data.args)); } }, true); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); // toggle the messaging menu and open the documentThread - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display one preview"); - testUtils.dom.click(messagingMenu.$('.o_mail_preview')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); assert.strictEqual($('.o_thread_window').length, 1, "should have open the DocumentThread in a thread window"); assert.strictEqual($('.o_thread_window .o_thread_message').length, 2, "should display 2 messages in the thread window"); // fold and unfold thread window - testUtils.dom.click($('.o_thread_window .o_thread_window_title')); - testUtils.dom.click($('.o_thread_window .o_thread_window_title')); + await testUtils.dom.click($('.o_thread_window .o_thread_window_title')); + await testUtils.nextTick(); + await testUtils.dom.click($('.o_thread_window .o_thread_window_title')); + await testUtils.nextTick(); // close thread window - testUtils.dom.click($('.o_thread_window .o_thread_window_close')); + await testUtils.dom.click($('.o_thread_window .o_thread_window_close')); + await testUtils.nextTick(); assert.verifySteps([ - ['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "open"}}], - ['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "folded"}}], - ['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "open"}}], - ['mail.document_threads_state', {"some.res.model_1": {"name": "Some Record", "windowState": "closed"}}], + '["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"open"}}]', + '["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"folded"}}]', + '["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"open"}}]', + '["mail.document_threads_state",{"some.res.model_1":{"name":"Some Record","windowState":"closed"}}]', ]); messagingMenu.destroy(); }); -QUnit.test('do not open thread window on fetch message failure', function (assert) { +QUnit.test('do not open thread window on fetch message failure', async function (assert) { // this may happen when the user receives a notification from a document // that he does not have access rights at the moment. assert.expect(4); @@ -284,17 +285,17 @@ QUnit.test('do not open thread window on fetch message failure', function (asser mockRPC: function (route, args) { if (args.method === 'read' && args.model === 'some.res.model' && args.args[0][0] === 1) { assert.step('some.res.model/1/read'); - return $.Deferred().reject(); // simulate failure + return Promise.reject(); // simulate failure } return this._super.apply(this, arguments); }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview'); - testUtils.dom.click(messagingMenu.$('.o_mail_preview')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); assert.verifySteps(['some.res.model/1/read']); assert.strictEqual($('.o_thread_window').length, 0, "should not have open the DocumentThread in a thread window on fetch messages failure"); diff --git a/addons/mail/static/tests/document_viewer_tests.js b/addons/mail/static/tests/document_viewer_tests.js index 87fa2dbed94..5d3dcf7069f 100644 --- a/addons/mail/static/tests/document_viewer_tests.js +++ b/addons/mail/static/tests/document_viewer_tests.js @@ -20,16 +20,16 @@ var createViewer = function (params) { var mockRPC = function (route) { if (route === '/web/static/lib/pdfjs/web/viewer.html?file=/web/content/1?model%3Dir.attachment') { - return $.when(); + return Promise.resolve(); } if (route === 'https://www.youtube.com/embed/FYqW0Gdwbzk') { - return $.when(); + return Promise.resolve(); } if (route === '/web/content/4?model%3Dir.attachment') { - return $.when(); + return Promise.resolve(); } if (route === '/web/image/6?unique=1&signature=999&model=ir.attachment') { - return $.when(); + return Promise.resolve(); } }; testUtils.mock.addMockEnvironment(parent, { @@ -51,14 +51,15 @@ var createViewer = function (params) { $target = $('body'); $target.addClass('debug'); } - viewer.appendTo($target); // actually destroy the parent when the viewer is destroyed viewer.destroy = function () { delete viewer.destroy; parent.destroy(); }; - return viewer; + return viewer.appendTo($target).then(function() { + return viewer; + }); }; QUnit.module('DocumentViewer', { @@ -74,10 +75,10 @@ QUnit.module('DocumentViewer', { }, }, function () { - QUnit.test('basic rendering', function (assert) { + QUnit.test('basic rendering', async function (assert) { assert.expect(7); - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 1, attachments: this.attachments, }); @@ -100,11 +101,11 @@ QUnit.module('DocumentViewer', { viewer.destroy(); }); - QUnit.test('Document Viewer Youtube', function (assert) { + QUnit.test('Document Viewer Youtube', async function (assert) { assert.expect(3); var youtubeURL = 'https://www.youtube.com/embed/FYqW0Gdwbzk'; - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 2, attachments: this.attachments, mockRPC: function (route) { @@ -123,10 +124,10 @@ QUnit.module('DocumentViewer', { viewer.destroy(); }); - QUnit.test('Document Viewer html/(txt)', function (assert) { + QUnit.test('Document Viewer html/(txt)', async function (assert) { assert.expect(2); - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 4, attachments: this.attachments, }); @@ -139,10 +140,10 @@ QUnit.module('DocumentViewer', { viewer.destroy(); }); - QUnit.test('Document Viewer mp4', function (assert) { + QUnit.test('Document Viewer mp4', async function (assert) { assert.expect(2); - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 5, attachments: this.attachments, }); @@ -155,10 +156,10 @@ QUnit.module('DocumentViewer', { viewer.destroy(); }); - QUnit.test('Document Viewer jpg', function (assert) { + QUnit.test('Document Viewer jpg', async function (assert) { assert.expect(2); - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 6, attachments: this.attachments, }); @@ -171,10 +172,10 @@ QUnit.module('DocumentViewer', { viewer.destroy(); }); - QUnit.test('is closable by button', function (assert) { + QUnit.test('is closable by button', async function (assert) { assert.expect(3); - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 6, attachments: this.attachments, }); @@ -184,15 +185,15 @@ QUnit.module('DocumentViewer', { assert.containsOnce(viewer, '.o_close_btn', "should have a close button"); - testUtils.dom.click(viewer.$('.o_close_btn')); + await testUtils.dom.click(viewer.$('.o_close_btn')); assert.ok(viewer.isDestroyed(), 'viewer should be destroyed'); }); - QUnit.test('is closable by clicking on the wrapper', function (assert) { + QUnit.test('is closable by clicking on the wrapper', async function (assert) { assert.expect(3); - var viewer = createViewer({ + var viewer = await createViewer({ attachmentID: 6, attachments: this.attachments, }); @@ -202,7 +203,7 @@ QUnit.module('DocumentViewer', { assert.containsOnce(viewer, '.o_viewer_img_wrapper', "should have a wrapper"); - testUtils.dom.click(viewer.$('.o_viewer_img_wrapper')); + await testUtils.dom.click(viewer.$('.o_viewer_img_wrapper')); assert.ok(viewer.isDestroyed(), 'viewer should be destroyed'); }); diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 54ceae92ce3..32203db5dfb 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -183,29 +183,29 @@ MockServer.include({ _performRpc: function (route, args) { // routes if (route === '/mail/init_messaging') { - return $.when(this._mockInitMessaging(args)); + return Promise.resolve(this._mockInitMessaging(args)); } // methods if (args.method === 'channel_fetch_listeners') { - return $.when([]); + return Promise.resolve([]); } if (args.method === 'channel_fetch_preview') { - return $.when(this._mockChannelFetchPreview(args)); + return Promise.resolve(this._mockChannelFetchPreview(args)); } if (args.method === 'channel_minimize') { - return $.when(); + return Promise.resolve(); } if (args.method === 'channel_seen') { - return $.when(); + return Promise.resolve(); } if (args.method === 'channel_fetched') { - return $.when(); + return Promise.resolve(); } if (args.method === 'message_fetch') { - return $.when(this._mockMessageFetch(args)); + return Promise.resolve(this._mockMessageFetch(args)); } if (args.method === 'message_format') { - return $.when(this._mockMessageFormat(args)); + return Promise.resolve(this._mockMessageFormat(args)); } if (args.method === 'activity_format') { var res = this._mockRead(args.model, args.args, args.kwargs); @@ -217,19 +217,19 @@ MockServer.include({ } return record; }); - return $.when(res); + return Promise.resolve(res); } if (args.method === 'set_message_done') { - return $.when(this._mockSetMessageDone(args)); + return Promise.resolve(this._mockSetMessageDone(args)); } if (args.method === 'moderate') { - return $.when(this._mockModerate(args)); + return Promise.resolve(this._mockModerate(args)); } if (args.method === 'notify_typing') { - return $.when(); + return Promise.resolve(); } if (args.method === 'set_message_done') { - return $.when(); + return Promise.resolve(); } return this._super(route, args); }, diff --git a/addons/mail/static/tests/helpers/test_utils.js b/addons/mail/static/tests/helpers/test_utils.js index 063e3a9eb03..af86d3c2e45 100644 --- a/addons/mail/static/tests/helpers/test_utils.js +++ b/addons/mail/static/tests/helpers/test_utils.js @@ -26,9 +26,9 @@ var Widget = require('web.Widget'); * This is async due to mail_manager/mail_service that needs to be ready. * * @param {Object} params - * @return {$.Promise} resolved with the discuss widget + * @return {Promise} resolved with the discuss widget */ -function createDiscuss(params) { +async function createDiscuss(params) { var Parent = Widget.extend({ do_push_state: function () {}, }); @@ -49,7 +49,7 @@ function createDiscuss(params) { parent.destroy(); }; - return discuss.appendTo($(selector)).then(function () { + return discuss.appendTo($(selector)).then(function () { return discuss; }); } diff --git a/addons/mail/static/tests/mail_status_manager_tests.js b/addons/mail/static/tests/mail_status_manager_tests.js index a1ae329c9dc..be769e8d0b0 100644 --- a/addons/mail/static/tests/mail_status_manager_tests.js +++ b/addons/mail/static/tests/mail_status_manager_tests.js @@ -38,7 +38,7 @@ QUnit.test('simple set im_status', function (assert) { parent.destroy(); }); -QUnit.test('multi get_im_status', function (assert) { +QUnit.test('multi get_im_status', async function (assert) { assert.expect(9); var readCount = 0; var parent = testUtils.createParent({ @@ -52,7 +52,7 @@ QUnit.test('multi get_im_status', function (assert) { assert.deepEqual(args.args[0], [2,3]); assert.deepEqual(args.args[1], ['id', 'im_status']); readCount++; - return $.when([ + return Promise.resolve([ {id: 2, im_status: 'away'}, {id: 3, im_status: 'im_partner'} ]); @@ -64,22 +64,27 @@ QUnit.test('multi get_im_status', function (assert) { id: 1, im_status: 'online', }]); + await testUtils.nextTick(); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 1 }), 'online'); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 2 }), undefined); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 3 }), undefined); this.timeoutMock.runPendingTimeouts(); + await testUtils.nextTick(); + assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 1 }), 'online'); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 2 }), 'away'); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 3 }), 'im_partner'); this.timeoutMock.runPendingTimeouts(); + await testUtils.nextTick(); + assert.strictEqual(readCount, 1, 'Only one read on partner should have been performed'); parent.destroy(); }); -QUnit.test('update loop', function (assert) { +QUnit.test('update loop', async function (assert) { assert.expect(12); var readCount = 0; var parent = testUtils.createParent({ @@ -92,7 +97,7 @@ QUnit.test('update loop', function (assert) { assert.deepEqual(args.args[0], [1, 2]); assert.deepEqual(args.args[1], ['id', 'im_status']); readCount++; - return $.when([ + return Promise.resolve([ {"id": 1, "im_status": "online"}, {"id": 2, "im_status": "away"} ]); @@ -106,11 +111,14 @@ QUnit.test('update loop', function (assert) { { id: 2, im_status: 'offline' }, { id: 3, im_status: 'im_partner' }, //shouldn't be updated !!!! ]); + await testUtils.nextTick(); //_updateImStatusLoop should be running at one second per iteration, lets make a minute pass. assert.strictEqual(readCount, 0); this.timeoutMock.addTime(50*1000); + await testUtils.nextTick(); assert.strictEqual(readCount, 0); this.timeoutMock.addTime(1000); + await testUtils.nextTick(); assert.strictEqual(readCount, 1, 'one call should have been made after 50 seconds' ); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 1 }), 'online'); assert.strictEqual(parent.call('mail_service', 'getImStatus', { partnerID: 2 }), 'away'); @@ -118,21 +126,27 @@ QUnit.test('update loop', function (assert) { //simulate change of focus //original listener: $(window).on("blur", this._onWindowFocusChange.bind(this, false); + unload, ... parent.call('mail_service', '_onWindowFocusChange', false); // remove focus from tab + await testUtils.nextTick(); this.timeoutMock.addTime(5*60*1000); // x minutes without focus, no rpc should be done during this time + await testUtils.nextTick(); assert.strictEqual(readCount, 1, 'No more call should have been performed'); //simulate change of focus //original listener: $(window).on("focus", this._onWindowFocusChange.bind(this, true); parent.call('mail_service', '_onWindowFocusChange', true); // give focus to tab + await testUtils.nextTick(); var nextUpdateDelay = this.timeoutMock.getNextTimeoutDelay(); + await testUtils.nextTick(); assert.strictEqual(nextUpdateDelay, 1000, "next update should be done in maximum one second"); this.timeoutMock.addTime(nextUpdateDelay); // one second should be enough + await testUtils.nextTick(); assert.strictEqual(readCount, 2, 'One more call should have been done once tab focused'); this.timeoutMock.runPendingTimeouts(); + await testUtils.nextTick(); parent.destroy(); }); -QUnit.test('update status', function (assert) { +QUnit.test('update status', async function (assert) { // the current solution to look for updatable im_status in dom is not perfect, but waiting for // ability to include widgets in views, this is the most simple solution assert.expect(2); @@ -159,7 +173,7 @@ QUnit.test('update status', function (assert) { throw new Error(_.str.sprintf('No rpc call should be performed: %s, %s \n %s', args.model, args.method, route)); }, }); - statusWidget.appendTo($('#qunit-fixture')); + await statusWidget.appendTo($('#qunit-fixture')); //Render unknow im_status: // set initial status assert.ok(statusWidget.$('.o_updatable_im_status i').hasClass('o_user_online')); @@ -167,6 +181,7 @@ QUnit.test('update status', function (assert) { statusWidget.call('mail_service', 'updateImStatus', [ { id: 1, im_status: 'offline' }, ]); + await testUtils.nextTick(); assert.notOk(statusWidget.$('.o_updatable_im_status i').hasClass('o_user_online')); this.timeoutMock.runPendingTimeouts(); statusWidget.destroy(); diff --git a/addons/mail/static/tests/models/cc_throttle_function_tests.js b/addons/mail/static/tests/models/cc_throttle_function_tests.js index f5deb6c1f3c..5ab00ef6048 100644 --- a/addons/mail/static/tests/models/cc_throttle_function_tests.js +++ b/addons/mail/static/tests/models/cc_throttle_function_tests.js @@ -10,7 +10,7 @@ QUnit.module('mail', { beforeEach: function () { var self = this; - this.timeoutCDDef = $.Deferred(); + this.timeoutCDDef = testUtils.makeTestPromise(); this.patch = function () { self.ORIGINAL_CCTFO_ON_CD_TIMEOUT = CCThrottleFunctionObject.prototype._onCooldownTimeout; @@ -36,7 +36,7 @@ QUnit.test('cancel()', function (assert) { assert.expect(3); var self = this; - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); var step = 1; var widget = testUtils.createParent({ @@ -52,7 +52,7 @@ QUnit.test('cancel()', function (assert) { } step++; - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -80,7 +80,7 @@ QUnit.test('cancel()', function (assert) { self.timeoutCDDef.resolve(); }); this.timeoutCDDef.then(function () { - assert.verifySteps(['__rpc__1'], + assert.verifySteps([], "function should still have been called once after 2nd function call cancelled"); widget.destroy(); done(); @@ -92,8 +92,8 @@ QUnit.test('clear()', function (assert) { assert.expect(5); var self = this; - var def1 = $.Deferred(); - var def2 = $.Deferred(); + var def1 = testUtils.makeTestPromise(); + var def2 = testUtils.makeTestPromise(); var step = 1; var widget = testUtils.createParent({ @@ -110,7 +110,7 @@ QUnit.test('clear()', function (assert) { } step++; - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -135,14 +135,14 @@ QUnit.test('clear()', function (assert) { }).then(function () { cctFunc(2); }).then(function () { - assert.verifySteps(['__rpc__1'], + assert.verifySteps([], "function should still have been called once (due to long throttle)"); cctFunc.clear(); self.timeoutCDDef.resolve(); def2.resolve(); }); - $.when(self.timeoutCDDef, def2).then(function () { - assert.verifySteps(['__rpc__1', '__rpc__2'], + Promise.all([self.timeoutCDDef, def2]).then(function () { + assert.verifySteps(['__rpc__2'], "function should have been called twice after 'clear' (buffered 2nd function call)"); widget.destroy(); done(); diff --git a/addons/mail/static/tests/models/timer_tests.js b/addons/mail/static/tests/models/timer_tests.js index 535c5649ec9..9a66e80ad22 100644 --- a/addons/mail/static/tests/models/timer_tests.js +++ b/addons/mail/static/tests/models/timer_tests.js @@ -1,31 +1,27 @@ odoo.define('mail.model.TimerTests', function (require) { "use strict"; +var testUtils = require("web.test_utils"); var Timer = require('mail.model.Timer'); QUnit.module('mail', {}, function () { QUnit.module('model', {}, function () { QUnit.module('Timer'); -QUnit.test('start()', function (assert) { - var done = assert.async(); +QUnit.test('start()', async function (assert) { assert.expect(2); - var def = $.Deferred(); - var func = function () { - def.resolve(); - }; + var prom = testUtils.makeTestPromise(); var t = new Timer({ duration: 0, - onTimeout: func, + onTimeout: prom.resolve, }); assert.verifySteps([], "should not have called the function"); t.start(); - def.then(function () { + await prom.then(function () { assert.ok(true, "should have called the function"); - done(); }); }); @@ -51,11 +47,10 @@ QUnit.test('clear()', function (assert) { }, 0); }); -QUnit.test('reset()', function (assert) { - var done = assert.async(); +QUnit.test('reset()', async function (assert) { assert.expect(4); - var def = $.Deferred(); + var prom = testUtils.makeTestPromise(); var func = function () { assert.step('function_called'); }; @@ -67,19 +62,17 @@ QUnit.test('reset()', function (assert) { t.start(); // Called after 1st timer timeout + await testUtils.nextTick(); + assert.verifySteps(['function_called'], "should have called the function once"); + t.reset(); + // Called after 2nd timer timeout setTimeout(function () { - assert.verifySteps(['function_called'], "should have called the function once"); - t.reset(); - // Called after 2nd timer timeout - setTimeout(function () { - def.resolve(); - }, 0); + prom.resolve(); }, 0); - def.then(function () { - assert.verifySteps(['function_called', 'function_called'], + await prom.then(function () { + assert.verifySteps(['function_called'], "should have called the function twice (2nd time from reset)"); - done(); }); }); diff --git a/addons/mail/static/tests/systray/systray_activity_menu_tests.js b/addons/mail/static/tests/systray/systray_activity_menu_tests.js index abf4dc6daf7..b2f76feb832 100644 --- a/addons/mail/static/tests/systray/systray_activity_menu_tests.js +++ b/addons/mail/static/tests/systray/systray_activity_menu_tests.js @@ -76,7 +76,7 @@ QUnit.module('ActivityMenu', { } }); -QUnit.test('activity menu widget: menu with no records', function (assert) { +QUnit.test('activity menu widget: menu with no records', async function (assert) { assert.expect(1); var activityMenu = new ActivityMenu(); @@ -84,17 +84,18 @@ QUnit.test('activity menu widget: menu with no records', function (assert) { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { - return $.when([]); + return Promise.resolve([]); } return this._super(route, args); }, }); - activityMenu.appendTo($('#qunit-fixture')); - assert.hasClass(activityMenu.$('.o_no_activity'),'o_no_activity', "should not have instance of widget"); + await activityMenu.appendTo($('#qunit-fixture')); + await testUtils.nextTick(); + assert.containsOnce(activityMenu, '.o_no_activity'); activityMenu.destroy(); }); -QUnit.test('activity menu widget: activity menu with 3 records', function (assert) { +QUnit.test('activity menu widget: activity menu with 3 records', async function (assert) { assert.expect(10); var self = this; var activityMenu = new ActivityMenu(); @@ -102,12 +103,13 @@ QUnit.test('activity menu widget: activity menu with 3 records', function (asser services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { - return $.when(self.data['mail.activity.menu']['records']); + return Promise.resolve(self.data['mail.activity.menu']['records']); } return this._super(route, args); }, }); - activityMenu.appendTo($('#qunit-fixture')); + await activityMenu.appendTo($('#qunit-fixture')); + await testUtils.nextTick(); assert.hasClass(activityMenu.$el, 'o_mail_systray_item', 'should be the instance of widget'); // the assertion below has not been replace because there are includes of ActivityMenu that modify the length. assert.ok(activityMenu.$('.o_mail_preview').length); @@ -123,34 +125,34 @@ QUnit.test('activity menu widget: activity menu with 3 records', function (asser context = { search_default_activities_overdue: 1, }; - testUtils.dom.click(activityMenu.$('.dropdown-toggle')); + await testUtils.dom.click(activityMenu.$('.dropdown-toggle')); assert.hasClass(activityMenu.$el, 'show', 'ActivityMenu should be open'); - testUtils.dom.click(activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='overdue']")); + await testUtils.dom.click(activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='overdue']")); assert.doesNotHaveClass(activityMenu.$el, 'show', 'ActivityMenu should be closed'); // case 2: click on "today" context = { search_default_activities_today: 1, }; - testUtils.dom.click(activityMenu.$('.dropdown-toggle')); - testUtils.dom.click(activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='today']")); + await testUtils.dom.click(activityMenu.$('.dropdown-toggle')); + await testUtils.dom.click(activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='today']")); // case 3: click on "future" context = { search_default_activities_upcoming_all: 1, }; - testUtils.dom.click(activityMenu.$('.dropdown-toggle')); - testUtils.dom.click(activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='upcoming_all']")); + await testUtils.dom.click(activityMenu.$('.dropdown-toggle')); + await testUtils.dom.click(activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='upcoming_all']")); // case 4: click anywere else context = { search_default_activities_overdue: 1, search_default_activities_today: 1, }; - testUtils.dom.click(activityMenu.$('.dropdown-toggle')); - testUtils.dom.click(activityMenu.$(".o_mail_systray_dropdown_items > div[data-model_name='Issue']")); + await testUtils.dom.click(activityMenu.$('.dropdown-toggle')); + await testUtils.dom.click(activityMenu.$(".o_mail_systray_dropdown_items > div[data-model_name='Issue']")); activityMenu.destroy(); }); -QUnit.test('activity menu widget: activity view icon', function (assert) { +QUnit.test('activity menu widget: activity view icon', async function (assert) { assert.expect(10); var self = this; var activityMenu = new ActivityMenu(); @@ -158,12 +160,13 @@ QUnit.test('activity menu widget: activity view icon', function (assert) { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { - return $.when(self.data['mail.activity.menu'].records); + return Promise.resolve(self.data['mail.activity.menu'].records); } return this._super(route, args); }, }); - activityMenu.appendTo($('#qunit-fixture')); + await activityMenu.appendTo($('#qunit-fixture')); + await testUtils.nextTick(); assert.containsN(activityMenu, '.o_mail_activity_action', 2, "widget should have 2 activity view icons"); @@ -189,11 +192,11 @@ QUnit.test('activity menu widget: activity view icon', function (assert) { }, true); // click on the "Issue" activity icon - testUtils.dom.click(activityMenu.$('.dropdown-toggle')); - testUtils.dom.click(activityMenu.$(".o_mail_activity_action[data-model_name='Issue']")); + await testUtils.dom.click(activityMenu.$('.dropdown-toggle')); + await testUtils.dom.click(activityMenu.$(".o_mail_activity_action[data-model_name='Issue']")); // click on the "Note" activity icon - testUtils.dom.click(activityMenu.$(".o_mail_activity_action[data-model_name='Note']")); + await testUtils.dom.click(activityMenu.$(".o_mail_activity_action[data-model_name='Note']")); assert.verifySteps([ 'do_action:Issue', diff --git a/addons/mail/static/tests/systray/systray_messaging_menu_mail_failure_tests.js b/addons/mail/static/tests/systray/systray_messaging_menu_mail_failure_tests.js index c03259697cd..f2c8e95ac29 100644 --- a/addons/mail/static/tests/systray/systray_messaging_menu_mail_failure_tests.js +++ b/addons/mail/static/tests/systray/systray_messaging_menu_mail_failure_tests.js @@ -66,7 +66,7 @@ QUnit.module('MessagingMenu (Mail Failures)', { } }); -QUnit.test('preview of mail failure', function (assert) { +QUnit.test('preview of mail failure', async function (assert) { assert.expect(4); this.data['mail.message'].records = [{ @@ -86,8 +86,8 @@ QUnit.test('preview of mail failure', function (assert) { data: this.data, services: this.services, }); - messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display one preview for the mail failure"); @@ -101,7 +101,7 @@ QUnit.test('preview of mail failure', function (assert) { messagingMenu.destroy(); }); -QUnit.test('preview grouped failures by document', function (assert) { +QUnit.test('preview grouped failures by document', async function (assert) { // If some failures linked to a document refers to a same document, a single // preview should group all those failures assert.expect(3); @@ -142,8 +142,8 @@ QUnit.test('preview grouped failures by document', function (assert) { data: this.data, services: this.services, }); - messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsN(messagingMenu, '.o_mail_preview', 2, "should have a two messaging previews for the mail failures"); @@ -156,7 +156,7 @@ QUnit.test('preview grouped failures by document', function (assert) { messagingMenu.destroy(); }); -QUnit.test('preview grouped failures by document model', function (assert) { +QUnit.test('preview grouped failures by document model', async function (assert) { // If all failures linked to a document model refers to different documents, // a single preview should group all failures that are linked to this // document model @@ -198,8 +198,8 @@ QUnit.test('preview grouped failures by document model', function (assert) { data: this.data, services: this.services, }); - messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsN(messagingMenu, '.o_mail_preview', 2, "should have a two messaging previews for the mail failures"); diff --git a/addons/mail/static/tests/systray/systray_messaging_menu_tests.js b/addons/mail/static/tests/systray/systray_messaging_menu_tests.js index 00abb2d0503..f3ae277ee56 100644 --- a/addons/mail/static/tests/systray/systray_messaging_menu_tests.js +++ b/addons/mail/static/tests/systray/systray_messaging_menu_tests.js @@ -101,7 +101,7 @@ QUnit.module('MessagingMenu', { } }); -QUnit.test('messaging menu widget: menu with no records', function (assert) { +QUnit.test('messaging menu widget: menu with no records', async function (assert) { assert.expect(1); var messagingMenu = new MessagingMenu(); @@ -109,27 +109,27 @@ QUnit.test('messaging menu widget: menu with no records', function (assert) { services: this.services, mockRPC: function (route, args) { if (args.method === 'message_fetch') { - return $.when([]); + return Promise.resolve([]); } return this._super.apply(this, arguments); } }); - messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.hasClass(messagingMenu.$('.o_no_activity'),'o_no_activity', "should not have instance of widget"); messagingMenu.destroy(); }); -QUnit.test('messaging menu widget: messaging menu with 1 record', function (assert) { +QUnit.test('messaging menu widget: messaging menu with 1 record', async function (assert) { assert.expect(3); var messagingMenu = new MessagingMenu(); testUtils.mock.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display a preview"); @@ -146,7 +146,7 @@ QUnit.test('messaging menu widget: messaging menu with 1 record', function (asse messagingMenu.destroy(); }); -QUnit.test('messaging menu widget: open inbox for needaction not linked to any document', function (assert) { +QUnit.test('messaging menu widget: open inbox for needaction not linked to any document', async function (assert) { assert.expect(4); var messagingMenu = new MessagingMenu(); @@ -157,7 +157,7 @@ QUnit.test('messaging menu widget: open inbox for needaction not linked to any d partner_id: 1, }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); // Simulate received needaction message without associated document, // so that we have a message in inbox without a model and a resID @@ -174,7 +174,7 @@ QUnit.test('messaging menu widget: open inbox for needaction not linked to any d messagingMenu.call('bus_service', 'trigger', 'notification', notifications); // Open messaging menu - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); var $firstChannelPreview = messagingMenu.$('.o_mail_preview').first(); @@ -190,7 +190,7 @@ QUnit.test('messaging menu widget: open inbox for needaction not linked to any d assert.step('do_action:' + ev.data.action + ':' + ev.data.options.active_id); } }, true); - testUtils.dom.click($firstChannelPreview); + await testUtils.dom.click($firstChannelPreview); assert.verifySteps( ['do_action:mail.action_discuss:mailbox_inbox'], "should open Discuss with Inbox"); @@ -198,7 +198,7 @@ QUnit.test('messaging menu widget: open inbox for needaction not linked to any d messagingMenu.destroy(); }); -QUnit.test("messaging menu widget: mark as read on thread preview", function ( assert ) { +QUnit.test("messaging menu widget: mark as read on thread preview", async function ( assert ) { assert.expect(8); testUtils.mock.patch(DocumentThread, { @@ -230,8 +230,8 @@ QUnit.test("messaging menu widget: mark as read on thread preview", function ( a data: this.data, }); - messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.hasClass(messagingMenu.$el,'o_mail_systray_item', 'should be the instance of widget'); assert.hasClass(messagingMenu.$el,'show', @@ -247,7 +247,7 @@ QUnit.test("messaging menu widget: mark as read on thread preview", function ( a assert.containsOnce(messagingMenu, '.o_mail_preview_mark_as_read', "should have mark as read icon next to preview"); - testUtils.dom.click(messagingMenu.$(".o_mail_preview_mark_as_read")); + await testUtils.dom.click(messagingMenu.$(".o_mail_preview_mark_as_read")); assert.verifySteps(['markedAsRead'], "the document thread should be marked as read"); @@ -256,7 +256,7 @@ QUnit.test("messaging menu widget: mark as read on thread preview", function ( a messagingMenu.destroy(); }); -QUnit.test('needaction messages in channels should appear, in addition to channel preview', function (assert) { +QUnit.test('needaction messages in channels should appear, in addition to channel preview', async function (assert) { // Let's suppose a channel whose before-last message (msg1) is a needaction, // but not the last message (msg2). In that case, the systray messaging // menu should display the needaction message msg1 in the preview, and the @@ -295,9 +295,9 @@ QUnit.test('needaction messages in channels should appear, in addition to channe partner_id: partnerID, }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsN(messagingMenu, '.o_mail_preview', 2, "should display two previews"); @@ -314,7 +314,7 @@ QUnit.test('needaction messages in channels should appear, in addition to channe messagingMenu.destroy(); }); -QUnit.test('preview of message on a document + mark as read', function (assert) { +QUnit.test('preview of message on a document + mark as read', async function (assert) { assert.expect(7); this.data.initMessaging = { @@ -351,11 +351,11 @@ QUnit.test('preview of message on a document + mark as read', function (assert) partner_id: partnerID, }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '1', "should display a counter of 1 on the messaging menu icon"); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display one preview"); @@ -367,7 +367,7 @@ QUnit.test('preview of message on a document + mark as read', function (assert) assert.strictEqual(messagingMenu.$('.o_preview_unread .o_last_message_preview').text().replace(/\s/g, ''), "Demo:*MessageOnDocument*", "should correctly display the preview"); - testUtils.dom.click(messagingMenu.$('.o_mail_preview_mark_as_read')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview_mark_as_read')); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '0', "should display a counter of 0 on the messaging menu icon"); assert.containsNone(messagingMenu, '.o_mail_preview', @@ -376,7 +376,7 @@ QUnit.test('preview of message on a document + mark as read', function (assert) messagingMenu.destroy(); }); -QUnit.test('update messaging preview on receiving a new message in channel preview', function (assert) { +QUnit.test('update messaging preview on receiving a new message in channel preview', async function (assert) { assert.expect(8); var messagingMenu = new MessagingMenu(); @@ -384,9 +384,9 @@ QUnit.test('update messaging preview on receiving a new message in channel previ services: this.services, data: this.data, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_mail_preview', "should display a single channel preview"); @@ -410,7 +410,7 @@ QUnit.test('update messaging preview on receiving a new message in channel previ }; var notification = [[false, 'mail.channel', 1], data]; messagingMenu.call('bus_service', 'trigger', 'notification', [notification]); - + await testUtils.nextTick(); assert.containsOnce(messagingMenu, '.o_mail_preview', "should still display a single channel preview"); assert.strictEqual(messagingMenu.$('.o_preview_name').text().trim(), "general", @@ -427,7 +427,7 @@ QUnit.test('update messaging preview on receiving a new message in channel previ messagingMenu.destroy(); }); -QUnit.test('preview of inbox message not linked to document + mark as read', function (assert) { +QUnit.test('preview of inbox message not linked to document + mark as read', async function (assert) { assert.expect(17); this.data.initMessaging = { @@ -459,19 +459,16 @@ QUnit.test('preview of inbox message not linked to document + mark as read', fun }, mockRPC: function (route, args) { if (args.method === 'set_message_done') { - assert.step({ - method: 'set_message_done', - messageIDs: args.args[0], - }); + assert.step('method: set_message_done, messageIDs: ' + JSON.stringify(args.args[0])); } return this._super.apply(this, arguments); }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '2', "should display a counter of 2 on the messaging menu icon"); - messagingMenu.$('.dropdown-toggle').click(); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 2, "should display two previews"); @@ -494,11 +491,9 @@ QUnit.test('preview of inbox message not linked to document + mark as read', fun assert.strictEqual($preview2.find('.o_last_message_preview').text().replace(/\s/g, ''), "Demo:*Message2*", "should correctly display the 2nd preview"); - $preview1.find('.o_mail_preview_mark_as_read').click(); - assert.verifySteps([{ - method: 'set_message_done', - messageIDs: [689], - }], "should mark 1st preview as read"); + await testUtils.dom.click($preview1.find('.o_mail_preview_mark_as_read')); + assert.verifySteps(['method: set_message_done, messageIDs: [689]'], + "should mark 1st preview as read"); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '1', "should display a counter of 1 on the messaging menu icon after marking one preview as read"); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 1, @@ -507,14 +502,9 @@ QUnit.test('preview of inbox message not linked to document + mark as read', fun "Demo:*Message2*", "preview 2 should be the remaining one"); $preview2 = messagingMenu.$('.o_mail_preview'); - $preview2.find('.o_mail_preview_mark_as_read').click(); - assert.verifySteps([{ - method: 'set_message_done', - messageIDs: [689], - }, { - method: 'set_message_done', - messageIDs: [690], - }], "should mark 2nd preview as read"); + await testUtils.dom.click($preview2.find('.o_mail_preview_mark_as_read')); + assert.verifySteps(['method: set_message_done, messageIDs: [690]'], + "should mark 2nd preview as read"); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '0', "should display a counter of 0 on the messaging menu icon after marking both previews as read"); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 0, @@ -523,7 +513,7 @@ QUnit.test('preview of inbox message not linked to document + mark as read', fun messagingMenu.destroy(); }); -QUnit.test('grouped preview for needaction messages linked to same document', function (assert) { +QUnit.test('grouped preview for needaction messages linked to same document', async function (assert) { assert.expect(5); // simulate two (read) needaction (mention) messages in channel 'general' @@ -561,9 +551,9 @@ QUnit.test('grouped preview for needaction messages linked to same document', fu partner_id: 44, }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); - messagingMenu.$('.dropdown-toggle').click(); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); var $previews = messagingMenu.$('.o_mail_preview'); assert.strictEqual($previews.length, 2, @@ -586,7 +576,7 @@ QUnit.test('grouped preview for needaction messages linked to same document', fu messagingMenu.destroy(); }); -QUnit.test("messaging menu widget: channel seen notification", function (assert) { +QUnit.test("messaging menu widget: channel seen notification", async function (assert) { assert.expect(4); this.data.initMessaging.channel_slots = { @@ -606,8 +596,8 @@ QUnit.test("messaging menu widget: channel seen notification", function (assert) session: { partner_id: 3 }, }); - messagingMenu.appendTo($('#qunit-fixture')); - messagingMenu.$('.dropdown-toggle').click(); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '1', "should have correct messaging menu counter (1 unread message in channel)"); @@ -625,7 +615,7 @@ QUnit.test("messaging menu widget: channel seen notification", function (assert) [['myDB', 'mail.channel', 1], message] ]; messagingMenu.call('bus_service', 'trigger', 'notification', notifications); - + await testUtils.nextTick(); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '0', "should no longer have a messaging menu counter (no unread message in channel)"); assert.strictEqual(messagingMenu.$('.o_preview_counter').text().replace(/\s/g, ''), '', @@ -634,7 +624,7 @@ QUnit.test("messaging menu widget: channel seen notification", function (assert) messagingMenu.destroy(); }); -QUnit.test("messaging menu widget: no traceback when receiving channel_fetched notification with blank thread window", function (assert) { +QUnit.test("messaging menu widget: no traceback when receiving channel_fetched notification with blank thread window", async function (assert) { assert.expect(3); this.data.initMessaging.channel_slots = { @@ -654,12 +644,12 @@ QUnit.test("messaging menu widget: no traceback when receiving channel_fetched n session: { partner_id: 3 }, }); - messagingMenu.appendTo($('#qunit-fixture')); - messagingMenu.$('.dropdown-toggle').click(); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.containsOnce(messagingMenu, '.o_new_message', "should have button to open blank thread window"); - testUtils.dom.click(messagingMenu.$('.o_new_message')); + await testUtils.dom.click(messagingMenu.$('.o_new_message')); var $threadWindow = $('.o_thread_window'); assert.strictEqual($threadWindow.length, 1, "should have an open thread window"); assert.ok($threadWindow.hasClass('o_thread_less'), "should be a blank thread window"); @@ -674,11 +664,12 @@ QUnit.test("messaging menu widget: no traceback when receiving channel_fetched n [['myDB', 'mail.channel', 1], message] ]; messagingMenu.call('bus_service', 'trigger', 'notification', notifications); + await testUtils.nextTick(); messagingMenu.destroy(); }); -QUnit.test("messaging menu widget: preview with no message should be undated", function ( assert ) { +QUnit.test("messaging menu widget: preview with no message should be undated", async function ( assert ) { assert.expect(2); // remove any message on the channel @@ -691,9 +682,8 @@ QUnit.test("messaging menu widget: preview with no message should be undated", f data: this.data, }); - messagingMenu.appendTo($('#qunit-fixture')); - - messagingMenu.$('.dropdown-toggle').click(); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 1, "should have two previews in messaging menu"); @@ -704,7 +694,7 @@ QUnit.test("messaging menu widget: preview with no message should be undated", f messagingMenu.destroy(); }); -QUnit.test("messaging menu widget: sort dated previews before undated previews", function ( assert ) { +QUnit.test("messaging menu widget: sort dated previews before undated previews", async function ( assert ) { assert.expect(5); var dm = { @@ -722,8 +712,8 @@ QUnit.test("messaging menu widget: sort dated previews before undated previews", data: this.data, }); - messagingMenu.appendTo($('#qunit-fixture')); - messagingMenu.$('.dropdown-toggle').click(); + await messagingMenu.appendTo($('#qunit-fixture')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 2, "should have two previews in messaging menu"); @@ -743,7 +733,7 @@ QUnit.test("messaging menu widget: sort dated previews before undated previews", messagingMenu.destroy(); }); -QUnit.test('global counter with channel previews', function (assert) { +QUnit.test('global counter with channel previews', async function (assert) { assert.expect(5); var channels = [{ @@ -812,11 +802,10 @@ QUnit.test('global counter with channel previews', function (assert) { partner_id: 3, }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); assert.strictEqual(messagingMenu.$('.o_notification_counter').text(), '2', "should display a counter of 2 on the messaging menu icon"); - - messagingMenu.$('.dropdown-toggle').click(); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); assert.strictEqual(messagingMenu.$('.o_mail_preview').length, 3, "should display 3 previews"); @@ -834,7 +823,7 @@ QUnit.test('global counter with channel previews', function (assert) { messagingMenu.destroy(); }); -QUnit.test('messaging menu widget: do not open chat window twice on preview clicked', function (assert) { +QUnit.test('messaging menu widget: do not open chat window twice on preview clicked', async function (assert) { // This test assumes that a condition for opening chat window is to // successfully fetch messages beforehand. assert.expect(4); @@ -844,7 +833,7 @@ QUnit.test('messaging menu widget: do not open chat window twice on preview clic // This is necessary `message_fetch` on mailbox_inbox is required to // display the previews. var lockMessageFetch = false; - var messageFetchDef = $.Deferred(); + var messageFetchDef = testUtils.makeTestPromise(); var messagingMenu = new MessagingMenu(); testUtils.addMockEnvironment(messagingMenu, { @@ -873,19 +862,20 @@ QUnit.test('messaging menu widget: do not open chat window twice on preview clic return this._super.apply(this, arguments); }, }); - messagingMenu.appendTo($('#qunit-fixture')); + await messagingMenu.appendTo($('#qunit-fixture')); // Opening chat window from messaging menu (pending from `messageFetchDef`) - testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); + await testUtils.dom.click(messagingMenu.$('.dropdown-toggle')); lockMessageFetch = true; - testUtils.dom.click(messagingMenu.$('.o_mail_preview')); + await testUtils.dom.click(messagingMenu.$('.o_mail_preview')); messageFetchDef.resolve(); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "should only display a single chat window"); assert.verifySteps([ - 'channel_minimize', 'message_fetch', + 'channel_minimize', ], "should have fetched messages only once"); messagingMenu.destroy(); 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 5a0bc62f880..d5af9cf5357 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 @@ -55,7 +55,7 @@ QUnit.module('Basic', { }, }); -QUnit.test('basic rendering thread window', function (assert) { +QUnit.test('basic rendering thread window', async function (assert) { assert.expect(10); var parent = this.createParent({ @@ -63,9 +63,13 @@ QUnit.test('basic rendering thread window', function (assert) { services: this.services, }); + await testUtils.nextTick(); // detach channel 1, so that it opens corresponding thread window. - parent.call('mail_service', 'getChannel', 1).detach(); + var result = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); + result.detach(); + await testUtils.nextTick(); var $threadWindow = $('.o_thread_window'); assert.strictEqual($threadWindow.length, 1, "thread window should be open"); @@ -98,7 +102,7 @@ QUnit.test('basic rendering thread window', function (assert) { parent.destroy(); }); -QUnit.test('close thread window using ESCAPE key', function (assert) { +QUnit.test('close thread window using ESCAPE key', async function (assert) { assert.expect(5); var parent = this.createParent({ @@ -107,28 +111,31 @@ QUnit.test('close thread window using ESCAPE key', function (assert) { mockRPC: function (route, args) { if (args.method === 'channel_fold') { assert.ok(true, "should call channel_fold"); - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, }); + await testUtils.nextTick(); // get channel instance to link to thread window var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); assert.ok(channel, "there should exist a channel locally with ID 1"); channel.detach(); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "there should be a thread window that is opened"); // focus on the thread window and press ESCAPE - testUtils.dom.click($('.o_thread_window .o_composer_text_field')); + await testUtils.dom.click($('.o_thread_window .o_composer_text_field')); assert.strictEqual(document.activeElement, $('.o_thread_window .o_composer_text_field')[0], "thread window's input should now be focused"); var upKeyEvent = $.Event( "keyup", { which: 27 }); - $('.o_thread_window .o_composer_text_field').trigger(upKeyEvent); + await testUtils.dom.triggerEvents($('.o_thread_window .o_composer_text_field'), [upKeyEvent]); assert.strictEqual($('.o_thread_window').length, 0, "the thread window should be closed"); @@ -136,7 +143,7 @@ QUnit.test('close thread window using ESCAPE key', function (assert) { parent.destroy(); }); -QUnit.test('thread window\'s input can still be focused when the UI is blocked', function (assert) { +QUnit.test('thread window\'s input can still be focused when the UI is blocked', async function (assert) { assert.expect(2); var parent = this.createParent({ @@ -147,11 +154,13 @@ QUnit.test('thread window\'s input can still be focused when the UI is blocked', var $dom = $('#qunit-fixture'); // get channel instance to link to thread window + await testUtils.nextTick(); var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); channel.detach(); - + await testUtils.nextTick(); var $input = $('', {type: 'text'}).appendTo($dom); - testUtils.dom.click($input.focus()); + await testUtils.dom.click($input.focus()); assert.strictEqual(document.activeElement, $input[0], "fake input should be focused"); @@ -160,16 +169,17 @@ QUnit.test('thread window\'s input can still be focused when the UI is blocked', // makes no sense, this test is just about // making sure that the code which forces the // focus on click is not removed - testUtils.dom.click($('.o_thread_window .o_composer_text_field')); + await testUtils.dom.click($('.o_thread_window .o_composer_text_field')); assert.strictEqual(document.activeElement, $('.o_thread_window .o_composer_text_field')[0], "thread window's input should now be focused"); framework.unblockUI(); parent.destroy(); + $input.remove(); }); -QUnit.test('emoji popover should open correctly in thread windows', function (assert) { +QUnit.test('emoji popover should open correctly in thread windows', async function (assert) { assert.expect(1); var parent = this.createParent({ @@ -178,23 +188,20 @@ QUnit.test('emoji popover should open correctly in thread windows', function (as }); // get channel instance to link to thread window + await testUtils.nextTick(); var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); channel.detach(); - + await testUtils.nextTick(); var $emojiButton = $('.o_composer_button_emoji'); - testUtils.dom.click($emojiButton.trigger('focusin').focus()); + await testUtils.dom.click($emojiButton.trigger('focusin').focus()); var $popover = $('.o_mail_emoji_container'); - var done = assert.async(); - // Async is needed as the popover focusout hiding is deferred - setTimeout(function () { - assert.ok($popover.is(':visible'), "emoji popover should have stayed opened"); - parent.destroy(); - done(); - }, 0); + assert.ok($popover.is(':visible'), "emoji popover should have stayed opened"); + parent.destroy(); }); -QUnit.test('do not increase unread counter when receiving message with myself as author', function (assert) { +QUnit.test('do not increase unread counter when receiving message with myself as author', async function (assert) { assert.expect(4); var parent = this.createParent({ @@ -204,9 +211,11 @@ QUnit.test('do not increase unread counter when receiving message with myself as }); // get channel instance to link to thread window + await testUtils.nextTick(); var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); channel.detach(); - + await testUtils.nextTick(); var threadWindowHeaderText = $('.o_thread_window_header').text().replace(/\s/g, ""); assert.strictEqual(threadWindowHeaderText, "#general", @@ -226,6 +235,7 @@ QUnit.test('do not increase unread counter when receiving message with myself as var notification = [[false, 'mail.channel', 1], messageData]; parent.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); threadWindowHeaderText = $('.o_thread_window_header').text().replace(/\s/g, ""); assert.strictEqual(threadWindowHeaderText, "#general", @@ -236,7 +246,7 @@ QUnit.test('do not increase unread counter when receiving message with myself as parent.destroy(); }); -QUnit.test('do not increment unread counter with focus on thread window', function (assert) { +QUnit.test('do not increment unread counter with focus on thread window', async function (assert) { // 'hard' focus means that the user has clicked on the thread window in // order to set the focus on it. assert.expect(2); @@ -248,14 +258,17 @@ QUnit.test('do not increment unread counter with focus on thread window', functi }); // get channel instance to link to thread window + await testUtils.nextTick(); var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); channel.detach(); + await testUtils.nextTick(); assert.strictEqual(channel.getUnreadCounter(), 0, "thread should have unread counter to 0 initially"); // hard focus on thread window composer - testUtils.dom.click($('.o_composer_text_field')); + await testUtils.dom.click($('.o_composer_text_field')); // simulate receiving message from someone else var messageData = { @@ -268,6 +281,7 @@ QUnit.test('do not increment unread counter with focus on thread window', functi }; var notification = [[false, 'mail.channel', 1], messageData]; parent.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); assert.strictEqual(channel.getUnreadCounter(), 0, "thread should not have incremented its unread counter after receiving the message"); @@ -275,7 +289,7 @@ QUnit.test('do not increment unread counter with focus on thread window', functi parent.destroy(); }); -QUnit.test('do not mark as read the newly open thread window from received message', function (assert) { +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'] = { @@ -315,7 +329,7 @@ QUnit.test('do not mark as read the newly open thread window from received messa if (args.method === 'channel_join_and_get_info') { this.data['mail.channel'].records[0].state = 'open'; this.data['mail.channel'].records[0].is_minimized = true; - return $.when(this.data['mail.channel'].records[0]); + return Promise.resolve(this.data['mail.channel'].records[0]); } return this._super.apply(this, arguments); }, @@ -338,7 +352,7 @@ QUnit.test('do not mark as read the newly open thread window from received messa // a new message from a new channel. this.data['mail.channel'].records[0].message_unread_counter++; parent.call('bus_service', 'trigger', 'notification', [notification]); - + await testUtils.nextTick(); var $threadWindow = $('.o_thread_window'); assert.strictEqual($threadWindow.length, 1, "a thread window should be open after receiving a new message on a new DM chat"); @@ -347,14 +361,14 @@ QUnit.test('do not mark as read the newly open thread window from received messa assert.strictEqual($threadWindow.find('.o_thread_window_title').text().replace(/\s/g, ''), '#DM(1)', "open DM chat window should have one unread message"); - testUtils.dom.click($threadWindow.find('.o_thread_composer')); + await testUtils.dom.click($threadWindow.find('.o_thread_composer')); assert.strictEqual($threadWindow.find('.o_thread_window_title').text().replace(/\s/g, ''), '#DM', "open DM chat window should have message marked as read on composer focus"); parent.destroy(); }); -QUnit.test('show document link of message linked to a document', function (assert) { +QUnit.test('show document link of message linked to a document', async function (assert) { assert.expect(6); this.data['mail.channel'] = { @@ -410,8 +424,11 @@ QUnit.test('show document link of message linked to a document', function (asser "no thread window should be open initially"); // get channel instance to link to thread window + await testUtils.nextTick(); var channel = parent.call('mail_service', 'getChannel', 2); + await testUtils.nextTick(); channel.detach(); + await testUtils.nextTick(); var $threadWindow = $('.o_thread_window'); assert.strictEqual($threadWindow.length, 1, @@ -429,7 +446,7 @@ QUnit.test('show document link of message linked to a document', function (asser parent.destroy(); }); -QUnit.test('do not autofocus chat window on receiving new direct message', function (assert) { +QUnit.test('do not autofocus chat window on receiving new direct message', async function (assert) { // Receiving a message doesn't make other input loose focus assert.expect(3); @@ -473,7 +490,7 @@ QUnit.test('do not autofocus chat window on receiving new direct message', funct }], }; - var form = testUtils.createView({ + var form = await testUtils.createView({ View: FormView, model: 'partner', data: this.data, @@ -487,7 +504,7 @@ QUnit.test('do not autofocus chat window on receiving new direct message', funct }, mockRPC: function (route, args) { if (args.method === 'channel_join_and_get_info') { - return $.when(this.data['mail.channel'].records[0]); + return Promise.resolve(this.data['mail.channel'].records[0]); } return this._super.apply(this, arguments); } @@ -512,7 +529,7 @@ QUnit.test('do not autofocus chat window on receiving new direct message', funct this.data['mail.message'].records.push(messageData); var notification = [[false, 'mail.channel', 2], messageData]; form.call('bus_service', 'trigger', 'notification', [notification]); - + await testUtils.nextTick(); assert.ok($('.o_thread_window.o_in_home_menu').length, 'Chat window is opened'); @@ -522,7 +539,7 @@ QUnit.test('do not autofocus chat window on receiving new direct message', funct form.destroy(); }); -QUnit.test('do not auto-focus chat window on receiving new message from new DM', function (assert) { +QUnit.test('do not auto-focus chat window on receiving new message from new DM', async function (assert) { assert.expect(10); var self = this; @@ -564,7 +581,7 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', session: { partner_id: 3 }, mockRPC: function (route, args) { if (args.method === 'channel_join_and_get_info') { - return $.when(_.extend({}, self.data['mail.channel'].records[0], { info: 'join' })); + return Promise.resolve(_.extend({}, self.data['mail.channel'].records[0], { info: 'join' })); } if (args.method === 'channel_minimize') { _.extend(self.data['mail.channel'].records[0], { @@ -594,6 +611,7 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', this.data['mail.message'].records.push(messageData); var notification = [[false, 'mail.channel', 2], messageData]; parent.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "should have DM window open"); @@ -610,6 +628,7 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', }); notification = [[false, 'res.partner', 3], dmInfo]; parent.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "should still have DM window open after receiving DM info from polling"); @@ -621,6 +640,7 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', // simulate receiving detached DM notification (cross-tab synchronization) notification = [[false, 'res.partner', 3], self.data['mail.channel'].records[0]]; parent.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "should still have DM open after receiving detached info from polling"); @@ -632,7 +652,7 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', parent.destroy(); }); -QUnit.test('out-of-office status in thread window', function (assert) { +QUnit.test('out-of-office status in thread window', async function (assert) { assert.expect(1); this.data = { 'mail.message': { @@ -655,8 +675,13 @@ QUnit.test('out-of-office status in thread window', function (assert) { data: this.data, services: this.services, }); + await testUtils.nextTick(); + // detach channel 1, so that it opens corresponding thread window. - parent.call('mail_service', 'getChannel', 1).detach(); + var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); + channel.detach(); + await testUtils.nextTick(); var $threadWindow = $('.o_thread_window'); assert.containsOnce($threadWindow, '.o_out_of_office_text'); @@ -664,7 +689,7 @@ QUnit.test('out-of-office status in thread window', function (assert) { parent.destroy(); }); -QUnit.test('no out-of-office status in thread window', function (assert) { +QUnit.test('no out-of-office status in thread window', async function (assert) { assert.expect(1); this.data = { 'mail.message': { @@ -687,11 +712,17 @@ QUnit.test('no out-of-office status in thread window', function (assert) { data: this.data, services: this.services, }); + await testUtils.nextTick(); + // detach channel 1, so that it opens corresponding thread window. - parent.call('mail_service', 'getChannel', 1).detach(); + var channel = parent.call('mail_service', 'getChannel', 1); + await testUtils.nextTick(); + channel.detach(); + await testUtils.nextTick(); var $threadWindow = $('.o_thread_window'); assert.containsNone($threadWindow, '.o_out_of_office_text'); + parent.destroy(); }); diff --git a/addons/mail/static/tests/thread_window/blank_thread_window_tests.js b/addons/mail/static/tests/thread_window/blank_thread_window_tests.js index 4f103f71163..4a24a80d4c3 100644 --- a/addons/mail/static/tests/thread_window/blank_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/blank_thread_window_tests.js @@ -53,8 +53,8 @@ QUnit.module('Blank', { * functions end. * * @param {Object} params - * @param {$.Deferred} params.selectDef - * @param {$.Deferred} params.sourceDef + * @param {Promise} params.selectDef + * @param {Promise} params.sourceDef */ this.patchAutocomplete = function (params) { var selectDef = params.selectDef; @@ -87,7 +87,7 @@ QUnit.module('Blank', { }, }); -QUnit.test('basic rendering blank thread window', function (assert) { +QUnit.test('basic rendering blank thread window', async function (assert) { assert.expect(5); var parent = this.createParent({ @@ -97,7 +97,7 @@ QUnit.test('basic rendering blank thread window', function (assert) { // open blank thread window parent.call('mail_service', 'openBlankThreadWindow'); - + await testUtils.nextMicrotaskTick(); assert.strictEqual($('.o_thread_window').length, 1, "should have a thread window open"); assert.strictEqual($('.o_thread_window_title').text().trim(), @@ -114,7 +114,7 @@ QUnit.test('basic rendering blank thread window', function (assert) { parent.destroy(); }); -QUnit.test('close blank thread window', function (assert) { +QUnit.test('close blank thread window', async function (assert) { assert.expect(1); var parent = this.createParent({ @@ -124,8 +124,9 @@ QUnit.test('close blank thread window', function (assert) { // open blank thread window parent.call('mail_service', 'openBlankThreadWindow'); + await testUtils.nextMicrotaskTick(); - testUtils.dom.click($('.o_thread_window_close')); + await testUtils.dom.click($('.o_thread_window_close')); assert.strictEqual($('.o_thread_window').length, 0, "blank thread window should be closed"); @@ -133,10 +134,10 @@ QUnit.test('close blank thread window', function (assert) { parent.destroy(); }); -QUnit.test('fold blank thread window', function (assert) { +QUnit.test('fold blank thread window', async function (assert) { // This test requires full height of thread windows when they are open. // (e.g. 400px) - assert.expect(2); + assert.expect(3); var parent = this.createParent({ data: this.data, @@ -153,34 +154,37 @@ QUnit.test('fold blank thread window', function (assert) { // Open blank thread window parent.call('mail_service', 'openBlankThreadWindow'); + await testUtils.nextTick(); + + assert.containsOnce(document.body, '.o_thread_window'); assert.strictEqual($('.o_thread_window').css('height'), HEIGHT_OPEN, "blank thread window should be open"); if ($('.o_thread_window').css('height') !== HEIGHT_OPEN) { console.warn('Assertion above may fail due to too narrow height of browser'); } - testUtils.dom.click($('.o_thread_window_title')); + await testUtils.dom.click($('.o_thread_window_title')); assert.strictEqual($('.o_thread_window').css('height'), HEIGHT_FOLDED, - "blank thread window should be open"); + "blank thread window should be folded"); parent.destroy(); testUtils.mock.unpatch(AbstractThreadWindow); }); -QUnit.test('open new DM chat from blank thread window', function (assert) { +QUnit.test('open new DM chat from blank thread window', async function (assert) { assert.expect(6); var done = assert.async(); var self = this; - var selectDef = $.Deferred(); - var sourceDef = $.Deferred(); + var selectDef = testUtils.makeTestPromise(); + var sourceDef = testUtils.makeTestPromise(); this.patchAutocomplete({ selectDef: selectDef, sourceDef: sourceDef, }); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); this.data['res.partner'] = { fields: {}, @@ -193,13 +197,13 @@ QUnit.test('open new DM chat from blank thread window', function (assert) { mockRPC: function (route, args) { if (args.method === 'im_search') { def.resolve(); - return $.when([ + return Promise.resolve([ { id: 1, name: 'DemoUser1' }, { id: 2, name: 'DemoUser2', } ]); } if (args.method === 'channel_get_and_minimize') { - return $.when({ + return Promise.resolve({ channel_type: 'chat', direct_partner: [{ id: args.args[0][0], name: 'DemoUser1', im_status: '' }], id: 50, @@ -213,19 +217,20 @@ QUnit.test('open new DM chat from blank thread window', function (assert) { // open blank thread window parent.call('mail_service', 'openBlankThreadWindow'); + await testUtils.nextMicrotaskTick(); + await testUtils.fields.editAndTrigger($('.o_thread_search_input input'), 'D', 'keydown'); + await testUtils.nextTick(); + await Promise.all([sourceDef, def]); + await testUtils.nextTick(); + assert.strictEqual($('.ui-menu-item a').length, 2, + "should suggest 2 partners for DM"); + assert.strictEqual($('.ui-menu-item a').eq(0).text(), "DemoUser1", + "first suggestion should be 'DemoUser1'"); + assert.strictEqual($('.ui-menu-item a').eq(1).text(), "DemoUser2", + "second suggestion should be 'DemoUser2'"); - $('.o_thread_search_input input').val("D").trigger('keydown'); - - $.when(sourceDef, def).then(function () { - assert.strictEqual($('.ui-menu-item a').length, 2, - "should suggest 2 partners for DM"); - assert.strictEqual($('.ui-menu-item a').eq(0).text(), "DemoUser1", - "first suggestion should be 'DemoUser1'"); - assert.strictEqual($('.ui-menu-item a').eq(1).text(), "DemoUser2", - "second suggestion should be 'DemoUser2'"); - - testUtils.dom.click($('.ui-menu-item a').eq(0)); - }); + await testUtils.dom.clickFirst($('.ui-menu-item a'),{allowInvisible:true}); + await testUtils.nextTick(); selectDef.then(function () { assert.strictEqual($('.o_thread_window').length, 1, "should be a single window"); @@ -240,22 +245,22 @@ QUnit.test('open new DM chat from blank thread window', function (assert) { }); }); -QUnit.test('open already detached DM chat from blank thread window', function (assert) { +QUnit.test('open already detached DM chat from blank thread window', async function (assert) { // when opening an already detach DM chat from the blank thread window, // the blank thread window should disappear assert.expect(6); var done = assert.async(); var self = this; - var selectDef = $.Deferred(); - var sourceDef = $.Deferred(); + var selectDef = testUtils.makeTestPromise(); + var sourceDef = testUtils.makeTestPromise(); this.patchAutocomplete({ selectDef: selectDef, sourceDef: sourceDef, }); - var def = $.Deferred(); + var def = testUtils.makeTestPromise(); this.data['res.partner'] = { fields: {}, @@ -275,7 +280,7 @@ QUnit.test('open already detached DM chat from blank thread window', function (a mockRPC: function (route, args) { if (args.method === 'im_search') { def.resolve(); - return $.when([ + return Promise.resolve([ { id: 1, name: 'DemoUser1' }, ]); } @@ -283,6 +288,7 @@ QUnit.test('open already detached DM chat from blank thread window', function (a }, }); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_window').length, 1, "should be a single window"); assert.strictEqual($('.o_thread_window_title').text().trim(), "DemoUser1", @@ -292,15 +298,16 @@ QUnit.test('open already detached DM chat from blank thread window', function (a // open blank thread window parent.call('mail_service', 'openBlankThreadWindow'); - + await testUtils.nextMicrotaskTick(); assert.strictEqual($('.o_thread_window').length, 2, "should have two thread windows open"); + await testUtils.fields.editAndTrigger($('.o_thread_search_input input'), 'D', 'keydown'); - $('.o_thread_search_input input').val("D").trigger('keydown'); - $.when(sourceDef, def).then(function () { - testUtils.dom.click($('.ui-menu-item a').eq(0)); - }); + await Promise.all([sourceDef, def]); + await testUtils.nextTick(); + await testUtils.dom.click($('.ui-menu-item a'),{allowInvisible:true}); + await testUtils.nextTick(); selectDef.then(function () { assert.strictEqual($('.o_thread_window').length, 1, "should now have a single thread window open"); diff --git a/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js b/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js index b87dbe0f794..691e37df362 100644 --- a/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js @@ -48,12 +48,15 @@ QUnit.module('Hidden', { }; }, afterEach: function () { + // remove thread window dropup appended by mail service + var $target = $(this.services.mail_service.prototype.THREAD_WINDOW_APPENDTO); + $target.find('.o_thread_window_dropdown').remove(); // reset thread window append to body this.services.mail_service.prototype.THREAD_WINDOW_APPENDTO = 'body'; }, }); -QUnit.test('hidden thread windows dropdown when not enough horizontal space (version 1)', function (assert) { +QUnit.test('hidden thread windows dropdown when not enough horizontal space (version 1)', async function (assert) { // This test has hard-set global width, so that it does not depend on // the width of the screen: at most 2 thread windows visible at the // same time, and additional thread windows are in hidden while and @@ -87,9 +90,10 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver data: this.data, services: this.services, }); - + await testUtils.nextTick(); parent.call('mail_service', 'getChannel', 0).detach(); parent.call('mail_service', 'getChannel', 1).detach(); + await testUtils.nextTick(); var $visibleThreadWindows = $('.o_thread_window:not(.o_thread_window_dropdown, .o_hidden)'); @@ -106,6 +110,7 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver // detach a channel so that it exceeds available slots parent.call('mail_service', 'getChannel', 2).detach(); + await testUtils.nextTick(); // update list of visible thread windows $visibleThreadWindows = $('.o_thread_window:not(.o_thread_window_dropdown, .o_hidden)'); @@ -131,7 +136,7 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver testUtils.mock.unpatch(this.services.mail_service); }); -QUnit.test('hidden thread windows dropdown when not enough horizontal space (version 2)', function (assert) { +QUnit.test('hidden thread windows dropdown when not enough horizontal space (version 2)', async function (assert) { // This is almost the same test as the one before, except there are // at most 3 thread windows visible, and 2 thread windows are visible // when it shows the 'hidden thread window' button. @@ -171,10 +176,11 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver data: this.data, services: this.services, }); - + await testUtils.nextTick(); parent.call('mail_service', 'getChannel', 0).detach(); parent.call('mail_service', 'getChannel', 1).detach(); parent.call('mail_service', 'getChannel', 2).detach(); + await testUtils.nextTick(); var $visibleThreadWindows = $('.o_thread_window:not(.o_thread_window_dropdown, .o_hidden)'); @@ -193,6 +199,7 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver // detach a channel so that it exceeds available slots parent.call('mail_service', 'getChannel', 3).detach(); + await testUtils.nextTick(); // update list of visible thread windows $visibleThreadWindows = $('.o_thread_window:not(.o_thread_window_dropdown, .o_hidden)'); diff --git a/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js b/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js index b35405a2243..732a5fe7938 100644 --- a/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js @@ -84,7 +84,7 @@ QUnit.module('Moderation', { }, }); -QUnit.test('moderator: moderated channel with pending moderation message', function (assert) { +QUnit.test('moderator: moderated channel with pending moderation message', async function (assert) { assert.expect(5); this.data.initMessaging = { @@ -114,9 +114,10 @@ QUnit.test('moderator: moderated channel with pending moderation message', funct data: this.data, services: this.services, }); - + await testUtils.nextTick(); // detach channel 1, so that it opens corresponding thread window. parent.call('mail_service', 'getChannel', 1).detach(); + await testUtils.nextTick(); assert.strictEqual($('.o_thread_message ').length, 1, "should display a message in the thread window"); diff --git a/addons/mail/static/tests/thread_window/typing_thread_window_tests.js b/addons/mail/static/tests/thread_window/typing_thread_window_tests.js index 549cfdb7184..be2bd4beec8 100644 --- a/addons/mail/static/tests/thread_window/typing_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/typing_thread_window_tests.js @@ -40,7 +40,7 @@ QUnit.module('Typing', { * @param {integer} params.partnerID * @param {Widget} params.widget a widget that can call the bus_service */ - this.simulateIsTyping = function (params) { + this.simulateIsTyping = async function (params) { var typingData = { info: 'typing_status', partner_id: params.partnerID, @@ -48,6 +48,7 @@ QUnit.module('Typing', { }; var notification = [[false, 'mail.channel', params.channelID], typingData]; params.widget.call('bus_service', 'trigger', 'notification', [notification]); + await testUtils.nextMicrotaskTick(); }; this.createParent = function (params) { @@ -72,7 +73,7 @@ QUnit.module('Typing', { }, }); -QUnit.test('receive typing notification', function (assert) { +QUnit.test('receive typing notification', async function (assert) { assert.expect(4); var parent = this.createParent({ @@ -82,22 +83,24 @@ QUnit.test('receive typing notification', function (assert) { // when receiving an 'is typing' notification, fetch the list of // members of this channel if we haven't done yet. if (args.method === 'channel_fetch_listeners') { - return $.when([ + return Promise.resolve([ { id: 42, name: "Someone" }, ]); } return this._super.apply(this, arguments); }, }); + await testUtils.nextTick(); // detach channel 1, so that it opens corresponding thread window. parent.call('mail_service', 'getChannel', 1).detach(); + await testUtils.nextTick(); var $threadWindow = $('.o_thread_window'); assert.containsNone($threadWindow, '.o_mail_thread_typing_icon', "should not display an icon that someone is typing something on this thread"); - this.simulateIsTyping({ + await this.simulateIsTyping({ channelID: 1, isTyping: true, partnerID: 42, @@ -110,7 +113,7 @@ QUnit.test('receive typing notification', function (assert) { "Someone is typing...", "should display someone is typing something on hover of this icon"); - this.simulateIsTyping({ + await this.simulateIsTyping({ channelID: 1, isTyping: false, partnerID: 42, diff --git a/addons/mail/static/tests/tools/debug_manager_tests.js b/addons/mail/static/tests/tools/debug_manager_tests.js index 8b9312ca733..7671150f0d8 100644 --- a/addons/mail/static/tests/tools/debug_manager_tests.js +++ b/addons/mail/static/tests/tools/debug_manager_tests.js @@ -7,7 +7,7 @@ var createDebugManager = testUtils.createDebugManager; QUnit.module('Mail DebugManager', {}, function () { - QUnit.test("Manage Messages", function (assert) { + QUnit.test("Manage Messages", async function (assert) { assert.expect(3); var debugManager = createDebugManager({ @@ -47,15 +47,16 @@ QUnit.module('Mail DebugManager', {}, function () { }, modelName: 'testModel', }; - debugManager.update('action', action, view); + await testUtils.nextTick(); + await debugManager.update('action', action, view); var $messageMenu = debugManager.$('a[data-action=getMailMessages]'); assert.strictEqual($messageMenu.length, 1, "should have Manage Message menu item"); assert.strictEqual($messageMenu.text().trim(), "Manage Messages", "should have correct menu item text"); - testUtils.dom.click(debugManager.$('> a')); // open dropdown - testUtils.dom.click($messageMenu); + await testUtils.dom.click(debugManager.$('> a')); // open dropdown + await testUtils.dom.click($messageMenu); debugManager.destroy(); });