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/message/message_tests.js | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/message/message_tests.js')
| -rw-r--r-- | addons/mail/static/src/components/message/message_tests.js | 1580 |
1 files changed, 1580 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/message/message_tests.js b/addons/mail/static/src/components/message/message_tests.js new file mode 100644 index 00000000..67fa9b96 --- /dev/null +++ b/addons/mail/static/src/components/message/message_tests.js @@ -0,0 +1,1580 @@ +odoo.define('mail/static/src/components/message/message_tests.js', function (require) { +'use strict'; + +const components = { + Message: require('mail/static/src/components/message/message.js'), +}; +const { makeDeferred } = require('mail/static/src/utils/deferred/deferred.js'); +const { + afterEach, + afterNextRender, + beforeEach, + createRootComponent, + nextAnimationFrame, + start, +} = require('mail/static/src/utils/test_utils.js'); + +const Bus = require('web.Bus'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('message', {}, function () { +QUnit.module('message_tests.js', { + beforeEach() { + beforeEach(this); + + this.createMessageComponent = async (message, otherProps) => { + const props = Object.assign({ messageLocalId: message.localId }, otherProps); + await createRootComponent(this, components.Message, { + props, + target: this.widget.el, + }); + }; + + this.start = async params => { + const { env, widget } = await start(Object.assign({}, params, { + data: this.data, + })); + this.env = env; + this.widget = widget; + }; + }, + afterEach() { + afterEach(this); + }, +}); + +QUnit.test('basic rendering', async function (assert) { + assert.expect(12); + + await this.start(); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 7, display_name: "Demo User" }]], + body: "<p>Test</p>", + id: 100, + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelectorAll('.o_Message').length, + 1, + "should display a message component" + ); + const messageEl = document.querySelector('.o_Message'); + assert.strictEqual( + messageEl.dataset.messageLocalId, + this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId, + "message component should be linked to message store model" + ); + assert.strictEqual( + messageEl.querySelectorAll(`:scope .o_Message_sidebar`).length, + 1, + "message should have a sidebar" + ); + assert.strictEqual( + messageEl.querySelectorAll(`:scope .o_Message_sidebar .o_Message_authorAvatar`).length, + 1, + "message should have author avatar in the sidebar" + ); + assert.strictEqual( + messageEl.querySelector(`:scope .o_Message_authorAvatar`).tagName, + 'IMG', + "message author avatar should be an image" + ); + assert.strictEqual( + messageEl.querySelector(`:scope .o_Message_authorAvatar`).dataset.src, + '/web/image/res.partner/7/image_128', + "message author avatar should GET image of the related partner" + ); + assert.strictEqual( + messageEl.querySelectorAll(`:scope .o_Message_authorName`).length, + 1, + "message should display author name" + ); + assert.strictEqual( + messageEl.querySelector(`:scope .o_Message_authorName`).textContent, + "Demo User", + "message should display correct author name" + ); + assert.strictEqual( + messageEl.querySelectorAll(`:scope .o_Message_date`).length, + 1, + "message should display date" + ); + assert.strictEqual( + messageEl.querySelectorAll(`:scope .o_Message_commands`).length, + 1, + "message should display list of commands" + ); + assert.strictEqual( + messageEl.querySelectorAll(`:scope .o_Message_content`).length, + 1, + "message should display the content" + ); + assert.strictEqual( + messageEl.querySelector(`:scope .o_Message_prettyBody`).innerHTML, + "<p>Test</p>", + "message should display the correct content" + ); +}); + +QUnit.test('moderation: as author, moderated channel with pending moderation message', async function (assert) { + assert.expect(1); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 20, + model: 'mail.channel', + }); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 1, display_name: "Admin" }]], + body: "<p>Test</p>", + id: 100, + moderation_status: 'pending_moderation', + originThread: [['link', thread]], + }); + await this.createMessageComponent(message); + + assert.strictEqual( + document.querySelectorAll(`.o_Message_moderationPending.o-author`).length, + 1, + "should have the message pending moderation" + ); +}); + +QUnit.test('moderation: as moderator, moderated channel with pending moderation message', async function (assert) { + assert.expect(9); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 20, + model: 'mail.channel', + moderators: [['link', this.env.messaging.currentPartner]], + }); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 7, display_name: "Demo User" }]], + body: "<p>Test</p>", + id: 100, + moderation_status: 'pending_moderation', + originThread: [['link', thread]], + }); + await this.createMessageComponent(message); + const messageEl = document.querySelector('.o_Message'); + assert.ok(messageEl, "should display a message"); + assert.containsOnce(messageEl, `.o_Message_moderationSubHeader`, + "should have the message pending moderation" + ); + assert.containsNone(messageEl, `.o_Message_checkbox`, + "should not have the moderation checkbox by default" + ); + assert.containsN(messageEl, '.o_Message_moderationAction', 5, + "there should be 5 contextual moderation decisions next to the message" + ); + assert.containsOnce(messageEl, '.o_Message_moderationAction.o-accept', + "there should be a contextual moderation decision to accept the message" + ); + assert.containsOnce(messageEl, '.o_Message_moderationAction.o-reject', + "there should be a contextual moderation decision to reject the message" + ); + assert.containsOnce(messageEl, '.o_Message_moderationAction.o-discard', + "there should be a contextual moderation decision to discard the message" + ); + assert.containsOnce(messageEl, '.o_Message_moderationAction.o-allow', + "there should be a contextual moderation decision to allow the user of the message)" + ); + assert.containsOnce(messageEl, '.o_Message_moderationAction.o-ban', + "there should be a contextual moderation decision to ban the user of the message" + ); + // The actions are tested as part of discuss tests. +}); + +QUnit.test('Notification Sent', async function (assert) { + assert.expect(9); + + await this.start(); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['create', { + id: 11, + model: 'mail.channel', + }]], + }); + const message = this.env.models['mail.message'].create({ + id: 10, + message_type: 'email', + notifications: [['insert', { + id: 11, + notification_status: 'sent', + notification_type: 'email', + partner: [['insert', { id: 12, name: "Someone" }]], + }]], + originThread: [['link', threadViewer.thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsOnce( + document.body, + '.o_Message_notificationIconClickable', + "should display the notification icon container" + ); + assert.containsOnce( + document.body, + '.o_Message_notificationIcon', + "should display the notification icon" + ); + assert.hasClass( + document.querySelector('.o_Message_notificationIcon'), + 'fa-envelope-o', + "icon should represent email success" + ); + + await afterNextRender(() => { + document.querySelector('.o_Message_notificationIconClickable').click(); + }); + assert.containsOnce( + document.body, + '.o_NotificationPopover', + "notification popover should be open" + ); + assert.containsOnce( + document.body, + '.o_NotificationPopover_notificationIcon', + "popover should have one icon" + ); + assert.hasClass( + document.querySelector('.o_NotificationPopover_notificationIcon'), + 'fa-check', + "popover should have the sent icon" + ); + assert.containsOnce( + document.body, + '.o_NotificationPopover_notificationPartnerName', + "popover should have the partner name" + ); + assert.strictEqual( + document.querySelector('.o_NotificationPopover_notificationPartnerName').textContent.trim(), + "Someone", + "partner name should be correct" + ); +}); + +QUnit.test('Notification Error', async function (assert) { + assert.expect(8); + + const openResendActionDef = makeDeferred(); + const bus = new Bus(); + bus.on('do-action', null, payload => { + assert.step('do_action'); + assert.strictEqual( + payload.action, + 'mail.mail_resend_message_action', + "action should be the one to resend email" + ); + assert.strictEqual( + payload.options.additional_context.mail_message_to_resend, + 10, + "action should have correct message id" + ); + openResendActionDef.resolve(); + }); + + await this.start({ env: { bus } }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['create', { + id: 11, + model: 'mail.channel', + }]], + }); + const message = this.env.models['mail.message'].create({ + id: 10, + message_type: 'email', + notifications: [['insert', { + id: 11, + notification_status: 'exception', + notification_type: 'email', + }]], + originThread: [['link', threadViewer.thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsOnce( + document.body, + '.o_Message_notificationIconClickable', + "should display the notification icon container" + ); + assert.containsOnce( + document.body, + '.o_Message_notificationIcon', + "should display the notification icon" + ); + assert.hasClass( + document.querySelector('.o_Message_notificationIcon'), + 'fa-envelope', + "icon should represent email error" + ); + document.querySelector('.o_Message_notificationIconClickable').click(); + await openResendActionDef; + assert.verifySteps( + ['do_action'], + "should do an action to display the resend email dialog" + ); +}); + +QUnit.test("'channel_fetch' notification received is correctly handled", async function (assert) { + assert.expect(3); + + await this.start(); + const currentPartner = this.env.models['mail.partner'].insert({ + id: this.env.messaging.currentPartner.id, + display_name: "Demo User", + }); + const thread = this.env.models['mail.thread'].create({ + channel_type: 'chat', + id: 11, + members: [ + [['link', currentPartner]], + [['insert', { id: 11, display_name: "Recipient" }]] + ], + model: 'mail.channel', + }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + const message = this.env.models['mail.message'].create({ + author: [['link', currentPartner]], + body: "<p>Test</p>", + id: 100, + originThread: [['link', thread]], + }); + + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId, + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsNone( + document.body, + '.o_MessageSeenIndicator_icon', + "message component should not have any check (V) as message is not yet received" + ); + + // Simulate received channel fetched notification + const notifications = [ + [['myDB', 'mail.channel', 11], { + info: 'channel_fetched', + last_message_id: 100, + partner_id: 11, + }], + ]; + await afterNextRender(() => { + this.widget.call('bus_service', 'trigger', 'notification', notifications); + }); + + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator_icon', + "message seen indicator component should only contain one check (V) as message is just received" + ); +}); + +QUnit.test("'channel_seen' notification received is correctly handled", async function (assert) { + assert.expect(3); + + await this.start(); + const currentPartner = this.env.models['mail.partner'].insert({ + id: this.env.messaging.currentPartner.id, + display_name: "Demo User", + }); + const thread = this.env.models['mail.thread'].create({ + channel_type: 'chat', + id: 11, + members: [ + [['link', currentPartner]], + [['insert', { id: 11, display_name: "Recipient" }]] + ], + model: 'mail.channel', + }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + const message = this.env.models['mail.message'].create({ + author: [['link', currentPartner]], + body: "<p>Test</p>", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId, + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsNone( + document.body, + '.o_MessageSeenIndicator_icon', + "message component should not have any check (V) as message is not yet received" + ); + + // Simulate received channel seen notification + const notifications = [ + [['myDB', 'mail.channel', 11], { + info: 'channel_seen', + last_message_id: 100, + partner_id: 11, + }], + ]; + await afterNextRender(() => { + this.widget.call('bus_service', 'trigger', 'notification', notifications); + }); + assert.containsN( + document.body, + '.o_MessageSeenIndicator_icon', + 2, + "message seen indicator component should contain two checks (V) as message is seen" + ); +}); + +QUnit.test("'channel_fetch' notification then 'channel_seen' received are correctly handled", async function (assert) { + assert.expect(4); + + await this.start(); + const currentPartner = this.env.models['mail.partner'].insert({ + id: this.env.messaging.currentPartner.id, + display_name: "Demo User", + }); + const thread = this.env.models['mail.thread'].create({ + channel_type: 'chat', + id: 11, + members: [ + [['link', currentPartner]], + [['insert', { id: 11, display_name: "Recipient" }]] + ], + model: 'mail.channel', + }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + const message = this.env.models['mail.message'].create({ + author: [['link', currentPartner]], + body: "<p>Test</p>", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId, + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsNone( + document.body, + '.o_MessageSeenIndicator_icon', + "message component should not have any check (V) as message is not yet received" + ); + + // Simulate received channel fetched notification + let notifications = [ + [['myDB', 'mail.channel', 11], { + info: 'channel_fetched', + last_message_id: 100, + partner_id: 11, + }], + ]; + await afterNextRender(() => { + this.widget.call('bus_service', 'trigger', 'notification', notifications); + }); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator_icon', + "message seen indicator component should only contain one check (V) as message is just received" + ); + + // Simulate received channel seen notification + notifications = [ + [['myDB', 'mail.channel', 11], { + info: 'channel_seen', + last_message_id: 100, + partner_id: 11, + }], + ]; + await afterNextRender(() => { + this.widget.call('bus_service', 'trigger', 'notification', notifications); + }); + assert.containsN( + document.body, + '.o_MessageSeenIndicator_icon', + 2, + "message seen indicator component should contain two checks (V) as message is now seen" + ); +}); + +QUnit.test('do not show messaging seen indicator if not authored by me', async function (assert) { + assert.expect(2); + + await this.start(); + const author = this.env.models['mail.partner'].create({ + id: 100, + display_name: "Demo User" + }); + const thread = this.env.models['mail.thread'].create({ + channel_type: 'chat', + id: 11, + partnerSeenInfos: [['create', [ + { + channelId: 11, + lastFetchedMessage: [['insert', { id: 100 }]], + partnerId: this.env.messaging.currentPartner.id, + }, + { + channelId: 11, + lastFetchedMessage: [['insert', { id: 100 }]], + partnerId: author.id, + }, + ]]], + model: 'mail.channel', + }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['link', author]], + body: "<p>Test</p>", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageComponent(message, { threadViewLocalId: threadViewer.threadView.localId }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsNone( + document.body, + '.o_Message_seenIndicator', + "message component should not have any message seen indicator" + ); +}); + +QUnit.test('do not show messaging seen indicator if before last seen by all message', async function (assert) { + assert.expect(3); + + await this.start(); + const currentPartner = this.env.models['mail.partner'].insert({ + id: this.env.messaging.currentPartner.id, + display_name: "Demo User", + }); + const thread = this.env.models['mail.thread'].create({ + channel_type: 'chat', + id: 11, + messageSeenIndicators: [['insert', { + channelId: 11, + messageId: 99, + }]], + model: 'mail.channel', + }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + const lastSeenMessage = this.env.models['mail.message'].create({ + author: [['link', currentPartner]], + body: "<p>You already saw me</p>", + id: 100, + originThread: [['link', thread]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['link', currentPartner]], + body: "<p>Test</p>", + id: 99, + originThread: [['link', thread]], + }); + thread.update({ + partnerSeenInfos: [['create', [ + { + channelId: 11, + lastSeenMessage: [['link', lastSeenMessage]], + partnerId: this.env.messaging.currentPartner.id, + }, + { + channelId: 11, + lastSeenMessage: [['link', lastSeenMessage]], + partnerId: 100, + }, + ]]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId, + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsOnce( + document.body, + '.o_Message_seenIndicator', + "message component should have a message seen indicator" + ); + assert.containsNone( + document.body, + '.o_MessageSeenIndicator_icon', + "message component should not have any check (V)" + ); +}); + +QUnit.test('only show messaging seen indicator if authored by me, after last seen by all message', async function (assert) { + assert.expect(3); + + await this.start(); + const currentPartner = this.env.models['mail.partner'].insert({ + id: this.env.messaging.currentPartner.id, + display_name: "Demo User" + }); + const thread = this.env.models['mail.thread'].create({ + channel_type: 'chat', + id: 11, + partnerSeenInfos: [['create', [ + { + channelId: 11, + lastSeenMessage: [['insert', { id: 100 }]], + partnerId: this.env.messaging.currentPartner.id, + }, + { + channelId: 11, + lastFetchedMessage: [['insert', { id: 100 }]], + lastSeenMessage: [['insert', { id: 99 }]], + partnerId: 100, + }, + ]]], + messageSeenIndicators: [['insert', { + channelId: 11, + messageId: 100, + }]], + model: 'mail.channel', + }); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['link', currentPartner]], + body: "<p>Test</p>", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId, + }); + + assert.containsOnce( + document.body, + '.o_Message', + "should display a message component" + ); + assert.containsOnce( + document.body, + '.o_Message_seenIndicator', + "message component should have a message seen indicator" + ); + assert.containsN( + document.body, + '.o_MessageSeenIndicator_icon', + 1, + "message component should have one check (V) because the message was fetched by everyone but no other member than author has seen the message" + ); +}); + +QUnit.test('allow attachment delete on authored message', async function (assert) { + assert.expect(5); + + await this.start(); + const message = this.env.models['mail.message'].create({ + attachments: [['insert-and-replace', { + filename: "BLAH.jpg", + id: 10, + name: "BLAH", + }]], + author: [['link', this.env.messaging.currentPartner]], + body: "<p>Test</p>", + id: 100, + }); + await this.createMessageComponent(message); + + assert.containsOnce( + document.body, + '.o_Attachment', + "should have an attachment", + ); + assert.containsOnce( + document.body, + '.o_Attachment_asideItemUnlink', + "should have delete attachment button" + ); + + await afterNextRender(() => document.querySelector('.o_Attachment_asideItemUnlink').click()); + assert.containsOnce( + document.body, + '.o_AttachmentDeleteConfirmDialog', + "An attachment delete confirmation dialog should have been opened" + ); + assert.strictEqual( + document.querySelector('.o_AttachmentDeleteConfirmDialog_mainText').textContent, + `Do you really want to delete "BLAH"?`, + "Confirmation dialog should contain the attachment delete confirmation text" + ); + + await afterNextRender(() => + document.querySelector('.o_AttachmentDeleteConfirmDialog_confirmButton').click() + ); + assert.containsNone( + document.body, + '.o_Attachment', + "should no longer have an attachment", + ); +}); + +QUnit.test('prevent attachment delete on non-authored message', async function (assert) { + assert.expect(2); + + await this.start(); + const message = this.env.models['mail.message'].create({ + attachments: [['insert-and-replace', { + filename: "BLAH.jpg", + id: 10, + name: "BLAH", + }]], + author: [['insert', { id: 11, display_name: "Guy" }]], + body: "<p>Test</p>", + id: 100, + }); + await this.createMessageComponent(message); + + assert.containsOnce( + document.body, + '.o_Attachment', + "should have an attachment", + ); + assert.containsNone( + document.body, + '.o_Attachment_asideItemUnlink', + "delete attachment button should not be printed" + ); +}); + +QUnit.test('subtype description should be displayed if it is different than body', async function (assert) { + assert.expect(2); + + await this.start(); + const message = this.env.models['mail.message'].create({ + body: "<p>Hello</p>", + id: 100, + subtype_description: 'Bonjour', + }); + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_content', + "message should have content" + ); + assert.strictEqual( + document.querySelector(`.o_Message_content`).textContent, + "HelloBonjour", + "message content should display both body and subtype description when they are different" + ); +}); + +QUnit.test('subtype description should not be displayed if it is similar to body', async function (assert) { + assert.expect(2); + + await this.start(); + const message = this.env.models['mail.message'].create({ + body: "<p>Hello</p>", + id: 100, + subtype_description: 'hello', + }); + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_content', + "message should have content" + ); + assert.strictEqual( + document.querySelector(`.o_Message_content`).textContent, + "Hello", + "message content should display only body when subtype description is similar" + ); +}); + +QUnit.test('data-oe-id & data-oe-model link redirection on click', async function (assert) { + assert.expect(7); + + const bus = new Bus(); + bus.on('do-action', null, payload => { + assert.strictEqual( + payload.action.type, + 'ir.actions.act_window', + "action should open view" + ); + assert.strictEqual( + payload.action.res_model, + 'some.model', + "action should open view on 'some.model' model" + ); + assert.strictEqual( + payload.action.res_id, + 250, + "action should open view on 250" + ); + assert.step('do-action:openFormView_some.model_250'); + }); + await this.start({ env: { bus } }); + const message = this.env.models['mail.message'].create({ + body: `<p><a href="#" data-oe-id="250" data-oe-model="some.model">some.model_250</a></p>`, + id: 100, + }); + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_content', + "message should have content" + ); + assert.containsOnce( + document.querySelector('.o_Message_content'), + 'a', + "message content should have a link" + ); + + document.querySelector(`.o_Message_content a`).click(); + assert.verifySteps( + ['do-action:openFormView_some.model_250'], + "should have open form view on related record after click on link" + ); +}); + +QUnit.test('chat with author should be opened after clicking on his avatar', async function (assert) { + assert.expect(4); + + this.data['res.partner'].records.push({ id: 10 }); + this.data['res.users'].records.push({ partner_id: 10 }); + await this.start({ + hasChatWindow: true, + }); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 10 }]], + id: 10, + }); + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_authorAvatar', + "message should have the author avatar" + ); + assert.hasClass( + document.querySelector('.o_Message_authorAvatar'), + 'o_redirect', + "author avatar should have the redirect style" + ); + + await afterNextRender(() => + document.querySelector('.o_Message_authorAvatar').click() + ); + assert.containsOnce( + document.body, + '.o_ChatWindow_thread', + "chat window with thread should be opened after clicking on author avatar" + ); + assert.strictEqual( + document.querySelector('.o_ChatWindow_thread').dataset.correspondentId, + message.author.id.toString(), + "chat with author should be opened after clicking on his avatar" + ); +}); + +QUnit.test('chat with author should be opened after clicking on his im status icon', async function (assert) { + assert.expect(4); + + this.data['res.partner'].records.push({ id: 10 }); + this.data['res.users'].records.push({ partner_id: 10 }); + await this.start({ + hasChatWindow: true, + }); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 10, im_status: 'online' }]], + id: 10, + }); + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_partnerImStatusIcon', + "message should have the author im status icon" + ); + assert.hasClass( + document.querySelector('.o_Message_partnerImStatusIcon'), + 'o-has-open-chat', + "author im status icon should have the open chat style" + ); + + await afterNextRender(() => + document.querySelector('.o_Message_partnerImStatusIcon').click() + ); + assert.containsOnce( + document.body, + '.o_ChatWindow_thread', + "chat window with thread should be opened after clicking on author im status icon" + ); + assert.strictEqual( + document.querySelector('.o_ChatWindow_thread').dataset.correspondentId, + message.author.id.toString(), + "chat with author should be opened after clicking on his im status icon" + ); +}); + +QUnit.test('open chat with author on avatar click should be disabled when currently chatting with the author', async function (assert) { + assert.expect(3); + + this.data['mail.channel'].records.push({ + channel_type: 'chat', + members: [this.data.currentPartnerId, 10], + public: 'private', + }); + this.data['res.partner'].records.push({ id: 10 }); + this.data['res.users'].records.push({ partner_id: 10 }); + await this.start({ + hasChatWindow: true, + }); + const correspondent = this.env.models['mail.partner'].insert({ id: 10 }); + const message = this.env.models['mail.message'].create({ + author: [['link', correspondent]], + id: 10, + }); + const thread = await correspondent.getChat(); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['link', thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId, + }); + assert.containsOnce( + document.body, + '.o_Message_authorAvatar', + "message should have the author avatar" + ); + assert.doesNotHaveClass( + document.querySelector('.o_Message_authorAvatar'), + 'o_redirect', + "author avatar should not have the redirect style" + ); + + document.querySelector('.o_Message_authorAvatar').click(); + await nextAnimationFrame(); + assert.containsNone( + document.body, + '.o_ChatWindow', + "should have no thread opened after clicking on author avatar when currently chatting with the author" + ); +}); + +QUnit.test('basic rendering of tracking value (float type)', async function (assert) { + assert.expect(8); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "float", + id: 6, + new_value: 45.67, + old_value: 12.3, + }], + }); + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_trackingValue', + "should display a tracking value" + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueFieldName', + "should display the name of the tracked field" + ); + assert.strictEqual( + document.querySelector('.o_Message_trackingValueFieldName').textContent, + "Total:", + "should display the correct tracked field name (Total)", + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueOldValue', + "should display the old value" + ); + assert.strictEqual( + document.querySelector('.o_Message_trackingValueOldValue').textContent, + "12.30", + "should display the correct old value (12.30)", + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueSeparator', + "should display the separator" + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueNewValue', + "should display the new value" + ); + assert.strictEqual( + document.querySelector('.o_Message_trackingValueNewValue').textContent, + "45.67", + "should display the correct new value (45.67)", + ); +}); + +QUnit.test('rendering of tracked field of type integer: from non-0 to 0', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "integer", + id: 6, + new_value: 0, + old_value: 1, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Total:10", + "should display the correct content of tracked field of type integer: from non-0 to 0 (Total: 1 -> 0)" + ); +}); + +QUnit.test('rendering of tracked field of type integer: from 0 to non-0', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "integer", + id: 6, + new_value: 1, + old_value: 0, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Total:01", + "should display the correct content of tracked field of type integer: from 0 to non-0 (Total: 0 -> 1)" + ); +}); + +QUnit.test('rendering of tracked field of type float: from non-0 to 0', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "float", + id: 6, + new_value: 0, + old_value: 1, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Total:1.000.00", + "should display the correct content of tracked field of type float: from non-0 to 0 (Total: 1.00 -> 0.00)" + ); +}); + +QUnit.test('rendering of tracked field of type float: from 0 to non-0', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "float", + id: 6, + new_value: 1, + old_value: 0, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Total:0.001.00", + "should display the correct content of tracked field of type float: from 0 to non-0 (Total: 0.00 -> 1.00)" + ); +}); + +QUnit.test('rendering of tracked field of type monetary: from non-0 to 0', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "monetary", + id: 6, + new_value: 0, + old_value: 1, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Total:1.000.00", + "should display the correct content of tracked field of type monetary: from non-0 to 0 (Total: 1.00 -> 0.00)" + ); +}); + +QUnit.test('rendering of tracked field of type monetary: from 0 to non-0', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Total", + field_type: "monetary", + id: 6, + new_value: 1, + old_value: 0, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Total:0.001.00", + "should display the correct content of tracked field of type monetary: from 0 to non-0 (Total: 0.00 -> 1.00)" + ); +}); + +QUnit.test('rendering of tracked field of type boolean: from true to false', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Is Ready", + field_type: "boolean", + id: 6, + new_value: false, + old_value: true, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Is Ready:TrueFalse", + "should display the correct content of tracked field of type boolean: from true to false (Is Ready: True -> False)" + ); +}); + +QUnit.test('rendering of tracked field of type boolean: from false to true', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Is Ready", + field_type: "boolean", + id: 6, + new_value: true, + old_value: false, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Is Ready:FalseTrue", + "should display the correct content of tracked field of type boolean: from false to true (Is Ready: False -> True)" + ); +}); + +QUnit.test('rendering of tracked field of type char: from a string to empty string', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Name", + field_type: "char", + id: 6, + new_value: "", + old_value: "Marc", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Name:Marc", + "should display the correct content of tracked field of type char: from a string to empty string (Name: Marc ->)" + ); +}); + +QUnit.test('rendering of tracked field of type char: from empty string to a string', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Name", + field_type: "char", + id: 6, + new_value: "Marc", + old_value: "", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Name:Marc", + "should display the correct content of tracked field of type char: from empty string to a string (Name: -> Marc)" + ); +}); + +QUnit.test('rendering of tracked field of type date: from no date to a set date', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Deadline", + field_type: "date", + id: 6, + new_value: "2018-12-14", + old_value: false, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Deadline:12/14/2018", + "should display the correct content of tracked field of type date: from no date to a set date (Deadline: -> 12/14/2018)" + ); +}); + +QUnit.test('rendering of tracked field of type date: from a set date to no date', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Deadline", + field_type: "date", + id: 6, + new_value: false, + old_value: "2018-12-14", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Deadline:12/14/2018", + "should display the correct content of tracked field of type date: from a set date to no date (Deadline: 12/14/2018 ->)" + ); +}); + +QUnit.test('rendering of tracked field of type datetime: from no date and time to a set date and time', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Deadline", + field_type: "datetime", + id: 6, + new_value: "2018-12-14 13:42:28", + old_value: false, + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Deadline:12/14/2018 13:42:28", + "should display the correct content of tracked field of type datetime: from no date and time to a set date and time (Deadline: -> 12/14/2018 13:42:28)" + ); +}); + +QUnit.test('rendering of tracked field of type datetime: from a set date and time to no date and time', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Deadline", + field_type: "datetime", + id: 6, + new_value: false, + old_value: "2018-12-14 13:42:28", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Deadline:12/14/2018 13:42:28", + "should display the correct content of tracked field of type datetime: from a set date and time to no date and time (Deadline: 12/14/2018 13:42:28 ->)" + ); +}); + +QUnit.test('rendering of tracked field of type text: from some text to empty', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Name", + field_type: "text", + id: 6, + new_value: "", + old_value: "Marc", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Name:Marc", + "should display the correct content of tracked field of type text: from some text to empty (Name: Marc ->)" + ); +}); + +QUnit.test('rendering of tracked field of type text: from empty to some text', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Name", + field_type: "text", + id: 6, + new_value: "Marc", + old_value: "", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Name:Marc", + "should display the correct content of tracked field of type text: from empty to some text (Name: -> Marc)" + ); +}); + +QUnit.test('rendering of tracked field of type selection: from a selection to no selection', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "State", + field_type: "selection", + id: 6, + new_value: "", + old_value: "ok", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "State:ok", + "should display the correct content of tracked field of type selection: from a selection to no selection (State: ok ->)" + ); +}); + +QUnit.test('rendering of tracked field of type selection: from no selection to a selection', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "State", + field_type: "selection", + id: 6, + new_value: "ok", + old_value: "", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "State:ok", + "should display the correct content of tracked field of type selection: from no selection to a selection (State: -> ok)" + ); +}); + +QUnit.test('rendering of tracked field of type many2one: from having a related record to no related record', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Author", + field_type: "many2one", + id: 6, + new_value: "", + old_value: "Marc", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Author:Marc", + "should display the correct content of tracked field of type many2one: from having a related record to no related record (Author: Marc ->)" + ); +}); + +QUnit.test('rendering of tracked field of type many2one: from no related record to having a related record', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Author", + field_type: "many2one", + id: 6, + new_value: "Marc", + old_value: "", + }], + }); + await this.createMessageComponent(message); + assert.strictEqual( + document.querySelector('.o_Message_trackingValue').textContent, + "Author:Marc", + "should display the correct content of tracked field of type many2one: from no related record to having a related record (Author: -> Marc)" + ); +}); + +QUnit.test('message should not be considered as "clicked" after clicking on its author name', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 7, display_name: "Demo User" }]], + body: "<p>Test</p>", + id: 100, + }); + await this.createMessageComponent(message); + document.querySelector(`.o_Message_authorName`).click(); + await nextAnimationFrame(); + assert.doesNotHaveClass( + document.querySelector(`.o_Message`), + 'o-clicked', + "message should not be considered as 'clicked' after clicking on its author name" + ); +}); + +QUnit.test('message should not be considered as "clicked" after clicking on its author avatar', async function (assert) { + assert.expect(1); + + await this.start(); + const message = this.env.models['mail.message'].create({ + author: [['insert', { id: 7, display_name: "Demo User" }]], + body: "<p>Test</p>", + id: 100, + }); + await this.createMessageComponent(message); + document.querySelector(`.o_Message_authorAvatar`).click(); + await nextAnimationFrame(); + assert.doesNotHaveClass( + document.querySelector(`.o_Message`), + 'o-clicked', + "message should not be considered as 'clicked' after clicking on its author avatar" + ); +}); + +QUnit.test('message should not be considered as "clicked" after clicking on notification failure icon', async function (assert) { + assert.expect(1); + + await this.start(); + const threadViewer = this.env.models['mail.thread_viewer'].create({ + hasThreadView: true, + thread: [['create', { + id: 11, + model: 'mail.channel', + }]], + }); + const message = this.env.models['mail.message'].create({ + id: 10, + message_type: 'email', + notifications: [['insert', { + id: 11, + notification_status: 'exception', + notification_type: 'email', + }]], + originThread: [['link', threadViewer.thread]], + }); + await this.createMessageComponent(message, { + threadViewLocalId: threadViewer.threadView.localId + }); + document.querySelector('.o_Message_notificationIconClickable.o-error').click(); + await nextAnimationFrame(); + assert.doesNotHaveClass( + document.querySelector(`.o_Message`), + 'o-clicked', + "message should not be considered as 'clicked' after clicking on notification failure icon" + ); +}); + +}); +}); +}); + +}); |
