diff --git a/addons/im_livechat/tests/__init__.py b/addons/im_livechat/tests/__init__.py
index f9259d670bb..bd4800bdb32 100644
--- a/addons/im_livechat/tests/__init__.py
+++ b/addons/im_livechat/tests/__init__.py
@@ -6,3 +6,4 @@ from . import test_chatbot_form_ui
from . import test_chatbot_internals
from . import test_get_mail_channel
from . import test_im_livechat_support_page
+from . import test_message
diff --git a/addons/im_livechat/tests/test_message.py b/addons/im_livechat/tests/test_message.py
new file mode 100644
index 00000000000..42bd1c153fb
--- /dev/null
+++ b/addons/im_livechat/tests/test_message.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import Command
+from odoo.tests.common import users, tagged, TransactionCase
+
+@tagged('post_install', '-at_install')
+class TestImLivechatMessage(TransactionCase):
+ def setUp(self):
+ super().setUp()
+ self.users = self.env['res.users'].create([
+ {
+ 'email': 'e.e@example.com',
+ 'groups_id': [Command.link(self.env.ref('base.group_user').id)],
+ 'login': 'emp',
+ 'name': 'Ernest Employee',
+ 'notification_type': 'inbox',
+ 'odoobot_state': 'disabled',
+ 'signature': '--\nErnest',
+ },
+ {'name': 'test1', 'login': 'test1', 'email': 'test1@example.com'},
+ ])
+
+ @users('emp')
+ def test_message_format(self):
+ im_livechat_channel = self.env['im_livechat.channel'].sudo().create({'name': 'support', 'user_ids': [Command.link(self.users[0].id)]})
+ self.users[0].im_status = 'online' # make available for livechat (ignore leave)
+ channel_livechat_1 = self.env['mail.channel'].browse(im_livechat_channel._open_livechat_mail_channel(anonymous_name='anon 1', previous_operator_id=self.users[0].partner_id.id, user_id=self.users[1].id, country_id=self.env.ref('base.in').id)['id'])
+ record_rating = self.env['rating.rating'].create({
+ 'res_model_id': self.env['ir.model']._get('mail.channel').id,
+ 'res_id': channel_livechat_1.id,
+ 'parent_res_model_id': self.env['ir.model']._get('im_livechat.channel').id,
+ 'parent_res_id': im_livechat_channel.id,
+ 'rated_partner_id': self.users[0].partner_id.id,
+ 'partner_id': self.users[1].partner_id.id,
+ 'rating': 5,
+ 'consumed': True,
+ })
+ message = channel_livechat_1.message_post(
+ author_id=record_rating.partner_id.id,
+ body="
%s"
+ % (record_rating.rating_image_url, record_rating.rating, record_rating.feedback),
+ rating_id=record_rating.id,
+ )
+ self.assertEqual(message.message_format(), [{
+ 'attachment_ids': [],
+ 'author_id': (self.users[1].partner_id.id, "test1"),
+ 'body': message.body,
+ 'date': message.date,
+ 'history_partner_ids': [],
+ 'id': message.id,
+ 'is_discussion': False,
+ 'is_note': True,
+ 'is_notification': False,
+ 'message_type': 'notification',
+ 'messageReactionGroups': [('insert-and-replace', [])],
+ 'model': 'mail.channel',
+ 'module_icon': '/mail/static/description/icon.png',
+ 'needaction_partner_ids': [],
+ 'notifications': [],
+ 'rating': [('insert-and-replace', {
+ 'id': record_rating.id,
+ 'ratingImageUrl': record_rating.rating_image_url,
+ 'ratingText': record_rating.rating_text,
+ })],
+ 'recipients': [],
+ 'record_name': "test1 Ernest Employee",
+ 'res_id': channel_livechat_1.id,
+ 'sms_ids': [],
+ 'starred_partner_ids': [],
+ 'subject': False,
+ 'subtype_description': False,
+ 'subtype_id': (self.env.ref('mail.mt_note').id, 'Note'),
+ 'trackingValues': [('insert-and-replace', [])],
+ }])
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
index 054f750adf2..d34e7435eca 100644
--- 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
@@ -1,7 +1,5 @@
/** @odoo-module **/
-import * as mailUtils from '@mail/js/utils';
-
import { useRefToModel } from '@mail/component_hooks/use_ref_to_model';
import { registerMessagingComponent } from '@mail/utils/messaging_component';
@@ -39,18 +37,6 @@ export class ThreadNeedactionPreview extends Component {
return '/mail/static/src/img/smiley/avatar.jpg';
}
- /**
- * Get inline content of the last message of this conversation.
- *
- * @returns {string}
- */
- get inlineLastNeedactionMessageAsOriginThreadBody() {
- if (!this.threadNeedactionPreviewView.thread.lastNeedactionMessageAsOriginThread) {
- return '';
- }
- return mailUtils.htmlToTextContentInline(this.threadNeedactionPreviewView.thread.lastNeedactionMessageAsOriginThread.prettyBody);
- }
-
/**
* @returns {ThreadNeedactionPreviewView}
*/
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
index 20a17e75d03..e174c06f4a4 100644
--- 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
@@ -41,11 +41,11 @@
-
+
-
+
diff --git a/addons/mail/static/src/components/thread_preview/thread_preview.js b/addons/mail/static/src/components/thread_preview/thread_preview.js
index 53a72bed0e8..c4f75eaca33 100644
--- a/addons/mail/static/src/components/thread_preview/thread_preview.js
+++ b/addons/mail/static/src/components/thread_preview/thread_preview.js
@@ -1,7 +1,5 @@
/** @odoo-module **/
-import * as mailUtils from '@mail/js/utils';
-
import { useRefToModel } from '@mail/component_hooks/use_ref_to_model';
import { registerMessagingComponent } from '@mail/utils/messaging_component';
@@ -33,18 +31,6 @@ export class ThreadPreview extends Component {
return `/web/image/mail.channel/${this.threadPreviewView.thread.id}/avatar_128?unique=${this.threadPreviewView.thread.avatarCacheKey}`;
}
- /**
- * Get inline content of the last message of this conversation.
- *
- * @returns {string}
- */
- get inlineLastMessageBody() {
- if (!this.threadPreviewView.thread.lastMessage) {
- return '';
- }
- return mailUtils.htmlToTextContentInline(this.threadPreviewView.thread.lastMessage.prettyBody);
- }
-
/**
* @returns {ThreadPreviewView}
*/
diff --git a/addons/mail/static/src/components/thread_preview/thread_preview.xml b/addons/mail/static/src/components/thread_preview/thread_preview.xml
index 72f71d243bb..866c21afe27 100644
--- a/addons/mail/static/src/components/thread_preview/thread_preview.xml
+++ b/addons/mail/static/src/components/thread_preview/thread_preview.xml
@@ -45,11 +45,11 @@
-
+
-
+
diff --git a/addons/mail/static/src/models/thread_needaction_preview_view.js b/addons/mail/static/src/models/thread_needaction_preview_view.js
index 24aa1c1eb49..a3145dfaedc 100644
--- a/addons/mail/static/src/models/thread_needaction_preview_view.js
+++ b/addons/mail/static/src/models/thread_needaction_preview_view.js
@@ -3,6 +3,7 @@
import { registerModel } from '@mail/model/model_core';
import { attr, one } from '@mail/model/model_field';
import { clear, insertAndReplace } from '@mail/model/model_field_command';
+import { htmlToTextContentInline } from '@mail/js/utils';
registerModel({
name: 'ThreadNeedactionPreviewView',
@@ -34,6 +35,23 @@ registerModel({
['res_id', '=', this.thread.id],
]);
},
+ /**
+ * @private
+ * @returns {string|FieldCommand}
+ */
+ _computeInlineLastNeedactionMessageAsOriginThreadBody() {
+ if (!this.thread.lastNeedactionMessageAsOriginThread) {
+ return clear();
+ }
+ return htmlToTextContentInline(this.thread.lastNeedactionMessageAsOriginThread.prettyBody);
+ },
+ /**
+ * @private
+ * @returns {boolean}
+ */
+ _computeIsEmpty() {
+ return !this.inlineLastNeedactionMessageAsOriginThreadBody;
+ },
/**
* @private
* @returns {FieldCommand}
@@ -49,6 +67,15 @@ registerModel({
},
},
fields: {
+ inlineLastNeedactionMessageAsOriginThreadBody: attr({
+ compute: '_computeInlineLastNeedactionMessageAsOriginThreadBody',
+ default: "",
+ readonly: true,
+ }),
+ isEmpty: attr({
+ compute: '_computeIsEmpty',
+ readonly: true,
+ }),
/**
* Reference of the "mark as read" button. Useful to disable the
* top-level click handler when clicking on this specific button.
diff --git a/addons/mail/static/src/models/thread_preview_view.js b/addons/mail/static/src/models/thread_preview_view.js
index 42fe056a406..9906b894c20 100644
--- a/addons/mail/static/src/models/thread_preview_view.js
+++ b/addons/mail/static/src/models/thread_preview_view.js
@@ -3,6 +3,7 @@
import { registerModel } from '@mail/model/model_core';
import { attr, one } from '@mail/model/model_field';
import { clear, insertAndReplace } from '@mail/model/model_field_command';
+import { htmlToTextContentInline } from '@mail/js/utils';
registerModel({
name: 'ThreadPreviewView',
@@ -33,6 +34,23 @@ registerModel({
this.thread.markAsSeen(this.thread.lastNonTransientMessage);
}
},
+ /**
+ * @private
+ * @returns {string|FieldCommand}
+ */
+ _computeInlineLastMessageBody() {
+ if (!this.thread.lastMessage) {
+ return clear();
+ }
+ return htmlToTextContentInline(this.thread.lastMessage.prettyBody);
+ },
+ /**
+ * @private
+ * @returns {boolean}
+ */
+ _computeIsEmpty() {
+ return !this.inlineLastMessageBody;
+ },
/**
* @private
* @returns {FieldCommand}
@@ -48,6 +66,15 @@ registerModel({
},
},
fields: {
+ inlineLastMessageBody: attr({
+ compute: '_computeInlineLastMessageBody',
+ default: "",
+ readonly: true,
+ }),
+ isEmpty: attr({
+ compute: '_computeIsEmpty',
+ readonly: true,
+ }),
/**
* Reference of the "mark as read" button. Useful to disable the
* top-level click handler when clicking on this specific button.
diff --git a/addons/rating/__manifest__.py b/addons/rating/__manifest__.py
index ee1787edcaf..7ecd96283ab 100644
--- a/addons/rating/__manifest__.py
+++ b/addons/rating/__manifest__.py
@@ -21,10 +21,18 @@ This module allows a customer to give rating.
'assets': {
'web.assets_backend': [
'rating/static/src/scss/rating_rating_views.scss',
+ 'rating/static/src/components/*/*.scss',
+ 'rating/static/src/models/*.js',
],
'web.assets_frontend': [
'rating/static/src/scss/rating_templates.scss',
],
+ 'web.assets_qweb': [
+ 'rating/static/src/components/*/*.xml',
+ ],
+ 'web.tests_assets': [
+ 'rating/static/tests/helpers/*.js',
+ ],
},
'license': 'LGPL-3',
}
diff --git a/addons/rating/models/mail_message.py b/addons/rating/models/mail_message.py
index 015f21d333a..a0a668fc0ef 100644
--- a/addons/rating/models/mail_message.py
+++ b/addons/rating/models/mail_message.py
@@ -25,3 +25,19 @@ class MailMessage(models.Model):
('message_id', '!=', False)
])
return [('id', 'in', ratings.mapped('message_id').ids)]
+
+ def message_format(self, format_reply=True):
+ message_values = super().message_format(format_reply=format_reply)
+ rating_mixin_messages = self.filtered(lambda message: issubclass(self.pool[message.model], self.pool['rating.mixin']))
+ if rating_mixin_messages:
+ ratings = self.env['rating.rating'].sudo().search([('message_id', 'in', rating_mixin_messages.ids), ('consumed', '=', True)])
+ rating_by_message_id = dict((r.message_id.id, r) for r in ratings)
+ for vals in message_values:
+ if vals['id'] in rating_by_message_id:
+ rating = rating_by_message_id[vals['id']]
+ vals['rating'] = [('insert-and-replace', {
+ 'id': rating.id,
+ 'ratingImageUrl': rating.rating_image_url,
+ 'ratingText': rating.rating_text,
+ })]
+ return message_values
diff --git a/addons/rating/static/src/components/thread_needaction_preview/thread_needaction_preview.scss b/addons/rating/static/src/components/thread_needaction_preview/thread_needaction_preview.scss
new file mode 100644
index 00000000000..d53434b3442
--- /dev/null
+++ b/addons/rating/static/src/components/thread_needaction_preview/thread_needaction_preview.scss
@@ -0,0 +1,4 @@
+.o_ThreadNeedactionPreview_ratingImage {
+ height: $font-size-base;
+ width: $font-size-base;
+}
diff --git a/addons/rating/static/src/components/thread_needaction_preview/thread_needaction_preview.xml b/addons/rating/static/src/components/thread_needaction_preview/thread_needaction_preview.xml
new file mode 100644
index 00000000000..a7864277cbe
--- /dev/null
+++ b/addons/rating/static/src/components/thread_needaction_preview/thread_needaction_preview.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Rating:
+
+
+ $0
+
+
+
diff --git a/addons/rating/static/src/components/thread_preview/thread_preview.scss b/addons/rating/static/src/components/thread_preview/thread_preview.scss
new file mode 100644
index 00000000000..f6cf4b8f920
--- /dev/null
+++ b/addons/rating/static/src/components/thread_preview/thread_preview.scss
@@ -0,0 +1,4 @@
+.o_ThreadPreview_ratingImage {
+ height: $font-size-base;
+ width: $font-size-base;
+}
diff --git a/addons/rating/static/src/components/thread_preview/thread_preview.xml b/addons/rating/static/src/components/thread_preview/thread_preview.xml
new file mode 100644
index 00000000000..56ad13aa0f5
--- /dev/null
+++ b/addons/rating/static/src/components/thread_preview/thread_preview.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Rating:
+
+
+ $0
+
+
+
diff --git a/addons/rating/static/src/models/message.js b/addons/rating/static/src/models/message.js
new file mode 100644
index 00000000000..8d2ef35c3e8
--- /dev/null
+++ b/addons/rating/static/src/models/message.js
@@ -0,0 +1,26 @@
+/** @odoo-module **/
+
+import { addFields, patchModelMethods } from '@mail/model/model_core';
+import { one } from '@mail/model/model_field';
+// ensure the model definition is loaded before the patch
+import '@mail/models/message';
+
+patchModelMethods('Message', {
+ /**
+ * @override
+ */
+ convertData(data) {
+ const data2 = this._super(data);
+ if ('rating' in data) {
+ data2.rating = data.rating;
+ }
+ return data2;
+ },
+
+});
+
+addFields('Message', {
+ rating: one('Rating', {
+ isCausal: true,
+ }),
+});
diff --git a/addons/rating/static/src/models/rating.js b/addons/rating/static/src/models/rating.js
new file mode 100644
index 00000000000..c91419b1ddb
--- /dev/null
+++ b/addons/rating/static/src/models/rating.js
@@ -0,0 +1,21 @@
+/** @odoo-module **/
+
+import { registerModel } from '@mail/model/model_core';
+import { attr } from '@mail/model/model_field';
+
+registerModel({
+ name: 'Rating',
+ identifyingFields: ['id'],
+ fields: {
+ id: attr({
+ readonly: true,
+ required: true,
+ }),
+ ratingImageUrl: attr({
+ readonly: true,
+ }),
+ ratingText: attr({
+ readonly: true,
+ }),
+ },
+});
diff --git a/addons/rating/static/src/models/thread_needaction_preview_view.js b/addons/rating/static/src/models/thread_needaction_preview_view.js
new file mode 100644
index 00000000000..843b59f5efa
--- /dev/null
+++ b/addons/rating/static/src/models/thread_needaction_preview_view.js
@@ -0,0 +1,31 @@
+/** @odoo-module **/
+
+import { addFields, addRecordMethods, patchRecordMethods } from '@mail/model/model_core';
+import { attr } from '@mail/model/model_field';
+// ensure the model definition is loaded before the patch
+import '@mail/models/thread_needaction_preview_view';
+
+patchRecordMethods('ThreadNeedactionPreviewView', {
+ /**
+ * @override
+ */
+ _computeIsEmpty() {
+ return this.isRating || this._super();
+ },
+});
+
+addRecordMethods('ThreadNeedactionPreviewView', {
+ /**
+ * @private
+ */
+ _computeIsRating() {
+ return Boolean(this.thread.lastNeedactionMessageAsOriginThread && this.thread.lastNeedactionMessageAsOriginThread.rating);
+ },
+});
+
+addFields('ThreadNeedactionPreviewView', {
+ isRating: attr({
+ compute: '_computeIsRating',
+ readonly: true,
+ }),
+});
diff --git a/addons/rating/static/src/models/thread_preview_view.js b/addons/rating/static/src/models/thread_preview_view.js
new file mode 100644
index 00000000000..5517641f957
--- /dev/null
+++ b/addons/rating/static/src/models/thread_preview_view.js
@@ -0,0 +1,31 @@
+/** @odoo-module **/
+
+import { addFields, addRecordMethods, patchRecordMethods } from '@mail/model/model_core';
+import { attr } from '@mail/model/model_field';
+// ensure the model definition is loaded before the patch
+import '@mail/models/thread_preview_view';
+
+patchRecordMethods('ThreadPreviewView', {
+ /**
+ * @override
+ */
+ _computeIsEmpty() {
+ return this.isRating || this._super();
+ },
+});
+
+addRecordMethods('ThreadPreviewView', {
+ /**
+ * @private
+ */
+ _computeIsRating() {
+ return Boolean(this.thread.lastMessage && this.thread.lastMessage.rating);
+ },
+});
+
+addFields('ThreadPreviewView', {
+ isRating: attr({
+ compute: '_computeIsRating',
+ readonly: true,
+ }),
+});
diff --git a/addons/rating/static/tests/helpers/mock_server.js b/addons/rating/static/tests/helpers/mock_server.js
new file mode 100644
index 00000000000..7f0570cef2d
--- /dev/null
+++ b/addons/rating/static/tests/helpers/mock_server.js
@@ -0,0 +1,28 @@
+/** @odoo-module **/
+
+import '@mail/../tests/helpers/mock_server'; // ensure mail overrides are applied first
+
+import { patch } from "@web/core/utils/patch";
+import { MockServer } from "@web/../tests/helpers/mock_server";
+
+patch(MockServer.prototype, 'rating', {
+ /**
+ * @override
+ */
+ _mockMailMessageMessageFormat(ids) {
+ const formattedMessages = this._super(...arguments);
+ for (const formattedMessage of formattedMessages) {
+ const [rating] = this.getRecords('rating.rating', [
+ ['message_id', '=', formattedMessage.id],
+ ]);
+ if (rating) {
+ formattedMessage['rating'] = [['insert-and-replace', {
+ 'id': rating.id,
+ 'ratingImageUrl': rating.rating_image_url,
+ 'ratingText': rating.rating_text,
+ }]];
+ }
+ }
+ return formattedMessages;
+ },
+});
diff --git a/addons/rating/static/tests/helpers/model_definitions_setup.js b/addons/rating/static/tests/helpers/model_definitions_setup.js
new file mode 100644
index 00000000000..ec9c0413d46
--- /dev/null
+++ b/addons/rating/static/tests/helpers/model_definitions_setup.js
@@ -0,0 +1,5 @@
+/** @odoo-module **/
+
+import { addModelNamesToFetch } from '@mail/../tests/helpers/model_definitions_helpers';
+
+addModelNamesToFetch(['rating.rating']);
diff --git a/addons/test_mail_full/__manifest__.py b/addons/test_mail_full/__manifest__.py
index 4fa2b9e6643..856aeea12a3 100644
--- a/addons/test_mail_full/__manifest__.py
+++ b/addons/test_mail_full/__manifest__.py
@@ -28,6 +28,14 @@ real applications. """,
'security/ir.model.access.csv',
'security/ir_rule_data.xml',
],
+ 'assets': {
+ 'web.qunit_suite_tests': [
+ 'test_mail_full/static/tests/qunit_suite_tests/*.js',
+ ],
+ 'web.tests_assets': [
+ 'test_mail_full/static/tests/helpers/*.js',
+ ],
+ },
'installable': True,
'license': 'LGPL-3',
}
diff --git a/addons/test_mail_full/static/tests/helpers/model_definitions_setup.js b/addons/test_mail_full/static/tests/helpers/model_definitions_setup.js
new file mode 100644
index 00000000000..7fefd302a25
--- /dev/null
+++ b/addons/test_mail_full/static/tests/helpers/model_definitions_setup.js
@@ -0,0 +1,5 @@
+/** @odoo-module **/
+
+import { addModelNamesToFetch } from '@mail/../tests/helpers/model_definitions_helpers';
+
+addModelNamesToFetch(['mail.test.rating']);
diff --git a/addons/test_mail_full/static/tests/qunit_suite_tests/thread_needaction_preview_tests.js b/addons/test_mail_full/static/tests/qunit_suite_tests/thread_needaction_preview_tests.js
new file mode 100644
index 00000000000..2ede3b6cd49
--- /dev/null
+++ b/addons/test_mail_full/static/tests/qunit_suite_tests/thread_needaction_preview_tests.js
@@ -0,0 +1,65 @@
+/** @odoo-module **/
+
+import { afterNextRender, start, startServer } from '@mail/../tests/helpers/test_utils';
+
+QUnit.module('test_mail_full', {}, function () {
+QUnit.module('thread_needaction_preview_tests.js');
+
+QUnit.test('rating value displayed on the thread needaction preview', async function (assert) {
+ assert.expect(4);
+
+ const pyEnv = await startServer();
+ const resPartnerId1 = pyEnv['res.partner'].create();
+ const mailTestRating1 = pyEnv['mail.test.rating'].create();
+ const mailMessageId1 = pyEnv['mail.message'].create({
+ model: 'mail.test.rating',
+ needaction: true,
+ needaction_partner_ids: [pyEnv.currentPartnerId],
+ res_id: mailTestRating1,
+ });
+ pyEnv['mail.notification'].create({
+ mail_message_id: mailMessageId1,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: pyEnv.currentPartnerId,
+ });
+ pyEnv['rating.rating'].create([{
+ consumed: true,
+ message_id: mailMessageId1,
+ partner_id: resPartnerId1,
+ rating_image_url: "/rating/static/src/img/rating_5.png",
+ rating_text: "top",
+ }]);
+ const { afterEvent, createMessagingMenuComponent } = await start();
+ await createMessagingMenuComponent();
+ await afterNextRender(() => 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.strictEqual(
+ document.querySelector('.o_ThreadNeedactionPreview_ratingText').textContent,
+ "Rating:",
+ "should display the correct content (Rating:)"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview_ratingImage',
+ "should have a rating image in the body"
+ );
+ assert.strictEqual(
+ $('.o_ThreadNeedactionPreview_ratingImage').attr('data-src'),
+ "/rating/static/src/img/rating_5.png",
+ "should contain the correct rating image"
+ );
+ assert.strictEqual(
+ $('.o_ThreadNeedactionPreview_ratingImage').attr('alt'),
+ "top",
+ "should contain the correct rating text"
+ );
+});
+
+});
diff --git a/addons/test_mail_full/static/tests/qunit_suite_tests/thread_preview_tests.js b/addons/test_mail_full/static/tests/qunit_suite_tests/thread_preview_tests.js
new file mode 100644
index 00000000000..4b3d9ad21bf
--- /dev/null
+++ b/addons/test_mail_full/static/tests/qunit_suite_tests/thread_preview_tests.js
@@ -0,0 +1,56 @@
+/** @odoo-module **/
+
+import { afterNextRender, start, startServer } from '@mail/../tests/helpers/test_utils';
+
+QUnit.module('test_mail_full', {}, function () {
+QUnit.module('thread_preview_tests.js');
+
+QUnit.test('rating value displayed on the thread preview', async function (assert) {
+ assert.expect(4);
+
+ const pyEnv = await startServer();
+ const resPartnerId1 = pyEnv['res.partner'].create();
+ const mailChannelId1 = pyEnv['mail.channel'].create();
+ const mailMessageId1 = pyEnv['mail.message'].create([
+ { author_id: resPartnerId1, model: 'mail.channel', res_id: mailChannelId1 },
+ ]);
+ pyEnv['rating.rating'].create({
+ consumed: true,
+ message_id: mailMessageId1,
+ partner_id: resPartnerId1,
+ rating_image_url: "/rating/static/src/img/rating_5.png",
+ rating_text: "top",
+ });
+ const { afterEvent, createMessagingMenuComponent } = await start();
+ await createMessagingMenuComponent();
+ await afterNextRender(() => 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.strictEqual(
+ document.querySelector('.o_ThreadPreview_ratingText').textContent,
+ "Rating:",
+ "should display the correct content (Rating:)"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadPreview_ratingImage',
+ "should have a rating image in the body"
+ );
+ assert.strictEqual(
+ $('.o_ThreadPreview_ratingImage').attr('data-src'),
+ "/rating/static/src/img/rating_5.png",
+ "should contain the correct rating image"
+ );
+ assert.strictEqual(
+ $('.o_ThreadPreview_ratingImage').attr('alt'),
+ "top",
+ "should contain the correct rating text"
+ );
+});
+
+});