summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-10-09 10:55:21 +0000
committerSébastien Theys <seb@odoo.com>2020-10-09 12:04:01 +0000
commitaa006658ea25abaa2b8d03003f1d4f9214fb6468 (patch)
tree06ba9046da4f2c4d6382c88a3075f3ec912b7ff9 /addons/mail/static/src/components
parentde62d7bae2c466b74b0d9a83398c5f031233afdb (diff)
[FIX] mail: fix discuss search filters and related issues
- don't reset `stringifiedDomain` when changing thread (this led to the control panel displaying obsolete filters) - refresh non-main cache that are currently displayed when there is a known change of messages in the same thread's main cache - refresh non-main-cache on displaying them, to fetch potentially new messages - fix discuss "select all" conditions task-2171873 task-2282374 closes odoo/odoo#59631 X-original-commit: b89dc5f949b3ad062fc4197a4152066c207e1bf9 Related: odoo/enterprise#13958 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Diffstat (limited to 'addons/mail/static/src/components')
-rw-r--r--addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js407
-rw-r--r--addons/mail/static/src/components/message_list/message_list.js9
-rw-r--r--addons/mail/static/src/components/thread_view/thread_view_tests.js10
3 files changed, 420 insertions, 6 deletions
diff --git a/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js b/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js
new file mode 100644
index 00000000000..21146652d28
--- /dev/null
+++ b/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js
@@ -0,0 +1,407 @@
+odoo.define('mail/static/src/components/discuss/tests/discuss_domain_tests.js', function (require) {
+'use strict';
+
+const {
+ afterEach,
+ afterNextRender,
+ beforeEach,
+ start,
+} = require('mail/static/src/utils/test_utils.js');
+
+QUnit.module('mail', {}, function () {
+QUnit.module('components', {}, function () {
+QUnit.module('discuss', {}, function () {
+QUnit.module('discuss_domain_tests.js', {
+ beforeEach() {
+ beforeEach(this);
+
+ this.start = async params => {
+ const { afterEvent, env, widget } = await start(Object.assign({}, params, {
+ autoOpenDiscuss: true,
+ data: this.data,
+ hasDiscuss: true,
+ }));
+ this.afterEvent = afterEvent;
+ this.env = env;
+ this.widget = widget;
+ };
+ },
+ afterEach() {
+ afterEach(this);
+ },
+});
+
+QUnit.test('discuss should filter messages based on given domain', async function (assert) {
+ assert.expect(2);
+
+ this.data['mail.message'].records.push({
+ body: "test",
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ }, {
+ body: "not empty",
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ });
+ await this.start();
+ assert.containsN(
+ document.body,
+ '.o_Message',
+ 2,
+ "should have 2 messages in Inbox initially"
+ );
+
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ // simulate control panel search
+ this.env.messaging.discuss.update({
+ stringifiedDomain: JSON.stringify([['body', 'ilike', 'test']]),
+ });
+ },
+ message: "should wait until search filter is applied",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.box' &&
+ threadViewer.thread.id === 'inbox'
+ );
+ },
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should only have the 1 message containing 'test' remaining after doing a search"
+ );
+});
+
+QUnit.test('discuss should keep filter domain on changing thread', async function (assert) {
+ assert.expect(3);
+
+ this.data['mail.channel'].records.push({ id: 20 });
+ this.data['mail.message'].records.push({
+ body: "test",
+ channel_ids: [20],
+ }, {
+ body: "not empty",
+ channel_ids: [20],
+ });
+ await this.start();
+ const channel = this.env.models['mail.thread'].findFromIdentifyingData({
+ id: 20,
+ model: 'mail.channel',
+ });
+ assert.containsNone(
+ document.body,
+ '.o_Message',
+ "should have no message in Inbox initially"
+ );
+
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ // simulate control panel search
+ this.env.messaging.discuss.update({
+ stringifiedDomain: JSON.stringify([['body', 'ilike', 'test']]),
+ });
+ },
+ message: "should wait until search filter is applied",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.box' &&
+ threadViewer.thread.id === 'inbox'
+ );
+ },
+ });
+ assert.containsNone(
+ document.body,
+ '.o_Message',
+ "should have still no message in Inbox after doing a search"
+ );
+
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ document.querySelector(`
+ .o_DiscussSidebar_item[data-thread-local-id="${channel.localId}"]
+ `).click();
+ },
+ message: "should wait until channel 20 is loaded after clicking on it",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 20
+ );
+ },
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should only have the 1 message containing 'test' in channel 20 (due to the domain still applied on changing thread)"
+ );
+});
+
+QUnit.test('discuss should refresh filtered thread on receiving new message', async function (assert) {
+ assert.expect(2);
+
+ this.data['mail.channel'].records.push({ id: 20 });
+ await this.start();
+ const channel = this.env.models['mail.thread'].findFromIdentifyingData({
+ id: 20,
+ model: 'mail.channel',
+ });
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ document.querySelector(`
+ .o_DiscussSidebar_item[data-thread-local-id="${channel.localId}"]
+ `).click();
+ },
+ message: "should wait until channel 20 is loaded after clicking on it",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 20
+ );
+ },
+ });
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ // simulate control panel search
+ this.env.messaging.discuss.update({
+ stringifiedDomain: JSON.stringify([['body', 'ilike', 'test']]),
+ });
+ },
+ message: "should wait until search filter is applied",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 20
+ );
+ },
+ });
+ assert.containsNone(
+ document.body,
+ '.o_Message',
+ "should have initially no message in channel 20 matching the search 'test'"
+ );
+
+ // simulate receiving a message
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => this.env.services.rpc({
+ route: '/mail/chat_post',
+ params: {
+ uuid: channel.uuid,
+ message_content: "test",
+ },
+ }),
+ message: "should wait until channel 20 refreshed its filtered message list",
+ predicate: data => {
+ return (
+ data.threadViewer.thread.model === 'mail.channel' &&
+ data.threadViewer.thread.id === 20 &&
+ data.hint.type === 'messages-loaded'
+ );
+ },
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should only have the 1 message containing 'test' in channel 20 after just receiving it"
+ );
+});
+
+QUnit.test('discuss should refresh filtered thread on changing thread', async function (assert) {
+ assert.expect(4);
+
+ this.data['mail.channel'].records.push({ id: 20 }, { id: 21 });
+ await this.start();
+ const channel20 = this.env.models['mail.thread'].findFromIdentifyingData({
+ id: 20,
+ model: 'mail.channel',
+ });
+ const channel21 = this.env.models['mail.thread'].findFromIdentifyingData({
+ id: 21,
+ model: 'mail.channel',
+ });
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ document.querySelector(`
+ .o_DiscussSidebar_item[data-thread-local-id="${channel20.localId}"]
+ `).click();
+ },
+ message: "should wait until channel 20 is loaded after clicking on it",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 20
+ );
+ },
+ });
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ // simulate control panel search
+ this.env.messaging.discuss.update({
+ stringifiedDomain: JSON.stringify([['body', 'ilike', 'test']]),
+ });
+ },
+ message: "should wait until search filter is applied",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 20
+ );
+ },
+ });
+ assert.containsNone(
+ document.body,
+ '.o_Message',
+ "should have initially no message in channel 20 matching the search 'test'"
+ );
+
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ document.querySelector(`
+ .o_DiscussSidebar_item[data-thread-local-id="${channel21.localId}"]
+ `).click();
+ },
+ message: "should wait until channel 21 is loaded after clicking on it",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 21
+ );
+ },
+ });
+ assert.containsNone(
+ document.body,
+ '.o_Message',
+ "should have no message in channel 21 matching the search 'test'"
+ );
+ // simulate receiving a message on channel 20 while channel 21 is displayed
+ await this.env.services.rpc({
+ route: '/mail/chat_post',
+ params: {
+ uuid: channel20.uuid,
+ message_content: "test",
+ },
+ });
+ assert.containsNone(
+ document.body,
+ '.o_Message',
+ "should still have no message in channel 21 matching the search 'test' after receiving a message on channel 20"
+ );
+
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ document.querySelector(`
+ .o_DiscussSidebar_item[data-thread-local-id="${channel20.localId}"]
+ `).click();
+ },
+ message: "should wait until channel 20 is loaded with the new message after clicking on it",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 20 &&
+ threadViewer.threadCache.fetchedMessages.length === 1
+ );
+ },
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should now have the 1 message containing 'test' in channel 20 when displaying it, after having received the message while the channel was not visible"
+ );
+});
+
+QUnit.test('select all and unselect all buttons should work on filtered thread', async function (assert) {
+ assert.expect(4);
+
+ this.data['mail.channel'].records.push({
+ id: 20,
+ is_moderator: true,
+ moderation: true,
+ name: "general",
+ });
+ this.data['mail.message'].records.push({
+ body: "<p>test</p>",
+ model: 'mail.channel',
+ moderation_status: 'pending_moderation',
+ res_id: 20,
+ });
+ await this.start();
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ document.querySelector(`
+ .o_DiscussSidebar_item[data-thread-local-id="${this.env.messaging.moderation.localId}"]
+ `).click();
+ },
+ message: "should wait until moderation box is loaded after clicking on it",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.box' &&
+ threadViewer.thread.id === 'moderation'
+ );
+ },
+ });
+ await this.afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func: () => {
+ // simulate control panel search
+ this.env.messaging.discuss.update({
+ stringifiedDomain: JSON.stringify([['body', 'ilike', 'test']]),
+ });
+ },
+ message: "should wait until search filter is applied",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.box' &&
+ threadViewer.thread.id === 'moderation'
+ );
+ },
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should only have the 1 message containing 'test' in moderation box"
+ );
+ assert.notOk(
+ document.querySelector('.o_Message_checkbox').checked,
+ "the moderation checkbox should not be checked initially"
+ );
+
+ await afterNextRender(() => document.querySelector('.o_widget_Discuss_controlPanelButtonSelectAll').click());
+ assert.ok(
+ document.querySelector('.o_Message_checkbox').checked,
+ "the moderation checkbox should be checked after clicking on 'select all'"
+ );
+
+ await afterNextRender(() => document.querySelector('.o_widget_Discuss_controlPanelButtonUnselectAll').click());
+ assert.notOk(
+ document.querySelector('.o_Message_checkbox').checked,
+ "the moderation checkbox should be unchecked after clicking on 'unselect all'"
+ );
+});
+
+});
+});
+});
+
+});
diff --git a/addons/mail/static/src/components/message_list/message_list.js b/addons/mail/static/src/components/message_list/message_list.js
index 72368ec3689..b7332514141 100644
--- a/addons/mail/static/src/components/message_list/message_list.js
+++ b/addons/mail/static/src/components/message_list/message_list.js
@@ -124,12 +124,18 @@ class MessageList extends Component {
case 'home-menu-shown':
this._adjustFromHomeMenuShown(hint);
break;
+ case 'messages-loaded':
+ this.threadView.markComponentHintProcessed(hint);
+ break;
case 'message-received':
this._adjustFromMessageReceived(hint);
break;
case 'more-messages-loaded':
this._adjustFromMoreMessagesLoaded(hint);
break;
+ case 'new-messages-loaded':
+ this.threadView.markComponentHintProcessed(hint);
+ break;
}
}
this._willPatchSnapshot = undefined;
@@ -333,9 +339,6 @@ class MessageList extends Component {
isProcessed = true;
}
if (isProcessed) {
- this.env.messagingBus.trigger('o-component-message-list-thread-cache-changed', {
- threadViewer: this.threadView.threadViewer,
- });
this.threadView.markComponentHintProcessed(hint);
}
}
diff --git a/addons/mail/static/src/components/thread_view/thread_view_tests.js b/addons/mail/static/src/components/thread_view/thread_view_tests.js
index 320512a0f8c..2bf4c08a7a8 100644
--- a/addons/mail/static/src/components/thread_view/thread_view_tests.js
+++ b/addons/mail/static/src/components/thread_view/thread_view_tests.js
@@ -741,13 +741,17 @@ QUnit.test('basic rendering of canceled notification', async function (assert) {
}]],
});
await this.afterEvent({
- eventName: 'o-component-message-list-thread-cache-changed',
+ eventName: 'o-thread-view-hint-processed',
func: () => {
this.createThreadViewComponent(threadViewer.threadView);
},
message: "thread become loaded with messages",
- predicate: ({ threadViewer }) => {
- return threadViewer.thread.model === 'mail.channel' && threadViewer.thread.id === 11;
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer.thread.model === 'mail.channel' &&
+ threadViewer.thread.id === 11
+ );
},
});