1. channel mention should work for channels, chats, and groups, even if there is a group restriction for the channel. 2. channel mention in livechat is disabled because the visitor can't get access to the channel. 3. remove the fetch and search for channel mentions in livechat so that there is no extra rpc call for unuseful data. 4. add a test for the channel mention in thread. closes odoo/odoo#155380 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
1061 lines
43 KiB
JavaScript
1061 lines
43 KiB
JavaScript
/* @odoo-module */
|
|
|
|
import { startServer } from "@bus/../tests/helpers/mock_python_environment";
|
|
|
|
import { Command } from "@mail/../tests/helpers/command";
|
|
import { start } from "@mail/../tests/helpers/test_utils";
|
|
|
|
import { config as transitionConfig } from "@web/core/transition";
|
|
import { makeDeferred, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
|
|
import {
|
|
click,
|
|
contains,
|
|
createFile,
|
|
dragenterFiles,
|
|
focus,
|
|
insertText,
|
|
scroll,
|
|
triggerEvents,
|
|
} from "@web/../tests/utils";
|
|
|
|
QUnit.module("thread");
|
|
|
|
QUnit.test("dragover files on thread with composer", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_type: "channel",
|
|
group_public_id: false,
|
|
name: "General",
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
const files = [
|
|
await createFile({
|
|
content: "hello, world",
|
|
contentType: "text/plain",
|
|
name: "text3.txt",
|
|
}),
|
|
];
|
|
await dragenterFiles(".o-mail-Thread", files);
|
|
await contains(".o-mail-Dropzone");
|
|
});
|
|
|
|
QUnit.test("load more messages from channel (auto-load on scroll)", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_type: "channel",
|
|
group_public_id: false,
|
|
name: "General",
|
|
});
|
|
for (let i = 0; i <= 60; i++) {
|
|
pyEnv["mail.message"].create({
|
|
body: i.toString(),
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
}
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains("button", { text: "Load More", before: [".o-mail-Message", { count: 30 }] });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
await scroll(".o-mail-Thread", 0);
|
|
await contains(".o-mail-Message", { count: 60 });
|
|
await contains(".o-mail-Message", { text: "30", after: [".o-mail-Message", { text: "29" }] });
|
|
});
|
|
|
|
QUnit.test("show message subject when subject is not the same as the thread name", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_type: "channel",
|
|
group_public_id: false,
|
|
name: "General",
|
|
});
|
|
pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
subject: "Salutations, voyageur",
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message", { text: "Subject: Salutations, voyageurnot empty" });
|
|
});
|
|
|
|
QUnit.test("do not show message subject when subject is the same as the thread name", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_type: "channel",
|
|
group_public_id: false,
|
|
name: "Salutations, voyageur",
|
|
});
|
|
pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
subject: "Salutations, voyageur",
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message", { text: "not empty" });
|
|
await contains(".o-mail-Message", {
|
|
count: 0,
|
|
text: "Subject: Salutations, voyageurnot empty",
|
|
});
|
|
});
|
|
|
|
QUnit.test("auto-scroll to bottom of thread on load", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
|
for (let i = 1; i <= 25; i++) {
|
|
pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
}
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message", { count: 25 });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
});
|
|
|
|
QUnit.test("display day separator before first message of the day", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "" });
|
|
pyEnv["mail.message"].create([
|
|
{
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
},
|
|
{
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
},
|
|
]);
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-DateSection");
|
|
});
|
|
|
|
QUnit.test("do not display day separator if all messages of the day are empty", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "" });
|
|
pyEnv["mail.message"].create({
|
|
body: "",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Thread", { text: "There are no messages in this conversation." });
|
|
await contains(".o-mail-DateSection", { count: 0 });
|
|
});
|
|
|
|
QUnit.test("scroll position is kept when navigating from one channel to another", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId_1 = pyEnv["discuss.channel"].create({ name: "channel-1" });
|
|
const channelId_2 = pyEnv["discuss.channel"].create({ name: "channel-2" });
|
|
// Fill both channels with random messages in order for the scrollbar to
|
|
// appear.
|
|
pyEnv["mail.message"].create(
|
|
Array(50)
|
|
.fill(0)
|
|
.map((_, index) => ({
|
|
body: "Non Empty Body ".repeat(25),
|
|
message_type: "comment",
|
|
model: "discuss.channel",
|
|
res_id: index < 20 ? channelId_1 : channelId_2,
|
|
}))
|
|
);
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId_1);
|
|
await contains(".o-mail-Message", { count: 20 });
|
|
const scrollValue1 = $(".o-mail-Thread")[0].scrollHeight / 2;
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
await scroll(".o-mail-Thread", scrollValue1);
|
|
await click(".o-mail-DiscussSidebarChannel", { text: "channel-2" });
|
|
await contains(".o-mail-Message", { count: 30 });
|
|
const scrollValue2 = $(".o-mail-Thread")[0].scrollHeight / 3;
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
await scroll(".o-mail-Thread", scrollValue2);
|
|
await click(".o-mail-DiscussSidebarChannel", { text: "channel-1" });
|
|
await contains(".o-mail-Message", { count: 20 });
|
|
await contains(".o-mail-Thread", { scroll: scrollValue1 });
|
|
await click(".o-mail-DiscussSidebarChannel", { text: "channel-2" });
|
|
await contains(".o-mail-Message", { count: 30 });
|
|
await contains(".o-mail-Thread", { scroll: scrollValue2 });
|
|
});
|
|
|
|
QUnit.test("thread is still scrolling after scrolling up then to bottom", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "channel-1" });
|
|
pyEnv["mail.message"].create(
|
|
Array(20)
|
|
.fill(0)
|
|
.map(() => ({
|
|
body: "Non Empty Body ".repeat(25),
|
|
message_type: "comment",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
}))
|
|
);
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message", { count: 20 });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
await scroll(".o-mail-Thread", $(".o-mail-Thread")[0].scrollHeight / 2);
|
|
await scroll(".o-mail-Thread", "bottom");
|
|
await insertText(".o-mail-Composer-input", "123");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message", { count: 21 });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
});
|
|
|
|
QUnit.test("Can mention other channels in a group-restricted channel", async () => {
|
|
const pyEnv = await startServer();
|
|
const groupId = pyEnv["res.groups"].create({
|
|
name: "Mario Group",
|
|
});
|
|
const [channelId1] = pyEnv["discuss.channel"].create([
|
|
{
|
|
channel_type: "channel",
|
|
group_public_id: groupId,
|
|
name: "Marios",
|
|
},
|
|
{
|
|
channel_type: "channel",
|
|
group_public_id: false,
|
|
name: "Link and Zelda",
|
|
},
|
|
]);
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId1);
|
|
await insertText(".o-mail-Composer-input", "#");
|
|
await contains(".o-mail-Composer-suggestion", { text: "#Marios" });
|
|
await contains(".o-mail-Composer-suggestion", { text: "#Link and Zelda" });
|
|
await click(".o-mail-Composer-suggestion", { text: "#Link and Zelda" });
|
|
await contains(".o-mail-Composer-input", { value: "#Link and Zelda " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message-body .o_channel_redirect", { text: "#Link and Zelda" });
|
|
});
|
|
|
|
QUnit.test("mention a channel with space in the name", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General good boy" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "#");
|
|
await click(".o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "#General good boy " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message-body .o_channel_redirect", { text: "#General good boy" });
|
|
});
|
|
|
|
QUnit.test('mention a channel with "&" in the name', async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General & good" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "#");
|
|
await click(".o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "#General & good " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message-body .o_channel_redirect", { text: "#General & good" });
|
|
});
|
|
|
|
QUnit.test(
|
|
"mark channel as fetched when a new message is loaded and as seen when focusing composer [REQUIRE FOCUS]",
|
|
async (assert) => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({
|
|
email: "fred@example.com",
|
|
name: "Fred",
|
|
});
|
|
const userId = pyEnv["res.users"].create({ partner_id: partnerId });
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
name: "test",
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
channel_type: "chat",
|
|
});
|
|
const { env } = await start({
|
|
mockRPC(route, args) {
|
|
if (args.method === "channel_fetched") {
|
|
assert.strictEqual(args.args[0][0], channelId);
|
|
assert.strictEqual(args.model, "discuss.channel");
|
|
assert.step("rpc:channel_fetch");
|
|
} else if (route === "/discuss/channel/set_last_seen_message") {
|
|
assert.strictEqual(args.channel_id, channelId);
|
|
assert.step("rpc:set_last_seen_message");
|
|
}
|
|
},
|
|
});
|
|
await contains(".o_menu_systray i[aria-label='Messages']");
|
|
pyEnv.withUser(userId, () =>
|
|
env.services.rpc("/mail/message/post", {
|
|
post_data: { body: "Hello!", message_type: "comment" },
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
})
|
|
);
|
|
await contains(".o-mail-Message");
|
|
assert.verifySteps(["rpc:channel_fetch"]);
|
|
await contains(".o-mail-Thread-newMessage hr + span", { text: "New messages" });
|
|
await focus(".o-mail-Composer-input");
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
|
|
assert.verifySteps(["rpc:set_last_seen_message"]);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"mark channel as fetched and seen when a new message is loaded if composer is focused [REQUIRE FOCUS]",
|
|
async (assert) => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({});
|
|
const userId = pyEnv["res.users"].create({ partner_id: partnerId });
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
name: "test",
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
});
|
|
const { env, openDiscuss } = await start({
|
|
async mockRPC(route, args) {
|
|
if (args.method === "channel_fetched" && args.args[0] === channelId) {
|
|
throw new Error(
|
|
"'channel_fetched' RPC must not be called for created channel as message is directly seen"
|
|
);
|
|
} else if (route === "/discuss/channel/set_last_seen_message") {
|
|
assert.strictEqual(args.channel_id, channelId);
|
|
assert.step("rpc:set_last_seen_message");
|
|
}
|
|
},
|
|
});
|
|
openDiscuss(channelId);
|
|
await focus(".o-mail-Composer-input");
|
|
// simulate receiving a message
|
|
await pyEnv.withUser(userId, () =>
|
|
env.services.rpc("/mail/message/post", {
|
|
post_data: { body: "<p>Some new message</p>", message_type: "comment" },
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
})
|
|
);
|
|
await contains(".o-mail-Message");
|
|
assert.verifySteps(["rpc:set_last_seen_message"]);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"should scroll to bottom on receiving new message if the list is initially scrolled to bottom (asc order)",
|
|
async () => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Foreigner partner" });
|
|
const userId = pyEnv["res.users"].create({ name: "Foreigner user", partner_id: partnerId });
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
});
|
|
for (let i = 0; i <= 10; i++) {
|
|
pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
}
|
|
const { env } = await start();
|
|
await click(".o_menu_systray i[aria-label='Messages']");
|
|
await click(".o-mail-NotificationItem");
|
|
await contains(".o-mail-Message", { count: 11 });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
// simulate receiving a message
|
|
pyEnv.withUser(userId, () =>
|
|
env.services.rpc("/mail/message/post", {
|
|
post_data: { body: "hello", message_type: "comment" },
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
})
|
|
);
|
|
await contains(".o-mail-Message", { count: 12 });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"should not scroll on receiving new message if the list is initially scrolled anywhere else than bottom (asc order)",
|
|
async () => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Foreigner partner" });
|
|
const userId = pyEnv["res.users"].create({ name: "Foreigner user", partner_id: partnerId });
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
});
|
|
for (let i = 0; i <= 10; i++) {
|
|
pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
}
|
|
const { env } = await start();
|
|
await click(".o_menu_systray i[aria-label='Messages']");
|
|
await click(".o-mail-NotificationItem");
|
|
await contains(".o-mail-Message", { count: 11 });
|
|
await contains(".o-mail-Thread", { scroll: "bottom" });
|
|
await scroll(".o-mail-Thread", 0);
|
|
// simulate receiving a message
|
|
pyEnv.withUser(userId, () =>
|
|
env.services.rpc("/mail/message/post", {
|
|
post_data: { body: "hello", message_type: "comment" },
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
})
|
|
);
|
|
await contains(".o-mail-Message", { count: 12 });
|
|
await contains(".o-mail-ChatWindow .o-mail-Thread", { scroll: 0 });
|
|
}
|
|
);
|
|
|
|
QUnit.test("show empty placeholder when thread contains no message", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Thread", { text: "There are no messages in this conversation." });
|
|
await contains(".o-mail-Message", { count: 0 });
|
|
});
|
|
|
|
QUnit.test("show empty placeholder when thread contains only empty messages", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
|
pyEnv["mail.message"].create({ model: "discuss.channel", res_id: channelId });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Thread", { text: "There are no messages in this conversation." });
|
|
await contains(".o-mail-Message", { count: 0 });
|
|
});
|
|
|
|
QUnit.test(
|
|
"message list with a full page of empty messages should load more messages until there are some non-empty",
|
|
async () => {
|
|
// Technical assumptions :
|
|
// - /discuss/channel/messages fetching exactly 30 messages,
|
|
// - empty messages not being displayed
|
|
// - auto-load more being triggered on scroll, not automatically when the 30 first messages are empty
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
|
for (let i = 0; i < 50; i++) {
|
|
pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
}
|
|
for (let i = 0; i < 50; i++) {
|
|
pyEnv["mail.message"].create({ model: "discuss.channel", res_id: channelId });
|
|
}
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
// initial load: +30 empty ; (auto) load more: +20 empty +10 non-empty
|
|
await contains(".o-mail-Message", { count: 10 });
|
|
await contains("button", { text: "Load More" }); // still 40 non-empty
|
|
}
|
|
);
|
|
|
|
QUnit.test("no new messages separator on posting message (some message history)", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_member_ids: [
|
|
Command.create({ message_unread_counter: 0, partner_id: pyEnv.currentPartnerId }),
|
|
],
|
|
channel_type: "channel",
|
|
name: "General",
|
|
});
|
|
const messageId = pyEnv["mail.message"].create({
|
|
body: "first message",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
const [memberId] = pyEnv["discuss.channel.member"].search([
|
|
["channel_id", "=", channelId],
|
|
["partner_id", "=", pyEnv.currentPartnerId],
|
|
]);
|
|
pyEnv["discuss.channel.member"].write([memberId], { seen_message_id: messageId });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message");
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
await insertText(".o-mail-Composer-input", "hey!");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message", { count: 2 });
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
});
|
|
|
|
QUnit.test("new messages separator on receiving new message [REQUIRE FOCUS]", async () => {
|
|
patchWithCleanup(transitionConfig, { disabled: true });
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Foreigner partner" });
|
|
const userId = pyEnv["res.users"].create({
|
|
name: "Foreigner user",
|
|
partner_id: partnerId,
|
|
});
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_member_ids: [
|
|
Command.create({ message_unread_counter: 0, partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
channel_type: "channel",
|
|
name: "General",
|
|
});
|
|
const messageId = pyEnv["mail.message"].create({
|
|
body: "blah",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
const [memberId] = pyEnv["discuss.channel.member"].search([
|
|
["channel_id", "=", channelId],
|
|
["partner_id", "=", pyEnv.currentPartnerId],
|
|
]);
|
|
pyEnv["discuss.channel.member"].write([memberId], { seen_message_id: messageId });
|
|
const { env, openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message");
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
|
|
$(".o-mail-Composer-input")[0].blur();
|
|
// simulate receiving a message
|
|
pyEnv.withUser(userId, () =>
|
|
env.services.rpc("/mail/message/post", {
|
|
post_data: { body: "hu", message_type: "comment" },
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
})
|
|
);
|
|
await contains(".o-mail-Message", { count: 2 });
|
|
await contains(".o-mail-Thread-newMessage hr + span", { text: "New messages" });
|
|
await contains(".o-mail-Thread-newMessage ~ .o-mail-Message", { text: "hu" });
|
|
await focus(".o-mail-Composer-input");
|
|
await nextTick();
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
});
|
|
|
|
QUnit.test("no new messages separator on posting message (no message history)", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_member_ids: [
|
|
Command.create({ message_unread_counter: 0, partner_id: pyEnv.currentPartnerId }),
|
|
],
|
|
channel_type: "channel",
|
|
name: "General",
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Composer-input");
|
|
await contains(".o-mail-Message", { count: 0 });
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
|
|
await insertText(".o-mail-Composer-input", "hey!");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message");
|
|
await contains(".o-mail-Thread-newMessage hr + span", { count: 0, text: "New messages" });
|
|
});
|
|
|
|
QUnit.test("Mention a partner with special character (e.g. apostrophe ')", async () => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({
|
|
email: "usatyi@example.com",
|
|
name: "Pynya's spokesman",
|
|
});
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
name: "test",
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "@");
|
|
await insertText(".o-mail-Composer-input", "Pyn");
|
|
await click(".o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "@Pynya's spokesman " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(
|
|
`.o-mail-Message-body .o_mail_redirect[data-oe-id="${partnerId}"][data-oe-model="res.partner"]`,
|
|
{ text: "@Pynya's spokesman" }
|
|
);
|
|
});
|
|
|
|
QUnit.test("mention 2 different partners that have the same name", async () => {
|
|
const pyEnv = await startServer();
|
|
const [partnerId_1, partnerId_2] = pyEnv["res.partner"].create([
|
|
{
|
|
email: "partner1@example.com",
|
|
name: "TestPartner",
|
|
},
|
|
{
|
|
email: "partner2@example.com",
|
|
name: "TestPartner",
|
|
},
|
|
]);
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
name: "test",
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId_1 }),
|
|
Command.create({ partner_id: partnerId_2 }),
|
|
],
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "@Te");
|
|
await click(":nth-child(1 of .o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "@TestPartner " });
|
|
await insertText(".o-mail-Composer-input", "@Te");
|
|
await click(":nth-child(2 of .o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "@TestPartner @TestPartner " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(
|
|
`.o-mail-Message-body .o_mail_redirect[data-oe-id="${partnerId_1}"][data-oe-model="res.partner"]`,
|
|
{ text: "@TestPartner" }
|
|
);
|
|
await contains(
|
|
`.o-mail-Message-body .o_mail_redirect[data-oe-id="${partnerId_2}"][data-oe-model="res.partner"]`,
|
|
{ text: "@TestPartner" }
|
|
);
|
|
});
|
|
|
|
QUnit.test("mention a channel on a second line when the first line contains #", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General good" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "#blabla\n#");
|
|
await click(".o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "#blabla\n#General good " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message-body .o_channel_redirect", { text: "#General good" });
|
|
});
|
|
|
|
QUnit.test(
|
|
"mention a channel when replacing the space after the mention by another char",
|
|
async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General good" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "#");
|
|
await click(".o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "#General good " });
|
|
const text = $(".o-mail-Composer-input").val();
|
|
$(".o-mail-Composer-input").val(text.slice(0, -1));
|
|
await insertText(".o-mail-Composer-input", ", test");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message-body .o_channel_redirect", { text: "#General good" });
|
|
}
|
|
);
|
|
|
|
QUnit.test("mention 2 different channels that have the same name", async () => {
|
|
const pyEnv = await startServer();
|
|
const [channelId_1, channelId_2] = pyEnv["discuss.channel"].create([
|
|
{
|
|
channel_type: "channel",
|
|
group_public_id: false,
|
|
name: "my channel",
|
|
},
|
|
{
|
|
channel_type: "channel",
|
|
name: "my channel",
|
|
},
|
|
]);
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId_1);
|
|
await insertText(".o-mail-Composer-input", "#m");
|
|
await click(":nth-child(1 of .o-mail-Composer-suggestion)");
|
|
await contains(".o-mail-Composer-input", { value: "#my channel " });
|
|
await insertText(".o-mail-Composer-input", "#m");
|
|
await click(":nth-child(2 of .o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "#my channel #my channel " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(
|
|
`.o-mail-Message-body .o_channel_redirect[data-oe-id="${channelId_1}"][data-oe-model="discuss.channel"]`,
|
|
{ text: "#my channel" }
|
|
);
|
|
await contains(
|
|
`.o-mail-Message-body .o_channel_redirect[data-oe-id="${channelId_2}"][data-oe-model="discuss.channel"]`,
|
|
{ text: "#my channel" }
|
|
);
|
|
});
|
|
|
|
QUnit.test(
|
|
"Post a message containing an email address followed by a mention on another line",
|
|
async () => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({
|
|
email: "testpartner@odoo.com",
|
|
name: "TestPartner",
|
|
});
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
name: "test",
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "email@odoo.com\n@Te");
|
|
await click(".o-mail-Composer-suggestion");
|
|
await contains(".o-mail-Composer-input", { value: "email@odoo.com\n@TestPartner " });
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(
|
|
`.o-mail-Message-body .o_mail_redirect[data-oe-id="${partnerId}"][data-oe-model="res.partner"]`,
|
|
{ text: "@TestPartner" }
|
|
);
|
|
}
|
|
);
|
|
|
|
QUnit.test("basic rendering of canceled notification", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "test" });
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Someone" });
|
|
const messageId = pyEnv["mail.message"].create({
|
|
body: "not empty",
|
|
message_type: "email",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
pyEnv["mail.notification"].create({
|
|
failure_type: "SMTP",
|
|
mail_message_id: messageId,
|
|
notification_status: "canceled",
|
|
notification_type: "email",
|
|
res_partner_id: partnerId,
|
|
});
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Message-notification .fa-envelope-o");
|
|
|
|
await click(".o-mail-Message-notification");
|
|
await contains(".o-mail-MessageNotificationPopover");
|
|
await contains(".o-mail-MessageNotificationPopover .fa-trash-o");
|
|
await contains(".o-mail-MessageNotificationPopover", { text: "Someone" });
|
|
});
|
|
|
|
QUnit.test(
|
|
"first unseen message should be directly preceded by the new message separator if there is a transient message just before it while composer is not focused [REQUIRE FOCUS]",
|
|
async () => {
|
|
// The goal of removing the focus is to ensure the thread is not marked as seen automatically.
|
|
// Indeed that would trigger set_last_seen_message no matter what, which is already covered by other tests.
|
|
// The goal of this test is to cover the conditions specific to transient messages,
|
|
// and the conditions from focus would otherwise shadow them.
|
|
const pyEnv = await startServer();
|
|
// Needed partner & user to allow simulation of message reception
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Foreigner partner" });
|
|
const userId = pyEnv["res.users"].create({
|
|
name: "Foreigner user",
|
|
partner_id: partnerId,
|
|
});
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_type: "channel",
|
|
name: "General",
|
|
channel_member_ids: [
|
|
Command.create({ partner_id: partnerId }),
|
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
|
],
|
|
});
|
|
pyEnv["mail.message"].create([
|
|
{
|
|
body: "not empty",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
},
|
|
]);
|
|
const { openDiscuss, env } = await start();
|
|
openDiscuss(channelId);
|
|
// send a command that leads to receiving a transient message
|
|
await insertText(".o-mail-Composer-input", "/who");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message", { count: 2 });
|
|
// composer is focused by default, we remove that focus
|
|
$(".o-mail-Composer-input")[0].blur();
|
|
// simulate receiving a message
|
|
pyEnv.withUser(userId, () =>
|
|
env.services.rpc("/mail/message/post", {
|
|
post_data: { body: "test", message_type: "comment" },
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
})
|
|
);
|
|
await contains(".o-mail-Message", { count: 3 });
|
|
await contains(".o-mail-Thread-newMessage hr + span", { text: "New messages" });
|
|
await contains(".o-mail-Message[aria-label='Note'] + .o-mail-Thread-newMessage");
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"composer should be focused automatically after clicking on the send button",
|
|
async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "test" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "Dummy Message");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Composer-input:focus");
|
|
}
|
|
);
|
|
|
|
QUnit.test("chat window header should not have unread counter for non-channel thread", async () => {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({ name: "test" });
|
|
const messageId = pyEnv["mail.message"].create({
|
|
author_id: partnerId,
|
|
body: "not empty",
|
|
model: "res.partner",
|
|
needaction: true,
|
|
needaction_partner_ids: [pyEnv.currentPartnerId],
|
|
res_id: partnerId,
|
|
});
|
|
pyEnv["mail.notification"].create({
|
|
mail_message_id: messageId,
|
|
notification_status: "sent",
|
|
notification_type: "inbox",
|
|
res_partner_id: pyEnv.currentPartnerId,
|
|
});
|
|
await start();
|
|
await click(".o_menu_systray i[aria-label='Messages']");
|
|
await click(".o-mail-NotificationItem");
|
|
await contains(".o-mail-ChatWindow-counter", { count: 0, text: "1" });
|
|
});
|
|
|
|
QUnit.test(
|
|
"[technical] opening a non-channel chat window should not call channel_fold",
|
|
async (assert) => {
|
|
// channel_fold should not be called when opening non-channels in chat
|
|
// window, because there is no server sync of fold state for them.
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({ name: "test" });
|
|
const messageId = pyEnv["mail.message"].create({
|
|
author_id: partnerId,
|
|
body: "not empty",
|
|
model: "res.partner",
|
|
needaction: true,
|
|
needaction_partner_ids: [pyEnv.currentPartnerId],
|
|
res_id: partnerId,
|
|
});
|
|
pyEnv["mail.notification"].create({
|
|
mail_message_id: messageId,
|
|
notification_status: "sent",
|
|
notification_type: "inbox",
|
|
res_partner_id: pyEnv.currentPartnerId,
|
|
});
|
|
await start({
|
|
async mockRPC(route, args) {
|
|
if (route.includes("channel_fold")) {
|
|
const message =
|
|
"should not call channel_fold when opening a non-channel chat window";
|
|
assert.step(message);
|
|
console.error(message);
|
|
throw Error(message);
|
|
}
|
|
},
|
|
});
|
|
await click(".o_menu_systray i[aria-label='Messages']");
|
|
await contains(".o-mail-NotificationItem");
|
|
await contains(".o-mail-ChatWindow", { count: 0 });
|
|
|
|
await click(".o-mail-NotificationItem");
|
|
await contains(".o-mail-ChatWindow");
|
|
}
|
|
);
|
|
|
|
QUnit.test("Thread messages are only loaded once", async (assert) => {
|
|
const pyEnv = await startServer();
|
|
const channelIds = pyEnv["discuss.channel"].create([{ name: "General" }, { name: "Sales" }]);
|
|
const { openDiscuss } = await start({
|
|
mockRPC(route, args, originalRPC) {
|
|
if (route === "/discuss/channel/messages") {
|
|
assert.step(`load messages - ${args["channel_id"]}`);
|
|
}
|
|
return originalRPC(route, args);
|
|
},
|
|
});
|
|
pyEnv["mail.message"].create([
|
|
{
|
|
model: "discuss.channel",
|
|
res_id: channelIds[0],
|
|
body: "Message on channel1",
|
|
},
|
|
{
|
|
model: "discuss.channel",
|
|
res_id: channelIds[1],
|
|
body: "Message on channel2",
|
|
},
|
|
]);
|
|
openDiscuss();
|
|
await click(":nth-child(1 of .o-mail-DiscussSidebarChannel");
|
|
await contains(".o-mail-Message-content", { text: "Message on channel1" });
|
|
await click(":nth-child(2 of .o-mail-DiscussSidebarChannel)");
|
|
await contains(".o-mail-Message-content", { text: "Message on channel2" });
|
|
await click(":nth-child(1 of .o-mail-DiscussSidebarChannel)");
|
|
await contains(".o-mail-Message-content", { text: "Message on channel1" });
|
|
assert.verifySteps([`load messages - ${channelIds[0]}`, `load messages - ${channelIds[1]}`]);
|
|
});
|
|
|
|
QUnit.test(
|
|
"Opening thread with needaction messages should mark all messages of thread as read",
|
|
async (assert) => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Demo" });
|
|
const { env, openDiscuss } = await start({
|
|
mockRPC(route, args) {
|
|
if (args.model === "mail.message" && args.method === "mark_all_as_read") {
|
|
assert.step("mark-all-messages-as-read");
|
|
assert.deepEqual(args.args[0], [
|
|
["model", "=", "discuss.channel"],
|
|
["res_id", "=", channelId],
|
|
]);
|
|
}
|
|
},
|
|
});
|
|
openDiscuss(channelId);
|
|
await contains(".o-mail-Composer-input");
|
|
await triggerEvents(".o-mail-Composer-input", ["blur", "focusout"]);
|
|
await click("button", { text: "Inbox" });
|
|
await contains("h4", { text: "Congratulations, your inbox is empty" });
|
|
const messageId = pyEnv["mail.message"].create({
|
|
author_id: partnerId,
|
|
body: "@Mitchel Admin",
|
|
needaction: true,
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
needaction_partner_ids: [pyEnv.currentPartnerId],
|
|
});
|
|
pyEnv["mail.notification"].create({
|
|
mail_message_id: messageId,
|
|
notification_status: "sent",
|
|
notification_type: "inbox",
|
|
res_partner_id: pyEnv.currentPartnerId,
|
|
});
|
|
// simulate receiving a new needaction message
|
|
const [formattedMessage] = await env.services.orm.call("mail.message", "message_format", [
|
|
[messageId],
|
|
]);
|
|
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "mail.message/inbox", formattedMessage);
|
|
await contains("button", { text: "Inbox", contains: [".badge", { text: "1" }] });
|
|
await click("button", { text: "General" });
|
|
await contains(".o-discuss-badge", { count: 0 });
|
|
await contains("button", { text: "Inbox", contains: [".badge", { count: 0 }] });
|
|
assert.verifySteps(["mark-all-messages-as-read"]);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"[technical] Opening thread without needaction messages should not mark all messages of thread as read",
|
|
async (assert) => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
|
const { env, openDiscuss } = await start({
|
|
mockRPC(route, args) {
|
|
if (args.model === "mail.message" && args.method === "mark_all_as_read") {
|
|
assert.step("mark-all-messages-as-read");
|
|
}
|
|
},
|
|
});
|
|
openDiscuss(channelId);
|
|
await click("button", { text: "Inbox" });
|
|
await env.services.rpc("/mail/message/post", {
|
|
post_data: {
|
|
body: "Hello world!",
|
|
attachment_ids: [],
|
|
},
|
|
thread_id: channelId,
|
|
thread_model: "discuss.channel",
|
|
});
|
|
await click("button", { text: "General" });
|
|
await nextTick();
|
|
assert.verifySteps([]);
|
|
}
|
|
);
|
|
|
|
QUnit.test("can be marked as read while loading", async function () {
|
|
const pyEnv = await startServer();
|
|
const partnerId = pyEnv["res.partner"].create({ name: "Demo" });
|
|
const channelId = pyEnv["discuss.channel"].create({
|
|
channel_member_ids: [
|
|
Command.create({ message_unread_counter: 1, partner_id: pyEnv.currentPartnerId }),
|
|
Command.create({ partner_id: partnerId }),
|
|
],
|
|
channel_type: "chat",
|
|
});
|
|
pyEnv["mail.message"].create({
|
|
author_id: partnerId,
|
|
body: "<p>Test</p>",
|
|
model: "discuss.channel",
|
|
res_id: channelId,
|
|
});
|
|
const loadDeferred = makeDeferred();
|
|
const { openDiscuss } = await start({
|
|
async mockRPC(route) {
|
|
if (route === "/discuss/channel/messages") {
|
|
await loadDeferred;
|
|
}
|
|
},
|
|
});
|
|
openDiscuss(undefined);
|
|
await contains(".o-discuss-badge", { text: "1" });
|
|
await click(".o-mail-DiscussSidebarChannel", { text: "Demo" });
|
|
loadDeferred.resolve();
|
|
await contains(".o-discuss-badge", { count: 0 });
|
|
});
|
|
|
|
QUnit.test("New message separator not appearing after showing composer on thread", async () => {
|
|
const pyEnv = await startServer();
|
|
pyEnv["mail.message"].create([
|
|
{
|
|
model: "res.partner",
|
|
res_id: pyEnv.currentPartnerId,
|
|
body: "Message on partner",
|
|
},
|
|
{
|
|
model: "res.partner",
|
|
res_id: pyEnv.currentPartnerId,
|
|
body: "Message on partner",
|
|
},
|
|
]);
|
|
const { openFormView } = await start();
|
|
openFormView("res.partner", pyEnv.currentPartnerId);
|
|
await contains("button", { text: "Log note" });
|
|
await contains(".o-mail-Thread-newMessage", { count: 0 });
|
|
await click("button", { text: "Log note" });
|
|
await contains(".o-mail-Thread-newMessage", { count: 0 });
|
|
});
|
|
|
|
QUnit.test("Transient messages are added at the end of the thread", async () => {
|
|
const pyEnv = await startServer();
|
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
|
const { openDiscuss } = await start();
|
|
openDiscuss(channelId);
|
|
await insertText(".o-mail-Composer-input", "Dummy Message");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message");
|
|
await insertText(".o-mail-Composer-input", "/help");
|
|
await click(".o-mail-Composer-send:enabled");
|
|
await contains(".o-mail-Message", { count: 2 });
|
|
await contains(":nth-child(1 of .o-mail-Message)", { text: "Mitchell Admin" });
|
|
await contains(":nth-child(2 of .o-mail-Message)", { text: "OdooBot" });
|
|
});
|