[IMP] mail, *: remove createThreadViewComponent in tests.

*: sms, snailmail.

The createThreadViewComponent helper was used during tests to mount
a thread view component  and test it. However, this approach is not
very realistic and blocks some waiting PRs. In order to get closer
from the reality, let's mount a webClient so that the thread view
component can be mounted in its real environment.

task-2871066

closes odoo/odoo#94691

Related: odoo/enterprise#28954
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
tsm-odoo
2022-06-29 17:11:30 +02:00
committed by Matthieu Stockbauer
parent 59ef804bba
commit bc2bf646c9
11 changed files with 310 additions and 634 deletions
+44 -42
View File
@@ -413,45 +413,8 @@ function getCreateNotificationListComponent({ env, target }) {
};
}
function getCreateThreadViewComponent({ afterEvent, env, target }) {
return async function createThreadViewComponent(threadView, otherProps = {}, { isFixedSize = false, waitUntilMessagesLoaded = true } = {}) {
let actualTarget;
if (isFixedSize) {
// needed to allow scrolling in some tests
const div = document.createElement('div');
Object.assign(div.style, {
display: 'flex',
'flex-flow': 'column',
height: '300px',
});
target.appendChild(div);
actualTarget = div;
} else {
actualTarget = target;
}
async function func() {
return createRootMessagingComponent(env, "ThreadView", { props: { record: threadView, ...otherProps }, target: actualTarget });
}
if (waitUntilMessagesLoaded) {
await afterNextRender(() => afterEvent({
eventName: 'o-thread-view-hint-processed',
func,
message: "should wait until thread loaded messages after creating thread view component",
predicate: ({ hint, threadViewer }) => {
return (
hint.type === 'messages-loaded' &&
threadViewer.threadView === threadView
);
},
}));
} else {
await func();
}
};
}
function getOpenDiscuss(webClient, { context, params, ...props } = {}) {
return async function openDiscuss() {
function getOpenDiscuss(afterEvent, webClient, { context = {}, params, ...props } = {}) {
return async function openDiscuss({ waitUntilMessagesLoaded = true } = {}) {
const actionOpenDiscuss = {
// hardcoded actionId, required for discuss_container props validation.
id: 104,
@@ -460,10 +423,49 @@ function getOpenDiscuss(webClient, { context, params, ...props } = {}) {
tag: 'mail.action_discuss',
type: 'ir.actions.client',
};
await afterNextRender(() => doAction(webClient, actionOpenDiscuss, { props }));
if (waitUntilMessagesLoaded) {
let threadId = context.active_id;
if (typeof threadId === 'string') {
threadId = parseInt(threadId.split('_')[1]);
}
return afterNextRender(() => afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => doAction(webClient, actionOpenDiscuss, { props }),
message: "should wait until discuss loaded its messages",
predicate: ({ hint, threadViewer }) => {
return (
hint.type === 'messages-loaded' &&
(!threadId || threadViewer.thread.id === threadId)
);
},
}));
}
return afterNextRender(() => doAction(webClient, actionOpenDiscuss, { props }));
};
}
function getOpenFormView(afterEvent, openView) {
return async function openFormView(action, { props, waitUntilMessagesLoaded = true } = {}) {
action['views'] = [[false, 'form']];
if (waitUntilMessagesLoaded) {
return afterNextRender(() => afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => openView(action, props),
message: "should wait until chatter loaded its messages",
predicate: ({ hint, threadViewer }) => {
return (
hint.type === 'messages-loaded' &&
threadViewer &&
threadViewer.thread.model === action.res_model &&
threadViewer.thread.id === action.res_id
)
},
}));
}
return openView(action, props);
}
}
//------------------------------------------------------------------------------
// Public: start function helpers
//------------------------------------------------------------------------------
@@ -572,12 +574,12 @@ async function start(param0 = {}) {
createComposerSuggestionComponent: getCreateComposerSuggestionComponent({ env: webClient.env, target }),
createNotificationListComponent: getCreateNotificationListComponent({ env: webClient.env, target }),
createRootMessagingComponent: (componentName, props) => createRootMessagingComponent(webClient.env, componentName, { props, target }),
createThreadViewComponent: getCreateThreadViewComponent({ afterEvent, env: webClient.env, target }),
env: webClient.env,
insertText,
messaging: webClient.env.services.messaging.modelManager.messaging,
openDiscuss: getOpenDiscuss(webClient, discuss),
openDiscuss: getOpenDiscuss(afterEvent, webClient, discuss),
openView,
openFormView: getOpenFormView(afterEvent, openView),
pyEnv,
webClient,
};
@@ -26,10 +26,6 @@ registerModel({
inverse: 'qunitTestOwner',
isCausal: true,
}),
threadViewer: one('ThreadViewer', {
inverse: 'qunitTest',
isCausal: true,
}),
throttle1: one('Throttle', {
inverse: 'qunitTestOwner1',
isCausal: true,
@@ -1,17 +0,0 @@
/** @odoo-module **/
import { addFields, patchIdentifyingFields } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
// ensure that the model definition is loaded before the patch
import '@mail/models/thread_viewer';
addFields('ThreadViewer', {
qunitTest: one('QUnitTest', {
inverse: 'threadViewer',
readonly: true,
}),
});
patchIdentifyingFields('ThreadViewer', identifyingFields => {
identifyingFields[0].push('qunitTest');
});
@@ -74,7 +74,7 @@ QUnit.test('auto-select "Inbox" when discuss had channel as active thread', asyn
},
},
});
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.hasClass(
document.querySelector('.o_MobileMessagingNavbar_tab[data-tab-id="channel"]'),
'o-active',
@@ -902,7 +902,7 @@ QUnit.test('chat window: composer state conservation on toggle discuss', async f
"composer should have 2 total attachments after adding 2 attachments"
);
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await openView({
@@ -968,7 +968,7 @@ QUnit.test('chat window: scroll conservation on toggle discuss', async function
},
});
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await afterEvent({
@@ -976,7 +976,7 @@ QUnit.test('chat window: scroll conservation on toggle discuss', async function
func: () => openView({
res_id: mailChannelId1,
res_model: 'mail.channel',
views: [[false, 'form']],
views: [[false, 'list']],
}),
message: "should wait until channel restored its scroll to 142 after closing discuss",
predicate: ({ scrollTop, thread }) => {
@@ -1649,13 +1649,13 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
});
// fold chat window
await click('.o_ChatWindow_header');
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await openView({
res_id: mailChannelId1,
res_model: 'mail.channel',
views: [[false, 'form']],
views: [[false, 'list']],
});
// unfold chat window
await afterEvent({
@@ -29,7 +29,7 @@ QUnit.test('messaging not created', async function (assert) {
messagingBeforeCreationDeferred,
waitUntilMessagingCondition: 'none'
});
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.containsOnce(document.body, '.o_DiscussContainer_spinner', "should display messaging not initialized");
messagingBeforeCreationDeferred.resolve();
});
@@ -42,7 +42,7 @@ QUnit.test('discuss should be marked as opened if the component is already rende
messagingBeforeCreationDeferred,
waitUntilMessagingCondition: 'none',
});
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
await afterNextRender(() => messagingBeforeCreationDeferred.resolve());
const { messaging } = env.services.messaging.modelManager;
@@ -76,7 +76,7 @@ QUnit.test('messaging not initialized', async function (assert) {
},
waitUntilMessagingCondition: 'created',
});
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.strictEqual(
document.querySelectorAll('.o_DiscussContainer_spinner').length,
1,
@@ -97,7 +97,7 @@ QUnit.test('messaging becomes initialized', async function (assert) {
},
waitUntilMessagingCondition: 'created',
});
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.strictEqual(
document.querySelectorAll('.o_DiscussContainer_spinner').length,
1,
@@ -395,7 +395,7 @@ QUnit.test('no new message when discuss is open', async function (assert) {
assert.expect(3);
const { click, openDiscuss, openView } = await start();
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
await click(`.o_MessagingMenu_toggler`);
assert.strictEqual(
@@ -415,7 +415,7 @@ QUnit.test('no new message when discuss is open', async function (assert) {
"should have 'new message' when discuss is closed"
);
await openDiscuss();
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.strictEqual(
document.querySelectorAll(`.o_MessagingMenu_newMessageButton`).length,
0,
@@ -1,6 +1,5 @@
/** @odoo-module **/
import { insert, insertAndReplace, replace } from '@mail/model/model_field_command';
import {
afterNextRender,
dragenterFiles,
@@ -22,17 +21,12 @@ QUnit.test('dragover files on thread with composer', async function (assert) {
name: "General",
public: 'public',
});
const { createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await afterNextRender(() =>
dragenterFiles(document.querySelector('.o_ThreadView'))
);
@@ -42,92 +36,6 @@ QUnit.test('dragover files on thread with composer', async function (assert) {
);
});
QUnit.test('message list desc order', async function (assert) {
assert.expect(5);
const pyEnv = await startServer();
const mailChannelId1 = pyEnv['mail.channel'].create({
channel_type: 'channel',
name: "General",
public: 'public',
});
for (let i = 0; i <= 60; i++) {
pyEnv['mail.message'].create({
body: "not empty",
model: 'mail.channel',
res_id: mailChannelId1,
});
}
const { afterEvent, createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
order: 'desc',
thread: replace(thread),
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView, undefined, { isFixedSize: true }),
message: "should wait until channel 1 loaded initial messages",
predicate: ({ hint, threadViewer }) => {
return (
hint.type === 'messages-loaded' &&
threadViewer.thread.model === 'mail.channel' &&
threadViewer.thread.id === mailChannelId1
);
},
});
const messageItems = document.querySelectorAll(`.o_MessageList_item`);
assert.notOk(
messageItems[0].classList.contains("o_MessageList_loadMore"),
"load more link should NOT be before messages"
);
assert.ok(
messageItems[messageItems.length - 1].classList.contains("o_MessageList_loadMore"),
"load more link should be after messages"
);
assert.strictEqual(
document.querySelectorAll(`.o_Message`).length,
30,
"should have 30 messages at the beginning"
);
// scroll to bottom
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => {
const messageList = document.querySelector('.o_ThreadView_messageList');
messageList.scrollTop = messageList.scrollHeight - messageList.clientHeight;
},
message: "should wait until channel 1 loaded more messages after scrolling to bottom",
predicate: ({ hint, threadViewer }) => {
return (
hint.type === 'more-messages-loaded' &&
threadViewer.thread.model === 'mail.channel' &&
threadViewer.thread.id === mailChannelId1
);
},
});
assert.strictEqual(
document.querySelectorAll(`.o_Message`).length,
60,
"should have 60 messages after scrolled to bottom"
);
await afterNextRender(() => {
document.querySelector(`.o_ThreadView_messageList`).scrollTop = 0;
});
assert.strictEqual(
document.querySelectorAll(`.o_Message`).length,
60,
"scrolling to top should not trigger any message fetching"
);
});
QUnit.test('message list asc order', async function (assert) {
assert.expect(5);
@@ -144,19 +52,14 @@ QUnit.test('message list asc order', async function (assert) {
res_id: mailChannelId1,
});
}
const { afterEvent, createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
const { afterEvent, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView, undefined, { isFixedSize: true }),
func: openDiscuss,
message: "should wait until channel 1 loaded initial messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -230,7 +133,7 @@ QUnit.test('mark channel as fetched when a new message is loaded and as seen whe
],
channel_type: 'chat',
});
const { afterEvent, createThreadViewComponent, messaging } = await start({
const { afterEvent, click, messaging } = await start({
mockRPC(route, args) {
if (args.method === 'channel_fetched') {
assert.strictEqual(
@@ -254,16 +157,11 @@ QUnit.test('mark channel as fetched when a new message is loaded and as seen whe
}
}
});
await click(`.o_MessagingMenu_toggler`);
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await afterNextRender(async () => messaging.rpc({
route: '/mail/chat_post',
params: {
@@ -305,7 +203,10 @@ QUnit.test('mark channel as fetched and seen when a new message is loaded if com
partner_id: resPartnerId1,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const { afterEvent, createThreadViewComponent, messaging } = await start({
const { afterEvent, messaging, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
mockRPC(route, args) {
if (args.method === 'channel_fetched' && args.args[0] === mailChannelId1) {
throw new Error("'channel_fetched' RPC must not be called for created channel as message is directly seen");
@@ -319,16 +220,7 @@ QUnit.test('mark channel as fetched and seen when a new message is loaded if com
}
}
});
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
document.querySelector('.o_ComposerTextInput_textarea').focus();
// simulate receiving a message
await afterEvent({
@@ -340,7 +232,10 @@ QUnit.test('mark channel as fetched and seen when a new message is loaded if com
mockedUserId: resUsersId1,
},
message_content: "<p>fdsfsd</p>",
uuid: thread.uuid,
uuid: messaging.models['Thread'].findFromIdentifyingData({
model: 'mail.channel',
id: mailChannelId1,
}).uuid,
},
}),
message: "should wait until last seen by current partner message id changed after receiving a message while thread is focused",
@@ -372,18 +267,12 @@ QUnit.test('show message subject when subject is not the same as the thread name
res_id: mailChannelId1,
subject: "Salutations, voyageur",
});
const { createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.containsOnce(
document.body,
'.o_Message',
@@ -416,17 +305,12 @@ QUnit.test('do not show message subject when subject is the same as the thread n
res_id: mailChannelId1,
subject: "Salutations, voyageur",
});
const { createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: [['link', thread]],
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.containsNone(
document.body,
@@ -457,17 +341,12 @@ QUnit.test('[technical] new messages separator on posting message', async functi
});
const [mailChannelPartnerId] = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId1], ['partner_id', '=', pyEnv.currentPartnerId]]);
pyEnv['mail.channel.partner'].write([mailChannelPartnerId], { seen_message_id: mailMessageId1 });
const { createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.containsOnce(
document.body,
@@ -529,17 +408,12 @@ QUnit.test('new messages separator on receiving new message [REQUIRE FOCUS]', as
});
const [mailChannelPartnerId] = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId1], ['partner_id', '=', pyEnv.currentPartnerId]]);
pyEnv['mail.channel.partner'].write([mailChannelPartnerId], { seen_message_id: mailMessageId1 });
const { afterEvent, createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { afterEvent, messaging, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.containsOnce(
document.body,
@@ -563,7 +437,10 @@ QUnit.test('new messages separator on receiving new message [REQUIRE FOCUS]', as
mockedUserId: resUsersId1,
},
message_content: "hu",
uuid: thread.uuid,
uuid: messaging.models['Thread'].findFromIdentifyingData({
model: 'mail.channel',
id: mailChannelId1,
}).uuid,
},
}),
message: "should wait until new message is received",
@@ -627,17 +504,12 @@ QUnit.test('new messages separator on posting message', async function (assert)
channel_type: 'channel',
name: "General",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.containsNone(
document.body,
@@ -683,18 +555,14 @@ QUnit.test('basic rendering of canceled notification', async function (assert) {
notification_type: 'email',
res_partner_id: resPartnerId1,
});
const { afterEvent, click, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { afterEvent, click, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView),
func: openDiscuss,
message: "thread become loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -769,12 +637,13 @@ QUnit.test('should scroll to bottom on receiving new message if the list is init
res_id: mailChannelId1,
});
}
const { afterEvent, click, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { afterEvent, click, messaging } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await click(`.o_MessagingMenu_toggler`);
const thread = messaging.models['Thread'].findFromIdentifyingData({ model: 'mail.channel', id: mailChannelId1 });
await afterEvent({
eventName: 'o-component-message-list-scrolled',
async func() {
@@ -833,12 +702,13 @@ QUnit.test('should not scroll on receiving new message if the list is initially
res_id: mailChannelId1,
});
}
const { afterEvent, click, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { afterEvent, click, messaging } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const thread = messaging.models['Thread'].findFromIdentifyingData({ model: 'mail.channel', id: mailChannelId1 });
await click(`.o_MessagingMenu_toggler`);
await afterEvent({
eventName: 'o-component-message-list-scrolled',
@@ -906,16 +776,15 @@ QUnit.test("delete all attachments of message without content should no longer d
res_id: mailChannelId1,
}
);
const { afterEvent, click, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({ id: mailChannelId1, model: 'mail.channel' }),
const { afterEvent, click, messaging, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
// wait for messages of the thread to be loaded
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView),
func: openDiscuss,
message: "thread become loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -957,16 +826,15 @@ QUnit.test('delete all attachments of a message with some text content should st
model: "mail.channel",
res_id: mailChannelId1,
});
const { afterEvent, click, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({ id: mailChannelId1, model: 'mail.channel' }),
const { afterEvent, click, messaging, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
// wait for messages of the thread to be loaded
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView),
func: openDiscuss,
message: "thread become loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -1002,17 +870,12 @@ QUnit.test('Post a message containing an email address followed by a mention on
email: "testpartner@odoo.com",
name: "TestPartner",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "email@odoo.com\n");
await insertText('.o_ComposerTextInput_textarea', "@Te");
await click('.o_ComposerSuggestion');
@@ -1033,17 +896,12 @@ QUnit.test(`Mention a partner with special character (e.g. apostrophe ')`, async
email: "usatyi@example.com",
name: "Pynya's spokesman",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "@Pyn");
await click('.o_ComposerSuggestion');
await click('.o_Composer_buttonSend');
@@ -1069,17 +927,12 @@ QUnit.test('mention 2 different partners that have the same name', async functio
name: "TestPartner",
},
]);
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "@Te");
await afterNextRender(() => document.querySelectorAll('.o_ComposerSuggestion')[0].click());
await insertText('.o_ComposerTextInput_textarea', "@Te");
@@ -1105,18 +958,12 @@ QUnit.test('mention a channel with space in the name', async function (assert) {
const mailChannelId1 = pyEnv['mail.channel'].create({
name: "General good boy",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "#");
await click('.o_ComposerSuggestion');
await click('.o_Composer_buttonSend');
@@ -1139,17 +986,12 @@ QUnit.test('mention a channel with "&" in the name', async function (assert) {
const mailChannelId1 = pyEnv['mail.channel'].create({
name: "General & good",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "#");
await click('.o_ComposerSuggestion');
@@ -1173,18 +1015,12 @@ QUnit.test('mention a channel on a second line when the first line contains #',
const mailChannelId1 = pyEnv['mail.channel'].create({
name: "General good",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "#blabla\n");
await insertText('.o_ComposerTextInput_textarea', "#");
await click('.o_ComposerSuggestion');
@@ -1208,17 +1044,12 @@ QUnit.test('mention a channel when replacing the space after the mention by anot
const mailChannelId1 = pyEnv['mail.channel'].create({
name: "General good",
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "#");
await click('.o_ComposerSuggestion');
@@ -1251,17 +1082,12 @@ QUnit.test('mention 2 different channels that have the same name', async functio
name: "my channel",
},
]);
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel',
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
document.querySelector('.o_ComposerTextInput_textarea').focus();
await insertText('.o_ComposerTextInput_textarea', "#my");
await afterNextRender(() => document.querySelectorAll('.o_ComposerSuggestion')[0].click());
@@ -1286,18 +1112,14 @@ QUnit.test('show empty placeholder when thread contains no message', async funct
const pyEnv = await startServer();
const mailChannelId1 = pyEnv['mail.channel'].create({});
const { afterEvent, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { afterEvent, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView),
func: openDiscuss,
message: "should wait until thread becomes loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -1328,18 +1150,14 @@ QUnit.test('show empty placeholder when thread contains only empty messages', as
model: "mail.channel",
res_id: mailChannelId1,
});
const { afterEvent, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { afterEvent, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView),
func: openDiscuss,
message: "thread become loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -1374,18 +1192,14 @@ QUnit.test('message with subtype should be displayed (and not considered as empt
res_id: mailChannelId1,
subtype_id: mailMessageSubtypeId1,
});
const { afterEvent, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { afterEvent, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView),
func: openDiscuss,
message: "should wait until thread becomes loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -1429,18 +1243,14 @@ QUnit.test('[technical] message list with a full page of empty messages should s
res_id: mailChannelId1,
});
}
const { afterEvent, createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { afterEvent, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => createThreadViewComponent(threadViewer.threadView, undefined, { isFixedSize: true }),
func: openDiscuss,
message: "should wait until thread becomes loaded with messages",
predicate: ({ hint, threadViewer }) => {
return (
@@ -1483,21 +1293,16 @@ QUnit.test('first unseen message should be directly preceded by the new message
name: "General",
uuid: 'channel20uuid',
});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { click, insertText, messaging, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
// send a command that leads to receiving a transient message
await insertText('.o_ComposerTextInput_textarea', "/who");
await click('.o_Composer_buttonSend');
const transientMessage = threadViewer.threadView.messageViews[0].message;
const transientMessage = messaging.discuss.threadViewer.threadView.messageViews[0].message;
// composer is focused by default, we remove that focus
document.querySelector('.o_ComposerTextInput_textarea').blur();
@@ -1537,17 +1342,12 @@ QUnit.test('composer should be focused automatically after clicking on the send
const pyEnv = await startServer();
const mailChannelId1 = pyEnv['mail.channel'].create({});
const { click, createThreadViewComponent, insertText, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { click, insertText, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
await insertText('.o_ComposerTextInput_textarea', "Dummy Message");
await click('.o_Composer_buttonSend');
assert.hasClass(
@@ -1565,23 +1365,17 @@ QUnit.test('failure on loading messages should display error', async function (a
channel_type: 'channel',
name: "General",
});
const { createThreadViewComponent, messaging } = await start({
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
async mockRPC(route, args) {
if (route === '/mail/channel/messages') {
return Promise.reject();
}
},
});
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView, undefined, { waitUntilMessagesLoaded: false });
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.containsOnce(
document.body,
@@ -1598,23 +1392,17 @@ QUnit.test('failure on loading messages should prompt retry button', async funct
channel_type: 'channel',
name: "General",
});
const { createThreadViewComponent, messaging } = await start({
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
async mockRPC(route, args) {
if (route === '/mail/channel/messages') {
return Promise.reject();
}
},
});
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView, undefined, { waitUntilMessagesLoaded: false });
await openDiscuss({ waitUntilMessagesLoaded: false });
assert.containsOnce(
document.body,
@@ -1642,7 +1430,10 @@ QUnit.test('failure on loading more messages should not alter message list displ
res_id: mailChannelId1,
};
}));
const { click, createThreadViewComponent, messaging } = await start({
const { click, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
async mockRPC(route, args) {
if (route === '/mail/channel/messages') {
if (messageFetchShouldFail) {
@@ -1651,16 +1442,7 @@ QUnit.test('failure on loading more messages should not alter message list displ
}
},
});
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView, undefined, { waitUntilMessagesLoaded: false });
await openDiscuss();
messageFetchShouldFail = true;
await click('.o_MessageList_loadMore');
@@ -1691,7 +1473,10 @@ QUnit.test('failure on loading more messages should display error and prompt ret
res_id: mailChannelId1,
};
}));
const { click, createThreadViewComponent, messaging } = await start({
const { click, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
async mockRPC(route, args) {
if (route === '/mail/channel/messages') {
if (messageFetchShouldFail) {
@@ -1700,16 +1485,7 @@ QUnit.test('failure on loading more messages should display error and prompt ret
}
},
});
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView, undefined, { waitUntilMessagesLoaded: false });
await openDiscuss();
messageFetchShouldFail = true;
await click('.o_MessageList_loadMore');
@@ -1749,25 +1525,19 @@ QUnit.test('Retry loading more messages on failed load more messages should load
res_id: mailChannelId1,
};
}));
const { afterEvent, click, createThreadViewComponent, messaging } = await start({
const { afterEvent, click, openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
async mockRPC(route, args) {
if (route === '/mail/channel/messages') {
if (messageFetchShouldFail) {
return Promise.reject();
}
}
},
}
});
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView, undefined, { waitUntilMessagesLoaded: false });
await openDiscuss();
messageFetchShouldFail = true;
await click('.o_MessageList_loadMore');
@@ -1805,17 +1575,12 @@ QUnit.test("highlight the message mentioning the current user inside the channel
partner_ids: [pyEnv.currentPartnerId],
res_id: mailChannelId1,
});
const { createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.hasClass(
document.querySelector(`.o_MessageList .o_Message`),
'o-highlighted',
@@ -1843,17 +1608,12 @@ QUnit.test("not highlighting the message if not mentioning the current user insi
partner_ids: [resPartnerId1],
res_id: mailChannelId1,
});
const { createThreadViewComponent, messaging } = await start();
const thread = messaging.models['Thread'].findFromIdentifyingData({
id: mailChannelId1,
model: 'mail.channel'
const { openDiscuss } = await start({
discuss: {
context: { active_id: mailChannelId1 },
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: replace(thread),
});
await createThreadViewComponent(threadViewer.threadView);
await openDiscuss();
assert.doesNotHaveClass(
document.querySelector(`.o_MessageList .o_Message`),
'o-highlighted',
@@ -455,26 +455,20 @@ QUnit.test('chatter updating', async function (assert) {
</div>
</form>`,
};
const { afterEvent, openView } = await start({
const { afterEvent, openFormView } = await start({
serverData: { views },
});
const openViewAction = {
res_model: 'res.partner',
res_id: resPartnerId1,
views: [[false, 'form']],
};
await afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => openView(openViewAction, { resIds: [resPartnerId1, resPartnerId2] }),
message: "should wait until partner 11 thread loaded messages initially",
predicate: ({ hint, threadViewer }) => {
return (
hint.type === 'messages-loaded' &&
threadViewer.thread.model === 'res.partner' &&
threadViewer.thread.id === resPartnerId1
);
await openFormView(
{
res_model: 'res.partner',
res_id: resPartnerId1,
},
});
{
props: {
resIds: [resPartnerId1, resPartnerId2],
},
}
);
await afterNextRender(() => afterEvent({
eventName: 'o-thread-view-hint-processed',
func: () => document.querySelector('.o_pager_next').click(),
@@ -1,6 +1,5 @@
/** @odoo-module **/
import { insert, insertAndReplace } from '@mail/model/model_field_command';
import { makeDeferred } from '@mail/utils/deferred';
import {
afterNextRender,
@@ -19,12 +18,11 @@ QUnit.test('Notification Sent', async function (assert) {
const pyEnv = await startServer();
const resPartnerId1 = pyEnv['res.partner'].create({ name: "Someone", partner_share: true });
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'sms',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
mail_message_id: mailMessageId1,
@@ -32,16 +30,11 @@ QUnit.test('Notification Sent', async function (assert) {
notification_type: 'sms',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -100,12 +93,11 @@ QUnit.test('Notification Error', async function (assert) {
const openResendActionDef = makeDeferred();
const pyEnv = await startServer();
const resPartnerId1 = pyEnv['res.partner'].create({ name: "Someone", partner_share: true });
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'sms',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
mail_message_id: mailMessageId1,
@@ -113,7 +105,11 @@ QUnit.test('Notification Error', async function (assert) {
notification_type: 'sms',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, env, messaging } = await start();
const { env, openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
patchWithCleanup(env.services.action, {
doAction(action, options) {
assert.step('do_action');
@@ -130,15 +126,6 @@ QUnit.test('Notification Error', async function (assert) {
openResendActionDef.resolve();
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -1,6 +1,5 @@
/** @odoo-module **/
import { insert, insertAndReplace } from '@mail/model/model_field_command';
import {
afterNextRender,
start,
@@ -21,12 +20,11 @@ QUnit.test('Sent', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
mail_message_id: mailMessageId1,
@@ -34,16 +32,11 @@ QUnit.test('Sent', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -99,12 +92,11 @@ QUnit.test('Canceled', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
mail_message_id: mailMessageId1,
@@ -112,16 +104,11 @@ QUnit.test('Canceled', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -177,12 +164,11 @@ QUnit.test('Pending', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
mail_message_id: mailMessageId1,
@@ -190,16 +176,11 @@ QUnit.test('Pending', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start();
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
const { openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -255,12 +236,11 @@ QUnit.test('No Price Available', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
failure_type: 'sn_price',
@@ -269,23 +249,17 @@ QUnit.test('No Price Available', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start({
const { openFormView } = await start({
async mockRPC(route, args) {
if (args.method === 'cancel_letter' && args.model === 'mail.message' && args.args[0][0] === mailMessageId1) {
assert.step(args.method);
}
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
'.o_Message',
@@ -348,12 +322,11 @@ QUnit.test('Credit Error', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
failure_type: 'sn_credit',
@@ -362,22 +335,17 @@ QUnit.test('Credit Error', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start({
const { openFormView } = await start({
async mockRPC(route, args) {
if (args.method === 'send_letter' && args.model === 'mail.message' && args.args[0][0] === mailMessageId1) {
assert.step(args.method);
}
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -446,12 +414,11 @@ QUnit.test('Trial Error', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
failure_type: 'sn_trial',
@@ -460,22 +427,17 @@ QUnit.test('Trial Error', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, messaging } = await start({
const { openFormView } = await start({
async mockRPC(route, args) {
if (args.method === 'send_letter' && args.model === 'mail.message' && args.args[0][0] === mailMessageId1) {
assert.step(args.method);
}
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -544,12 +506,11 @@ QUnit.test('Format Error', async function (assert) {
name: "Someone",
partner_share: true,
});
const mailChannelId1 = pyEnv['mail.channel'].create({});
const mailMessageId1 = pyEnv['mail.message'].create({
body: 'not empty',
message_type: 'snailmail',
model: 'mail.channel',
res_id: mailChannelId1,
model: 'res.partner',
res_id: resPartnerId1,
});
pyEnv['mail.notification'].create({
failure_type: 'sn_format',
@@ -558,7 +519,11 @@ QUnit.test('Format Error', async function (assert) {
notification_type: 'snail',
res_partner_id: resPartnerId1,
});
const { createThreadViewComponent, env, messaging } = await start();
const { env, openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
patchWithCleanup(env.services.action, {
doAction(action, options) {
assert.step('do_action');
@@ -574,15 +539,6 @@ QUnit.test('Format Error', async function (assert) {
);
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({
id: mailChannelId1,
model: 'mail.channel',
}),
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,
@@ -635,7 +591,11 @@ QUnit.test('Missing Required Fields', async function (assert) {
message_id: mailMessageId1,
});
const { createThreadViewComponent, env, messaging } = await start();
const { env, openFormView } = await start();
await openFormView({
res_id: resPartnerId1,
res_model: 'res.partner',
});
patchWithCleanup(env.services.action, {
doAction(action, options) {
assert.step('do_action');
@@ -651,12 +611,6 @@ QUnit.test('Missing Required Fields', async function (assert) {
);
},
});
const threadViewer = messaging.models['ThreadViewer'].create({
hasThreadView: true,
qunitTest: insertAndReplace(),
thread: insert({ id: resPartnerId1, model: 'res.partner' }),
});
await createThreadViewComponent(threadViewer.threadView);
assert.containsOnce(
document.body,