diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/thread_needaction_preview | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/thread_needaction_preview')
4 files changed, 774 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js new file mode 100644 index 00000000..b70c8f6b --- /dev/null +++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js @@ -0,0 +1,151 @@ +odoo.define('mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js', function (require) { +'use strict'; + +const components = { + MessageAuthorPrefix: require('mail/static/src/components/message_author_prefix/message_author_prefix.js'), + PartnerImStatusIcon: require('mail/static/src/components/partner_im_status_icon/partner_im_status_icon.js'), +}; +const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js'); +const useStore = require('mail/static/src/component_hooks/use_store/use_store.js'); +const mailUtils = require('mail.utils'); + +const { Component } = owl; +const { useRef } = owl.hooks; + +class ThreadNeedactionPreview extends Component { + + /** + * @override + */ + constructor(...args) { + super(...args); + useShouldUpdateBasedOnProps(); + useStore(props => { + const thread = this.env.models['mail.thread'].get(props.threadLocalId); + const mainThreadCache = thread ? thread.mainCache : undefined; + let lastNeedactionMessageAsOriginThreadAuthor; + let lastNeedactionMessageAsOriginThread; + let threadCorrespondent; + if (thread) { + lastNeedactionMessageAsOriginThread = mainThreadCache.lastNeedactionMessageAsOriginThread; + threadCorrespondent = thread.correspondent; + } + if (lastNeedactionMessageAsOriginThread) { + lastNeedactionMessageAsOriginThreadAuthor = lastNeedactionMessageAsOriginThread.author; + } + return { + isDeviceMobile: this.env.messaging.device.isMobile, + lastNeedactionMessageAsOriginThread: lastNeedactionMessageAsOriginThread ? lastNeedactionMessageAsOriginThread.__state : undefined, + lastNeedactionMessageAsOriginThreadAuthor: lastNeedactionMessageAsOriginThreadAuthor + ? lastNeedactionMessageAsOriginThreadAuthor.__state + : undefined, + thread: thread ? thread.__state : undefined, + threadCorrespondent: threadCorrespondent + ? threadCorrespondent.__state + : undefined, + }; + }); + /** + * Reference of the "mark as read" button. Useful to disable the + * top-level click handler when clicking on this specific button. + */ + this._markAsReadRef = useRef('markAsRead'); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Get the image route of the thread. + * + * @returns {string} + */ + image() { + if (this.thread.moduleIcon) { + return this.thread.moduleIcon; + } + if (this.thread.correspondent) { + return this.thread.correspondent.avatarUrl; + } + if (this.thread.model === 'mail.channel') { + return `/web/image/mail.channel/${this.thread.id}/image_128`; + } + return '/mail/static/src/img/smiley/avatar.jpg'; + } + + /** + * Get inline content of the last message of this conversation. + * + * @returns {string} + */ + get inlineLastNeedactionMessageBody() { + if (!this.thread.lastNeedactionMessage) { + return ''; + } + return mailUtils.htmlToTextContentInline(this.thread.lastNeedactionMessage.prettyBody); + } + + /** + * Get inline content of the last message of this conversation. + * + * @returns {string} + */ + get inlineLastNeedactionMessageAsOriginThreadBody() { + if (!this.thread.lastNeedactionMessageAsOriginThread) { + return ''; + } + return mailUtils.htmlToTextContentInline(this.thread.lastNeedactionMessageAsOriginThread.prettyBody); + } + + /** + * @returns {mail.thread} + */ + get thread() { + return this.env.models['mail.thread'].get(this.props.threadLocalId); + } + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {MouseEvent} ev + */ + _onClick(ev) { + const markAsRead = this._markAsReadRef.el; + if (markAsRead && markAsRead.contains(ev.target)) { + // handled in `_onClickMarkAsRead` + return; + } + this.thread.open(); + if (!this.env.messaging.device.isMobile) { + this.env.messaging.messagingMenu.close(); + } + } + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickMarkAsRead(ev) { + this.env.models['mail.message'].markAllAsRead([ + ['model', '=', this.thread.model], + ['res_id', '=', this.thread.id], + ]); + } + +} + +Object.assign(ThreadNeedactionPreview, { + components, + props: { + threadLocalId: String, + }, + template: 'mail.ThreadNeedactionPreview', +}); + +return ThreadNeedactionPreview; + +}); diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss new file mode 100644 index 00000000..5de87f8b --- /dev/null +++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss @@ -0,0 +1,108 @@ +// ------------------------------------------------------------------ +// Layout +// ------------------------------------------------------------------ + +.o_ThreadNeedactionPreview { + @include o-mail-notification-list-item-layout(); + + &:hover .o_ThreadNeedactionPreview_markAsRead { + opacity: 1; + } +} + +.o_ThreadNeedactionPreview_content { + @include o-mail-notification-list-item-content-layout(); +} + +.o_ThreadNeedactionPreview_core { + @include o-mail-notification-list-item-core-layout(); +} + +.o_ThreadNeedactionPreview_coreItem { + @include o-mail-notification-list-item-core-item-layout(); +} + +.o_ThreadNeedactionPreview_counter { + @include o-mail-notification-list-item-counter-layout(); +} + +.o_ThreadNeedactionPreview_date { + @include o-mail-notification-list-item-date-layout(); +} + +.o_ThreadNeedactionPreview_header { + @include o-mail-notification-list-item-header-layout(); +} + +.o_ThreadNeedactionPreview_image { + @include o-mail-notification-list-item-image-layout(); +} + +.o_ThreadNeedactionPreview_imageContainer { + @include o-mail-notification-list-item-image-container-layout(); +} + +.o_ThreadNeedactionPreview_inlineText { + @include o-mail-notification-list-item-inline-text-layout(); +} + +.o_ThreadNeedactionPreview_markAsRead { + @include o-mail-notification-list-item-mark-as-read-layout(); +} + +.o_ThreadNeedactionPreview_name { + @include o-mail-notification-list-item-name-layout(); +} + +.o_ThreadNeedactionPreview_partnerImStatusIcon { + @include o-mail-notification-list-item-partner-im-status-icon-layout(); +} + +.o_ThreadNeedactionPreview_sidebar { + @include o-mail-notification-list-item-sidebar-layout(); +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_ThreadNeedactionPreview { + @include o-mail-notification-list-item-style(); + background-color: rgba($o-brand-primary, 0.1); + + &:hover { + background-color: rgba($o-brand-primary, 0.2); + + .o_ThreadNeedactionPreview_partnerImStatusIcon { + @include o-mail-notification-list-item-hover-partner-im-status-icon-style(); + } + } +} + +.o_ThreadNeedactionPreview_core { + @include o-mail-notification-list-item-core-style(); +} + +.o_ThreadNeedactionPreview_counter { + @include o-mail-notification-list-item-bold-style(); +} + +.o_ThreadNeedactionPreview_date { + @include o-mail-notification-list-item-date-style(); +} + +.o_ThreadNeedactionPreview_image { + @include o-mail-notification-list-item-image-style(); +} + +.o_ThreadNeedactionPreview_markAsRead { + @include o-mail-notification-list-item-mark-as-read-style(); +} + +.o_ThreadNeedactionPreview_name { + @include o-mail-notification-list-item-bold-style(); +} + +.o_ThreadNeedactionPreview_partnerImStatusIcon { + @include o-mail-notification-list-item-partner-im-status-icon-style(); +} diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml new file mode 100644 index 00000000..3fd33224 --- /dev/null +++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <t t-name="mail.ThreadNeedactionPreview" owl="1"> + <!-- + The preview template is used by the discuss in mobile, and by the systray + menu in order to show preview of threads. + --> + <div class="o_ThreadNeedactionPreview" t-on-click="_onClick" t-att-data-thread-local-id="thread ? thread.localId : undefined"> + <t t-if="thread"> + <div class="o_ThreadNeedactionPreview_sidebar"> + <div class="o_ThreadNeedactionPreview_imageContainer o_ThreadNeedactionPreview_sidebarItem"> + <img class="o_ThreadNeedactionPreview_image" t-att-src="image()" alt="Thread Image"/> + <t t-if="thread.correspondent and thread.correspondent.im_status"> + <PartnerImStatusIcon + class="o_ThreadNeedactionPreview_partnerImStatusIcon" + t-att-class="{ + 'o-mobile': env.messaging.device.isMobile, + }" + partnerLocalId="thread.correspondent.localId" + /> + </t> + </div> + </div> + <div class="o_ThreadNeedactionPreview_content"> + <div class="o_ThreadNeedactionPreview_header"> + <span class="o_ThreadNeedactionPreview_name" t-att-class="{ 'o-mobile': env.messaging.device.isMobile }"> + <t t-esc="thread.displayName"/> + </span> + <span class="o_ThreadNeedactionPreview_counter"> + (<t t-esc="thread.needactionMessagesAsOriginThread.length"/>) + </span> + <span class="o-autogrow"/> + <t t-if="thread.lastNeedactionMessageAsOriginThread"> + <span class="o_ThreadNeedactionPreview_date"> + <t t-esc="thread.lastNeedactionMessageAsOriginThread.date.fromNow()"/> + </span> + </t> + </div> + <div class="o_ThreadNeedactionPreview_core"> + <span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_inlineText" t-att-class="{ 'o-empty': inlineLastNeedactionMessageAsOriginThreadBody.length === 0 }"> + <t t-if="thread.lastNeedactionMessageAsOriginThread and thread.lastNeedactionMessageAsOriginThread.author"> + <MessageAuthorPrefix + messageLocalId="thread.lastNeedactionMessageAsOriginThread.localId" + threadLocalId="thread.localId" + /> + </t> + <t t-esc="inlineLastNeedactionMessageAsOriginThreadBody"/> + </span> + <span class="o-autogrow"/> + <span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_markAsRead fa fa-check" title="Mark as Read" t-on-click="_onClickMarkAsRead" t-ref="markAsRead"/> + </div> + </div> + </t> + </div> + </t> + +</templates> diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js new file mode 100644 index 00000000..ca1fe22c --- /dev/null +++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js @@ -0,0 +1,457 @@ +odoo.define('mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js', function (require) { +'use strict'; + +const components = { + ThreadNeedactionPreview: require('mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js'), +}; + +const { + afterEach, + afterNextRender, + beforeEach, + createRootComponent, + start, +} = require('mail/static/src/utils/test_utils.js'); + +const Bus = require('web.Bus'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('thread_needaction_preview', {}, function () { +QUnit.module('thread_needaction_preview_tests.js', { + beforeEach() { + beforeEach(this); + + this.createThreadNeedactionPreviewComponent = async props => { + await createRootComponent(this, components.ThreadNeedactionPreview, { + props, + target: this.widget.el + }); + }; + + this.start = async params => { + const { afterEvent, env, widget } = await start(Object.assign({}, params, { + data: this.data, + })); + this.afterEvent = afterEvent; + this.env = env; + this.widget = widget; + }; + }, + afterEach() { + afterEach(this); + }, +}); + +QUnit.test('mark as read', async function (assert) { + assert.expect(5); + + this.data['mail.message'].records.push({ + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ + hasChatWindow: true, + hasMessagingMenu: true, + async mockRPC(route, args) { + if (route.includes('mark_all_as_read')) { + assert.step('mark_all_as_read'); + assert.deepEqual( + args.kwargs.domain, + [ + ['model', '=', 'res.partner'], + ['res_id', '=', 11], + ], + "should mark all as read the correct thread" + ); + } + return this._super(...arguments); + }, + }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + assert.containsOnce( + document.body, + '.o_ThreadNeedactionPreview_markAsRead', + "should have 1 mark as read button" + ); + + await afterNextRender(() => + document.querySelector('.o_ThreadNeedactionPreview_markAsRead').click() + ); + assert.verifySteps( + ['mark_all_as_read'], + "should have marked the thread as read" + ); + assert.containsNone( + document.body, + '.o_ChatWindow', + "should not have opened the thread" + ); +}); + +QUnit.test('click on preview should mark as read and open the thread', async function (assert) { + assert.expect(6); + + this.data['mail.message'].records.push({ + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ + hasChatWindow: true, + hasMessagingMenu: true, + async mockRPC(route, args) { + if (route.includes('mark_all_as_read')) { + assert.step('mark_all_as_read'); + assert.deepEqual( + args.kwargs.domain, + [ + ['model', '=', 'res.partner'], + ['res_id', '=', 11], + ], + "should mark all as read the correct thread" + ); + } + return this._super(...arguments); + }, + }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + assert.containsOnce( + document.body, + '.o_ThreadNeedactionPreview', + "should have a preview initially" + ); + assert.containsNone( + document.body, + '.o_ChatWindow', + "should have no chat window initially" + ); + + await afterNextRender(() => + document.querySelector('.o_ThreadNeedactionPreview').click() + ); + assert.verifySteps( + ['mark_all_as_read'], + "should have marked the message as read on clicking on the preview" + ); + assert.containsOnce( + document.body, + '.o_ChatWindow', + "should have opened the thread on clicking on the preview" + ); +}); + +QUnit.test('click on expand from chat window should close the chat window and open the form view', async function (assert) { + assert.expect(8); + + const bus = new Bus(); + bus.on('do-action', null, payload => { + assert.step('do_action'); + assert.strictEqual( + payload.action.res_id, + 11, + "should redirect to the id of the thread" + ); + assert.strictEqual( + payload.action.res_model, + 'res.partner', + "should redirect to the model of the thread" + ); + }); + this.data['mail.message'].records.push({ + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ + env: { bus }, + hasChatWindow: true, + hasMessagingMenu: true, + }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + assert.containsOnce( + document.body, + '.o_ThreadNeedactionPreview', + "should have a preview initially" + ); + await afterNextRender(() => + document.querySelector('.o_ThreadNeedactionPreview').click() + ); + assert.containsOnce( + document.body, + '.o_ChatWindow', + "should have opened the thread on clicking on the preview" + ); + assert.containsOnce( + document.body, + '.o_ChatWindowHeader_commandExpand', + "should have an expand button" + ); + + await afterNextRender(() => + document.querySelector('.o_ChatWindowHeader_commandExpand').click() + ); + assert.containsNone( + document.body, + '.o_ChatWindow', + "should have closed the chat window on clicking expand" + ); + assert.verifySteps( + ['do_action'], + "should have done an action to open the form view" + ); +}); + +QUnit.test('[technical] opening a non-channel chat window should not call channel_fold', async function (assert) { + // channel_fold should not be called when opening non-channels in chat + // window, because there is no server sync of fold state for them. + assert.expect(3); + + this.data['mail.message'].records.push({ + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ + hasChatWindow: true, + hasMessagingMenu: true, + async mockRPC(route, args) { + if (route.includes('channel_fold')) { + const message = "should not call channel_fold when opening a non-channel chat window"; + assert.step(message); + console.error(message); + throw Error(message); + } + return this._super(...arguments); + }, + }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + assert.containsOnce( + document.body, + '.o_ThreadNeedactionPreview', + "should have a preview initially" + ); + assert.containsNone( + document.body, + '.o_ChatWindow', + "should have no chat window initially" + ); + + await afterNextRender(() => + document.querySelector('.o_ThreadNeedactionPreview').click() + ); + assert.containsOnce( + document.body, + '.o_ChatWindow', + "should have opened the chat window on clicking on the preview" + ); +}); + +QUnit.test('preview should display last needaction message preview even if there is a more recent message that is not needaction in the thread', async function (assert) { + assert.expect(2); + + this.data['res.partner'].records.push({ + id: 11, + name: "Stranger", + }); + this.data['mail.message'].records.push({ + author_id: 11, + body: "I am the oldest but needaction", + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.message'].records.push({ + author_id: this.data.currentPartnerId, + body: "I am more recent", + id: 22, + model: 'res.partner', + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ + hasChatWindow: true, + hasMessagingMenu: true, + }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + assert.containsOnce( + document.body, + '.o_ThreadNeedactionPreview_inlineText', + "should have a preview from the last message" + ); + assert.strictEqual( + document.querySelector('.o_ThreadNeedactionPreview_inlineText').textContent, + 'Stranger: I am the oldest but needaction', + "the displayed message should be the one that needs action even if there is a more recent message that is not needaction on the thread" + ); +}); + +QUnit.test('needaction preview should only show on its origin thread', async function (assert) { + assert.expect(2); + + this.data['mail.channel'].records.push({ id: 12 }); + this.data['mail.message'].records.push({ + channel_ids: [12], + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ hasMessagingMenu: true }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + assert.containsOnce( + document.body, + '.o_ThreadNeedactionPreview', + "should have only one preview" + ); + const thread = this.env.models['mail.thread'].findFromIdentifyingData({ + id: 11, + model: 'res.partner', + }); + assert.containsOnce( + document.body, + `.o_ThreadNeedactionPreview[data-thread-local-id="${thread.localId}"]`, + "preview should be on the origin thread" + ); +}); + +QUnit.test('chat window header should not have unread counter for non-channel thread', async function (assert) { + assert.expect(2); + + this.data['res.partner'].records.push({ id: 11 }); + this.data['mail.message'].records.push({ + author_id: 11, + body: 'not empty', + id: 21, + model: 'res.partner', + needaction: true, + needaction_partner_ids: [this.data.currentPartnerId], + res_id: 11, + }); + this.data['mail.notification'].records.push({ + mail_message_id: 21, + notification_status: 'sent', + notification_type: 'inbox', + res_partner_id: this.data.currentPartnerId, + }); + await this.start({ + hasChatWindow: true, + hasMessagingMenu: true, + }); + await afterNextRender(() => this.afterEvent({ + eventName: 'o-thread-cache-loaded-messages', + func: () => document.querySelector('.o_MessagingMenu_toggler').click(), + message: "should wait until inbox loaded initial needaction messages", + predicate: ({ threadCache }) => { + return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; + }, + })); + await afterNextRender(() => + document.querySelector('.o_ThreadNeedactionPreview').click() + ); + assert.containsOnce( + document.body, + '.o_ChatWindow', + "should have opened the chat window on clicking on the preview" + ); + assert.containsNone( + document.body, + '.o_ChatWindowHeader_counter', + "chat window header should not have unread counter for non-channel thread" + ); +}); + +}); +}); +}); + +}); |
