import { SIZES, assertSteps, click, contains, defineMailModels, hover, insertText, onRpcBefore, openDiscuss, openFormView, patchUiSize, start, startServer, step, triggerHotkey, } from "@mail/../tests/mail_test_helpers"; import { describe, expect, test } from "@odoo/hoot"; import { leave, press, queryFirst } from "@odoo/hoot-dom"; import { Deferred, mockDate, tick } from "@odoo/hoot-mock"; import { Command, mockService, onRpc, patchWithCleanup, serverState, withUser, } from "@web/../tests/web_test_helpers"; import { browser } from "@web/core/browser/browser"; import { deserializeDateTime } from "@web/core/l10n/dates"; import { getOrigin, url } from "@web/core/utils/urls"; const { DateTime } = luxon; describe.current.tags("desktop"); defineMailModels(); test("Start edition on click edit", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains(".o-mail-Message .o-mail-Composer-input", { value: "Hello world" }); await click("a[role='button']", { text: "cancel" }); await contains(".o-mail-Message .o-mail-Composer-input", { count: 0 }); }); test("Edit message (mobile)", async () => { patchUiSize({ size: SIZES.SM }); const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message"); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains(".o-mail-Message.o-editing .o-mail-Composer-input", { value: "Hello world" }); await click("button", { text: "Discard editing" }); await contains(".o-mail-Message.o-editing .o-mail-Composer", { count: 0 }); await contains(".o-mail-Message-content", { text: "Hello world" }); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", "edited message", { replace: true }); await click(".o-mail-Message .fa-paper-plane-o"); await contains(".o-mail-Message-content", { text: "edited message (edited)" }); }); test("Editing message keeps the mentioned channels", async () => { const pyEnv = await startServer(); const channelId1 = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["discuss.channel"].create({ name: "other", channel_type: "channel", }); await start(); await openDiscuss(channelId1); await insertText(".o-mail-Composer-input", "#"); await click(".o-mail-Composer-suggestion strong", { text: "other" }); await click(".o-mail-Composer-send:enabled"); await contains(".o_channel_redirect", { count: 1, text: "other" }); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains(".o-mail-Message .o-mail-Composer-input", { value: "#other" }); await insertText(".o-mail-Message .o-mail-Composer-input", "#other bye", { replace: true }); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message-content", { text: "other bye (edited)" }); await click(".o_channel_redirect", { text: "other" }); await contains(".o-mail-Discuss-threadName", { value: "other" }); }); test("Can edit message comment in chatter", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "TestPartner" }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "original message", message_type: "comment", model: "res.partner", res_id: partnerId, }); await start(); await openFormView("res.partner", partnerId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", "edited message", { replace: true }); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message-content", { text: "edited message (edited)" }); }); test("Can edit message comment in chatter (mobile)", async () => { patchUiSize({ size: SIZES.SM }); const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "TestPartner" }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "original message", message_type: "comment", model: "res.partner", res_id: partnerId, }); await start(); await openFormView("res.partner", partnerId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains("button", { text: "Discard editing" }); await insertText(".o-mail-Message .o-mail-Composer-input", "edited message", { replace: true }); await click("button[aria-label='Save editing']"); await contains(".o-mail-Message-content", { text: "edited message (edited)" }); }); test("Cursor is at end of composer input on edit", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "", }); pyEnv["mail.message"].create({ body: "sattva", res_id: channelId, model: "discuss.channel", message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); const textarea = queryFirst(".o-mail-Composer-input"); const contentLength = textarea.value.length; expect(textarea.selectionStart).toBe(contentLength); expect(textarea.selectionEnd).toBe(contentLength); }); test("Stop edition on click cancel", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await click(".o-mail-Message a", { text: "cancel" }); await contains(".o-mail-Message.o-editing .o-mail-Composer", { count: 0 }); }); test("Stop edition on press escape", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); triggerHotkey("Escape", false); await contains(".o-mail-Message.o-editing .o-mail-Composer", { count: 0 }); }); test("Stop edition on click save", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message.o-editing .o-mail-Composer", { count: 0 }); }); test("Stop edition on press enter", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); triggerHotkey("Enter", false); await contains(".o-mail-Message.o-editing .o-mail-Composer", { count: 0 }); }); test("Do not stop edition on click away when clicking on emoji", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await click(".o-mail-Composer button[aria-label='Emojis']"); await click(".o-EmojiPicker-content :nth-child(1 of .o-Emoji)"); await contains(".o-mail-Message.o-editing .o-mail-Composer"); }); test("Edit and click save", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", "Goodbye World", { replace: true }); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message-body", { text: "Goodbye World (edited)" }); }); test("Do not call server on save if no changes", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); onRpcBefore("/mail/message/update_content", () => step("update_content")); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await click(".o-mail-Message a", { text: "save" }); await assertSteps([]); }); test("Update the link previews when a message is edited", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); onRpcBefore("/mail/link_preview", (args) => step("link_preview")); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", "http://odoo.com", { replace: true, }); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message-body", { text: "http://odoo.com" }); await assertSteps(["link_preview"]); }); test("Scroll bar to the top when edit starts", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world!".repeat(1000), model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains(".o-mail-Message .o-mail-Composer-input"); const textarea = document.querySelector(".o-mail-Message .o-mail-Composer-input"); expect(textarea.scrollHeight).toBeGreaterThan(textarea.clientHeight); await contains(".o-mail-Message .o-mail-Composer-input", { scroll: 0 }); }); test("mentions are kept when editing message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello @Mitchell Admin", model: "discuss.channel", partner_ids: [serverState.partnerId], res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", "Hi @Mitchell Admin", { replace: true, }); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message", { text: "Hi @Mitchell Admin (edited)", contains: ["a.o_mail_redirect", { text: "@Mitchell Admin" }], }); }); test("can add new mentions when editing message", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ email: "testpartner@odoo.com", name: "TestPartner", }); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_member_ids: [ Command.create({ partner_id: serverState.partnerId }), Command.create({ partner_id: partnerId }), ], channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello", model: "discuss.channel", partner_ids: [serverState.partnerId], res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", " @"); await click(".o-mail-Composer-suggestion strong", { text: "TestPartner" }); await contains(".o-mail-Composer-input", { value: "Hello @TestPartner " }); await click(".o-mail-Message a", { text: "save" }); await contains(".o-mail-Message", { text: "Hello @TestPartner (edited)", contains: ["a.o_mail_redirect", { text: "@TestPartner" }], }); }); test("Other messages are grayed out when replying to another one", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create([ { body: "Hello world", res_id: channelId, model: "discuss.channel" }, { body: "Goodbye world", res_id: channelId, model: "discuss.channel" }, ]); await start(); await openDiscuss(channelId); await contains(".o-mail-Message", { count: 2 }); await click(".o-mail-Message [title='Reply']", { parent: [".o-mail-Message", { text: "Hello world" }], }); await contains(".o-mail-Message.opacity-50", { text: "Goodbye world" }); await contains(".o-mail-Message:not(.opacity_50)", { text: "Hello world" }); }); test("Parent message body is displayed on replies", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Reply']"); await insertText(".o-mail-Composer-input", "FooBarFoo"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-MessageInReply-message", { text: "Hello world" }); }); test("Updating the parent message of a reply also updates the visual of the reply", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); const messageId = pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", parent_id: messageId, }); await start(); await openDiscuss(channelId); await click(":nth-child(1 of .o-mail-Message) [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message .o-mail-Composer-input", "Goodbye World", { replace: true, }); triggerHotkey("Enter"); await contains(".o-mail-MessageInReply-message", { text: "Goodbye World (edited)" }); }); test("Deleting parent message of a reply should adapt reply visual", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Reply']"); await insertText(".o-mail-Composer-input", "FooBarFoo"); triggerHotkey("Enter", false); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Delete']"); await click("button", { text: "Confirm" }); await contains(".o-mail-MessageInReply", { text: "Original message was deleted" }); }); test("Can open emoji picker after edit mode", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await click(".o-mail-Message a", { text: "save" }); await click("[title='Add a Reaction']"); await contains(".o-EmojiPicker"); }); test("Can add a reaction", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); await click(".o-Emoji", { text: "😅" }); await contains(".o-mail-MessageReaction", { text: "😅1" }); }); test("Can remove a reaction", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); await click(".o-Emoji", { text: "😅" }); await click(".o-mail-MessageReaction"); await contains(".o-mail-MessageReaction", { count: 0 }); }); test("Two users reacting with the same emoji", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); const messageId = pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); pyEnv["mail.message.reaction"].create([ { message_id: messageId, content: "😅", partner_id: serverState.partnerId, }, { message_id: messageId, content: "😅", partner_id: partnerId, }, ]); await start(); await openDiscuss(channelId); await click(".o-mail-MessageReaction", { text: "😅2" }); await click(".o-mail-MessageReaction", { text: "😅1" }); await contains(".o-mail-MessageReaction", { text: "😅2" }); }); test("Can quickly add a reaction", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); await click(".o-Emoji", { text: "😅" }); await contains(".o-mail-MessageReaction", { text: "😅1" }); await hover(".o-mail-MessageReactions"); await click("button[title='Add Reaction']"); await click(".o-Emoji", { text: "😏" }); await contains(".o-mail-MessageReaction", { text: "😏1" }); }); test("Reaction summary", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message", { text: "Hello world" }); const partnerNames = ["Foo", "Bar", "FooBar", "Bob", "Baz"]; const expectedSummaries = [ "😅:sweat_smile: reacted by Foo", "😅:sweat_smile: reacted by Foo and Bar", "😅:sweat_smile: reacted by Foo, Bar, and FooBar", "😅:sweat_smile: reacted by Foo, Bar, FooBar, and 1 other", "😅:sweat_smile: reacted by Foo, Bar, FooBar, and 2 others", ]; for (const [idx, name] of partnerNames.entries()) { const partner_id = pyEnv["res.partner"].create({ name }); const userId = pyEnv["res.users"].create({ partner_id }); pyEnv["res.partner"].create({ name, user_ids: [Command.link(userId)] }); await withUser(userId, async () => { await click("[title='Add a Reaction']"); await click(".o-Emoji", { after: ["span", { textContent: "Smileys & Emotion" }], text: "😅", }); await contains(".o-mail-MessageReaction", { text: `😅${idx + 1}` }); await hover(".o-mail-MessageReaction"); await contains(".o-mail-MessageReactionList-preview", { text: `${expectedSummaries[idx]}`, }); await leave(".o-mail-MessageReaction"); }); } }); test("Add the same reaction twice from the emoji picker", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); await click(".o-Emoji", { text: "😅" }); await click("[title='Add a Reaction']"); await click(".o-Emoji", { text: "😅" }); await contains(".o-mail-MessageReaction", { text: "😅1" }); }); test("basic rendering of message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general" }); const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); pyEnv["res.users"].create({ partner_id: partnerId, name: "Demo", }); pyEnv["mail.message"].create({ author_id: partnerId, body: "
body
", date: "2019-04-20 10:00:00", model: "discuss.channel", res_id: channelId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message"); await contains(".o-mail-Message .o-mail-Message-content", { text: "body" }); const partner = pyEnv["res.partner"].search_read([["id", "=", partnerId]])[0]; await contains( `.o-mail-Message .o-mail-Message-sidebar .o-mail-Message-avatarContainer img.cursor-pointer[data-src='${getOrigin()}/web/image/res.partner/${partnerId}/avatar_128?unique=${ deserializeDateTime(partner.write_date).ts }']` ); await contains(".o-mail-Message .o-mail-Message-header .o-mail-Message-author.cursor-pointer", { text: "Demo", }); await contains( `.o-mail-Message .o-mail-Message-header .o-mail-Message-date[title='${deserializeDateTime( "2019-04-20 10:00:00" ).toLocaleString(DateTime.DATETIME_SHORT_WITH_SECONDS)}']` ); }); test("should not be able to reply to temporary/transient messages", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general" }); await start(); await openDiscuss(channelId); // these user interactions is to forge a transient message response from channel command "/who" await insertText(".o-mail-Composer-input", "/who"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message [title='Reply']", { count: 0 }); }); test.skip("squashed transient message should not have date in the sidebar", async () => { // FIXME: mock timezone not working mockDate("2024-03-26 10:00:00", 0); const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "Channel 1" }); pyEnv["mail.message"].create([ { body: "Hello world 1", model: "discuss.channel", res_id: channelId, }, { body: "Hello world 2", model: "discuss.channel", res_id: channelId, }, ]); await start(); await openDiscuss(channelId); await click(".o-mail-Message.o-squashed"); await contains(".o-mail-Message.o-squashed .o-mail-Message-sidebar", { text: "11:00", // FIXME: should be 10:00 }); await insertText(".o-mail-Composer-input", "/who"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message", { text: "You are alone in this channel." }); await insertText(".o-mail-Composer-input", "/who"); await click(".o-mail-Composer-send:enabled"); await click(":nth-child(2 of .o-mail-Message.o-squashed"); await tick(); await contains(":nth-child(2 of .o-mail-Message.o-squashed) .o-mail-Message-sidebar", { text: "11:00", // FIXME: should be 10:00 count: 0, }); }); test("message comment of same author within 5min. should be squashed", async () => { mockDate("2024-03-26 10:00:00", 0); const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general" }); const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); pyEnv["mail.message"].create([ { author_id: partnerId, body: "body1
", date: "2019-04-20 10:00:00", message_type: "comment", model: "discuss.channel", res_id: channelId, }, { author_id: partnerId, body: "body2
", date: "2019-04-20 10:02:30", message_type: "comment", model: "discuss.channel", res_id: channelId, }, ]); await start(); await openDiscuss(channelId); await contains(".o-mail-Message", { count: 2 }); await contains(".o-mail-Message", { contains: [ [".o-mail-Message-content", { text: "body1" }], [".o-mail-Message-header"], [".o-mail-Message-sidebar", { contains: [".o-mail-Message-date", { count: 0 }] }], ], }); await contains(".o-mail-Message", { contains: [ [".o-mail-Message-content", { text: "body2" }], [".o-mail-Message-header", { count: 0 }], [".o-mail-Message-sidebar", { contains: [".o-mail-Message-date", { count: 0 }] }], ], }); await click(".o-mail-Message", { text: "body2" }); await contains(".o-mail-Message", { contains: [ [".o-mail-Message-content", { text: "body2" }], [".o-mail-Message-sidebar .o-mail-Message-date", { text: "10:02" }], ], }); }); test("open author avatar card", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Demo", email: "demo@example.com", phone: "+5646548", }); pyEnv["res.users"].create({ partner_id: partnerId, name: "Demo", }); window.pyEnv = pyEnv; const [channelId_1] = pyEnv["discuss.channel"].create([ { name: "General" }, { channel_member_ids: [ Command.create({ partner_id: serverState.partnerId }), Command.create({ partner_id: partnerId }), ], channel_type: "chat", }, ]); pyEnv["mail.message"].create({ author_id: partnerId, body: "not empty", model: "discuss.channel", res_id: channelId_1, }); await start(); await openDiscuss(channelId_1); await contains(".o-mail-DiscussSidebarChannel.o-active", { text: "General" }); await contains(".o-mail-Discuss-content .o-mail-Message-avatarContainer img"); await click(".o-mail-Discuss-content .o-mail-Message-avatarContainer img"); await contains(".o_avatar_card"); await contains(".o_card_user_infos > span", { text: "Demo" }); await contains(".o_card_user_infos > a", { text: "demo@example.com" }); await contains(".o_card_user_infos > a", { text: "+5646548" }); }); test("toggle_star message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general" }); const messageId = pyEnv["mail.message"].create({ body: "not empty", model: "discuss.channel", res_id: channelId, }); onRpc("mail.message", "toggle_message_starred", ({ args }) => { step("rpc:toggle_message_starred"); expect(args[0][0]).toBe(messageId); }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message"); await contains(".o-mail-Message [title='Mark as Todo']"); await contains(".o-mail-Message [title='Mark as Todo']" + " i.fa-star-o"); await contains("button", { text: "Starred", contains: [".badge", { count: 0 }] }); await click(".o-mail-Message [title='Mark as Todo']"); await contains("button", { text: "Starred", contains: [".badge", { text: "1" }] }); await assertSteps(["rpc:toggle_message_starred"]); await contains(".o-mail-Message"); await contains(".o-mail-Message [title='Mark as Todo']" + " i.fa-star"); await click(".o-mail-Message [title='Mark as Todo']"); await contains("button", { text: "Starred", contains: [".badge", { count: 0 }] }); await assertSteps(["rpc:toggle_message_starred"]); await contains(".o-mail-Message"); await contains(".o-mail-Message [title='Mark as Todo']" + " i.fa-star-o"); }); test("Name of message author is only displayed in chat window for partners others than the current user", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel" }); const partnerId = pyEnv["res.partner"].create({ name: "Not the current user" }); pyEnv["mail.message"].create([ { body: "not empty", model: "discuss.channel", res_id: channelId, }, { author_id: partnerId, body: "not empty", model: "discuss.channel", res_id: channelId, }, ]); await start(); await click(".o_menu_systray i[aria-label='Messages']"); await click(".o-mail-NotificationItem"); await contains(".o-mail-Message-author", { text: "Not the current user" }); }); test("Name of message author is not displayed in chat window for channel of type chat", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "chat" }); const partnerId = pyEnv["res.partner"].create({ name: "A" }); pyEnv["mail.message"].create([ { body: "not empty", model: "discuss.channel", res_id: channelId, }, { author_id: partnerId, body: "not empty", model: "discuss.channel", res_id: channelId, }, ]); await start(); await click(".o_menu_systray i[aria-label='Messages']"); await click(".o-mail-NotificationItem"); await contains(".o-mail-Message-author", { count: 0 }); }); test("click on message edit button should open edit composer", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); pyEnv["mail.message"].create({ body: "not empty", message_type: "comment", model: "discuss.channel", res_id: channelId, }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains(".o-mail-Message .o-mail-Composer"); }); test("Notification Sent", async () => { const pyEnv = await startServer(); const [threadId, partnerId] = pyEnv["res.partner"].create([ {}, { name: "Someone", partner_share: true }, ]); const messageId = pyEnv["mail.message"].create({ body: "not empty", message_type: "email", model: "res.partner", res_id: threadId, }); pyEnv["mail.notification"].create({ mail_message_id: messageId, notification_status: "sent", notification_type: "email", res_partner_id: partnerId, }); await start(); await openFormView("res.partner", threadId); await contains(".o-mail-Message"); await contains(".o-mail-Message-notification"); await contains(".o-mail-Message-notification i"); expect(".o-mail-Message-notification i:first").toHaveClass("fa-envelope-o"); await click(".o-mail-Message-notification"); await contains(".o-mail-MessageNotificationPopover"); await contains(".o-mail-MessageNotificationPopover i"); expect(".o-mail-MessageNotificationPopover i:first").toHaveClass("fa-check"); await contains(".o-mail-MessageNotificationPopover", { text: "Someone" }); }); test("Notification Error", async () => { const pyEnv = await startServer(); const [threadId, partnerId] = pyEnv["res.partner"].create([ {}, { name: "Someone", partner_share: true }, ]); const messageId = pyEnv["mail.message"].create({ body: "not empty", message_type: "email", model: "res.partner", res_id: threadId, }); pyEnv["mail.notification"].create({ mail_message_id: messageId, notification_status: "exception", notification_type: "email", res_partner_id: partnerId, }); const openResendActionDef = new Deferred(); mockService("action", { doAction(action, options) { if (action?.res_model === "res.partner") { return super.doAction(...arguments); } step("do_action"); expect(action).toBe("mail.mail_resend_message_action"); expect(options.additionalContext.mail_message_to_resend).toBe(messageId); openResendActionDef.resolve(); }, }); await start(); await openFormView("res.partner", threadId); await contains(".o-mail-Message"); await contains(".o-mail-Message-notification"); await contains(".o-mail-Message-notification i"); expect(".o-mail-Message-notification i:first").toHaveClass("fa-envelope"); await click(".o-mail-Message-notification").then(() => {}); await openResendActionDef; await assertSteps(["do_action"]); }); test('Quick edit (edit from Composer with ArrowUp) ignores empty ("deleted") messages.', async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "not empty", model: "discuss.channel", res_id: channelId, message_type: "comment", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "", // empty body model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message"); triggerHotkey("ArrowUp"); await contains(".o-mail-Message.o-editing"); await contains(".o-mail-Message .o-mail-Composer-input", { value: "not empty" }); }); test("Editing a message to clear its composer opens message delete dialog.", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "not empty", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message.o-editing .o-mail-Composer-input", "", { replace: true }); triggerHotkey("Enter"); await contains(".modal-body p", { text: "Are you sure you want to delete this message?" }); }); test("Clear message body should not open message delete dialog if it has attachments", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "not empty", model: "discuss.channel", res_id: channelId, message_type: "comment", attachment_ids: [ pyEnv["ir.attachment"].create({ name: "test.txt", mimetype: "text/plain" }), ], }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await insertText(".o-mail-Message.o-editing .o-mail-Composer-input", "", { replace: true }); triggerHotkey("Enter"); await contains(".o-mail-Message-textContent", { text: "" }); // weak test, no guarantee that we waited long enough for the potential dialog to show await contains(".modal-body p", { text: "Are you sure you want to delete this message?", count: 0, }); }); test("highlight the message mentioning the current user inside the channel", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ display_name: "Test Partner" }); pyEnv["res.users"].create({ partner_id: partnerId }); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "General", }); pyEnv["mail.message"].create({ author_id: partnerId, body: "hello @Admin", model: "discuss.channel", partner_ids: [serverState.partnerId], res_id: channelId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message-bubble.o-orange"); }); test("not highlighting the message if not mentioning the current user inside the channel", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ display_name: "testPartner", email: "testPartner@odoo.com", }); pyEnv["res.users"].create({ partner_id: partnerId }); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "General", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "hello @testPartner", model: "discuss.channel", partner_ids: [partnerId], res_id: channelId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message-bubble:not(.o-orange)"); }); test("allow attachment delete on authored message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "test" }); pyEnv["mail.message"].create({ attachment_ids: [ Command.create({ mimetype: "image/jpeg", name: "BLAH", res_id: channelId, res_model: "discuss.channel", }), ], author_id: serverState.partnerId, body: "Test
", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-AttachmentImage [title='Actions']"); await click(".dropdown-item", { text: "Remove" }); await contains(".modal-dialog .modal-body", { text: 'Do you really want to delete "BLAH"?' }); await click(".modal-footer .btn-primary"); await contains(".o-mail-AttachmentCard", { count: 0 }); }); test("prevent attachment delete on non-authored message in channels", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({}); const channelId = pyEnv["discuss.channel"].create({ name: "test" }); pyEnv["mail.message"].create({ attachment_ids: [ Command.create({ mimetype: "image/jpeg", name: "BLAH", res_id: channelId, res_model: "discuss.channel", }), ], author_id: partnerId, body: "Test
", model: "discuss.channel", res_id: channelId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-AttachmentImage"); await contains(".o-mail-AttachmentImage div[title='Remove']", { count: 0 }); }); test("Toggle star should update starred counter on all tabs", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "Hello world", model: "discuss.channel", res_id: channelId, message_type: "comment", }); const env1 = await start({ asTab: true }); const env2 = await start({ asTab: true }); await openDiscuss(channelId, { target: env1 }); await openDiscuss(undefined, { target: env2 }); await click(".o-mail-Message [title='Mark as Todo']", { target: env1 }); await contains("button", { target: env2, text: "Starred", contains: [".badge", { text: "1" }], }); }); test("allow attachment image download on message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "test" }); const attachmentId = pyEnv["ir.attachment"].create({ name: "Blah.jpg", mimetype: "image/jpeg", }); pyEnv["mail.message"].create({ attachment_ids: [attachmentId], body: "Test
", model: "discuss.channel", res_id: channelId, }); await start(); await openDiscuss(channelId); await click(".o-mail-AttachmentImage [title='Actions']"); await contains(".dropdown-item", { text: "Download" }); }); test("Can download all files of a message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "test" }); const [attachmentId_1, attachmentId_2, attachmentId_3] = pyEnv["ir.attachment"].create([ { name: "test.png", mimetype: "image/png" }, { name: "Blah.png", mimetype: "image/png" }, { name: "shut.png", mimetype: "image/png" }, ]); pyEnv["mail.message"].create([ { attachment_ids: [attachmentId_1, attachmentId_2], body: "Test
", model: "discuss.channel", res_id: channelId, message_type: "comment", }, { attachment_ids: [attachmentId_3], model: "discuss.channel", res_id: channelId, message_type: "comment", }, ]); await start(); await openDiscuss(channelId); await click(":nth-child(1 of .o-mail-Message) [title='Expand']"); await contains("[title='Download Files']"); await click(":nth-child(2 of .o-mail-Message) [title='Expand']"); await contains("[title='Download Files']", { count: 0 }); }); test("Can remove files of message individually", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "test" }); const [attachmentId_1, attachmentId_2, attachmentId_3, attachmentId_4] = pyEnv[ "ir.attachment" ].create([ { name: "attachment1.txt", mimetype: "text/plain" }, { name: "attachment2.txt", mimetype: "text/plain" }, { name: "attachment3.txt", mimetype: "text/plain" }, { name: "attachment4.txt", mimetype: "text/plain" }, ]); pyEnv["mail.message"].create([ { attachment_ids: [attachmentId_1, attachmentId_2], model: "discuss.channel", res_id: channelId, message_type: "comment", }, { attachment_ids: [attachmentId_3], model: "discuss.channel", res_id: channelId, message_type: "comment", }, { attachment_ids: [attachmentId_4], body: "Test
", model: "discuss.channel", res_id: channelId, message_type: "comment", }, ]); await start(); await openDiscuss(channelId); await contains( ":nth-child(1 of .o-mail-Message) :nth-child(1 of .o-mail-AttachmentCard) [title='Remove']" ); await contains( ":nth-child(1 of .o-mail-Message) :nth-child(2 of .o-mail-AttachmentCard) [title='Remove']" ); await contains(":nth-child(2 of .o-mail-Message) .o-mail-AttachmentCard [title='Remove']", { count: 0, }); await contains(":nth-child(3 of .o-mail-Message) .o-mail-AttachmentCard [title='Remove']"); }); test("avatar card from author should be opened after clicking on their avatar", async () => { const pyEnv = await startServer(); const [partnerId_1, partnerId_2] = pyEnv["res.partner"].create([ { name: "Partner_1" }, { name: "Partner_2", email: "partner2@mail.com", phone: "+15968415" }, ]); pyEnv["res.users"].create({ partner_id: partnerId_2, name: "Partner_2", }); pyEnv["mail.message"].create({ author_id: partnerId_2, body: "not empty", model: "res.partner", res_id: partnerId_1, }); await start(); await openFormView("res.partner", partnerId_1); await contains(".o-mail-Message-avatar"); expect(".o-mail-Message-avatarContainer:first").toHaveClass("cursor-pointer"); await click(".o-mail-Message-avatar"); await contains(".o_avatar_card"); await contains(".o_card_user_infos > span", { text: "Partner_2" }); await contains(".o_card_user_infos > a", { text: "partner2@mail.com" }); await contains(".o_card_user_infos > a", { text: "+15968415" }); }); test("avatar card from author should be opened after clicking on their name", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Demo", email: "demo@example.com", phone: "+5646548", }); pyEnv["res.users"].create({ partner_id: partnerId, name: "Demo", }); pyEnv["mail.message"].create({ author_id: partnerId, body: "not empty", model: "res.partner", res_id: partnerId, }); await start(); await openFormView("res.partner", partnerId); await click(".o-mail-Message-author", { text: "Demo" }); await contains(".o_avatar_card"); await contains(".o_card_user_infos > span", { text: "Demo" }); await contains(".o_card_user_infos > a", { text: "demo@example.com" }); await contains(".o_card_user_infos > a", { text: "+5646548" }); }); test("subtype description should be displayed if it is different than body", async () => { const pyEnv = await startServer(); const threadId = pyEnv["res.partner"].create({}); const subtypeId = pyEnv["mail.message.subtype"].create({ description: "Bonjour" }); pyEnv["mail.message"].create({ body: "Hello
", model: "res.partner", res_id: threadId, subtype_id: subtypeId, }); await start(); await openFormView("res.partner", threadId); await contains(".o-mail-Message-body", { text: "HelloBonjour" }); }); test("subtype description should not be displayed if it is similar to body", async () => { const pyEnv = await startServer(); const threadId = pyEnv["res.partner"].create({}); const subtypeId = pyEnv["mail.message.subtype"].create({ description: "hello" }); pyEnv["mail.message"].create({ body: "Hello
", model: "res.partner", res_id: threadId, subtype_id: subtypeId, }); await start(); await openFormView("res.partner", threadId); await contains(".o-mail-Message-body", { text: "Hello" }); }); test("data-oe-id & data-oe-model link redirection on click", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({}); pyEnv["mail.message"].create({ body: '', model: "res.partner", res_id: partnerId, }); mockService("action", { doAction(action) { if (action?.res_model === "res.partner") { return super.doAction(...arguments); } expect(action.type).toBe("ir.actions.act_window"); expect(action.res_model).toBe("some.model"); expect(action.res_id).toBe(250); step("do-action:openFormView_some.model_250"); }, }); await start(); await openFormView("res.partner", partnerId); await click(".o-mail-Message-body a"); await assertSteps(["do-action:openFormView_some.model_250"]); }); test("Chat with partner should be opened after clicking on their mention", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Test Partner", email: "testpartner@odoo.com", }); pyEnv["res.users"].create({ partner_id: partnerId }); await start(); await openFormView("res.partner", partnerId); await click("button", { text: "Send message" }); await insertText(".o-mail-Composer-input", "@Te"); await click(".o-mail-Composer-suggestion strong", { text: "Test Partner" }); await contains(".o-mail-Composer-input", { value: "@Test Partner " }); await click(".o-mail-Composer-send:enabled"); await click(".o_mail_redirect"); await contains(".o-mail-ChatWindow .o-mail-Thread"); await contains(".o-mail-ChatWindow", { text: "Test Partner" }); }); test("Channel should be opened after clicking on its mention", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({}); pyEnv["discuss.channel"].create({ name: "my-channel" }); await start(); await openFormView("res.partner", partnerId); await click("button", { text: "Send message" }); await insertText(".o-mail-Composer-input", "#"); await click(".o-mail-Composer-suggestion strong", { text: "my-channel" }); await click(".o-mail-Composer-send:enabled"); await click(".o_channel_redirect"); await contains(".o-mail-ChatWindow .o-mail-Thread"); await contains(".o-mail-ChatWindow", { text: "my-channel" }); }); test("delete all attachments of message without content should no longer display the message", async () => { const pyEnv = await startServer(); const attachmentId = pyEnv["ir.attachment"].create({ mimetype: "text/plain", name: "Blah.txt", }); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); pyEnv["mail.message"].create({ attachment_ids: [attachmentId], message_type: "comment", model: "discuss.channel", res_id: channelId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message"); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Delete']"); await click("button", { text: "Confirm" }); await contains(".o-mail-Message", { count: 0 }); }); test("delete all attachments of a message with some text content should still keep it displayed", async () => { const pyEnv = await startServer(); const attachmentId = pyEnv["ir.attachment"].create({ mimetype: "text/plain", name: "Blah.txt", }); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); pyEnv["mail.message"].create({ attachment_ids: [attachmentId], body: "Some content", message_type: "comment", model: "discuss.channel", res_id: channelId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message"); await click(".o-mail-AttachmentCard button[title='Remove']"); await click(".modal button", { text: "Ok" }); await contains(".o-mail-Message"); }); test("message with subtype should be displayed (and not considered as empty)", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); const subtypeId = pyEnv["mail.message.subtype"].create({ description: "Task created" }); pyEnv["mail.message"].create({ model: "discuss.channel", res_id: channelId, subtype_id: subtypeId, }); await start(); await openDiscuss(channelId); await contains(".o-mail-Message-content", { text: "Task created" }); }); test("message considered empty", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); pyEnv["mail.message"].create([ { model: "discuss.channel", res_id: channelId, }, { body: "", model: "discuss.channel", res_id: channelId, }, { body: "", model: "discuss.channel", res_id: channelId, }, { body: "\n
", model: "discuss.channel", res_id: channelId, }, { body: "\r\n\r\n
", model: "discuss.channel", res_id: channelId, }, { body: "", model: "discuss.channel", res_id: channelId, }, { body: '', model: "discuss.channel", res_id: channelId, }, ]); await start(); await openDiscuss(channelId); await contains(".o-mail-Thread", { text: "The conversation is empty." }); await contains(".o-mail-Message", { count: 0 }); }); test("message with html not to be considered empty", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); pyEnv["mail.message"].create({ body: "
Want to know features and benefits of using the new software.
", email_from: "md@oilcompany.fr", message_type: "email", subject: "Need Details", model: "res.partner", res_id: serverState.partnerId, author_id: null, }); await start(); await openFormView("res.partner", serverState.partnerId); await contains(".o-mail-Message-avatar[data-src*='mail/static/src/img/email_icon.png']"); }); test("Show email_from of message without author for email message", async () => { const pyEnv = await startServer(); pyEnv["mail.message"].create({ author_id: null, body: "Want to know features and benefits of using the new software.
", email_from: "md@oilcompany.fr", message_type: "email", subject: "Need Details", model: "res.partner", res_id: serverState.partnerId, }); await start(); await openFormView("res.partner", serverState.partnerId); await contains(".o-mail-Message-author", { text: "md@oilcompany.fr" }); }); test("Avatar of unknown author for not email message", async () => { const pyEnv = await startServer(); pyEnv["mail.message"].create({ body: "Want to know features and benefits of using the new software.
", email_from: "md@oilcompany.fr", message_type: "comment", subject: "Need Details", model: "res.partner", res_id: serverState.partnerId, author_id: null, }); await start(); await openFormView("res.partner", serverState.partnerId); await contains(".o-mail-Message-avatar[data-src*='/mail/static/src/img/smiley/avatar.jpg']"); }); test("Show email_from of message without author for not email message", async () => { const pyEnv = await startServer(); pyEnv["mail.message"].create({ author_id: null, body: "Want to know features and benefits of using the new software.
", email_from: "md@oilcompany.fr", message_type: "comment", subject: "Need Details", model: "res.partner", res_id: serverState.partnerId, }); await start(); await openFormView("res.partner", serverState.partnerId); await contains(".o-mail-Message-author", { text: "md@oilcompany.fr" }); }); test("Message should display attachments in order", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); pyEnv["mail.message"].create({ author_id: serverState.partnerId, body: "not empty", model: "discuss.channel", res_id: channelId, message_type: "comment", attachment_ids: [ pyEnv["ir.attachment"].create({ name: "A.txt", mimetype: "text/plain" }), pyEnv["ir.attachment"].create({ name: "B.txt", mimetype: "text/plain" }), pyEnv["ir.attachment"].create({ name: "C.txt", mimetype: "text/plain" }), ], }); await start(); await openDiscuss(channelId); await contains(":nth-child(1 of .o-mail-AttachmentCard)", { text: "A.txt" }); await contains(":nth-child(2 of .o-mail-AttachmentCard)", { text: "B.txt" }); await contains(":nth-child(3 of .o-mail-AttachmentCard)", { text: "C.txt" }); }); test("Can edit a message only containing an attachment", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "general", channel_type: "channel", }); const attachmentId = pyEnv["ir.attachment"].create({ name: "Blah.jpg", mimetype: "image/jpeg", }); pyEnv["mail.message"].create({ attachment_ids: [attachmentId], author_id: serverState.partnerId, body: "", model: "discuss.channel", res_id: channelId, message_type: "comment", }); await start(); await openDiscuss(channelId); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Edit']"); await contains(".o-mail-Message.o-editing .o-mail-Composer-input"); }); test("Click on view reactions shows the reactions on the message", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }); await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); await click(".o-Emoji", { text: "😅" }); await contains(".o-mail-MessageReaction", { text: "😅1" }); await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message-moreMenu [title='View Reactions']"); await contains(".o-mail-MessageReactionMenu", { text: "😅1" }); }); test("Reactions are ordered by id", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); pyEnv["mail.message"].create({ body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", reaction_ids: [ pyEnv["mail.message.reaction"].create({ content: "🔰", partner_id: serverState.partnerId, }), pyEnv["mail.message.reaction"].create({ content: "🔢", partner_id: serverState.partnerId, }), ], }); await start(); await openDiscuss(channelId); await contains(".o-mail-MessageReaction:eq(0)", { text: "🔰" }); await contains(".o-mail-MessageReaction:eq(1)", { text: "🔢" }); }); test("discuss - bigger font size when there is only emoji", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ channel_type: "channel", name: "channel1", }); await start(); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "🥳"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message-body", { text: "🥳" }); await insertText(".o-mail-Composer-input", "not only emoji!! 😅"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message-body", { text: "not only emoji!! 😅" }); const [emojiMessage, textMessage] = document.querySelectorAll(".o-mail-Message-body"); expect( parseFloat(getComputedStyle(emojiMessage).getPropertyValue("font-size")) ).toBeGreaterThan(parseFloat(getComputedStyle(textMessage).getPropertyValue("font-size"))); }); test("chatter - font size unchanged when there is only emoji", async () => { await startServer(); await start(); await openFormView("res.partner", serverState.partnerId); await click(".o-mail-Chatter-sendMessage"); await insertText(".o-mail-Composer-input", "🥳"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message-body", { text: "🥳" }); await click(".o-mail-Chatter-sendMessage"); await insertText(".o-mail-Composer-input", "not only emoji!! 😅"); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message-body", { text: "not only emoji!! 😅" }); const [emojiMessage, textMessage] = document.querySelectorAll(".o-mail-Message-body"); expect(parseFloat(getComputedStyle(emojiMessage).getPropertyValue("font-size"))).toBe( parseFloat(getComputedStyle(textMessage).getPropertyValue("font-size")) ); }); test("Delete starred message decrements starred counter once", async () => { const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "General" }); pyEnv["mail.message"].create([ { author_id: serverState.partnerId, body: "delete me", message_type: "comment", model: "discuss.channel", res_id: channelId, starred_partner_ids: [serverState.partnerId], }, { author_id: serverState.partnerId, body: "Hello World!", message_type: "comment", model: "discuss.channel", res_id: channelId, starred_partner_ids: [serverState.partnerId], }, { author_id: serverState.partnerId, body: "test", message_type: "comment", model: "discuss.channel", res_id: channelId, starred_partner_ids: [serverState.partnerId], }, ]); await start(); await openDiscuss(channelId); await contains("button", { count: 1, text: "Starred3" }); await click(":nth-child(1 of .o-mail-Message) [title='Expand']"); await click(".o-mail-Message-moreMenu [title='Delete']"); await click("button", { text: "Confirm" }); await contains("button", { count: 1, text: "Starred2" }); }); test("Copy Message Link", async () => { patchWithCleanup(browser.navigator.clipboard, { writeText(text) { step(text); super.writeText(text); }, }); const pyEnv = await startServer(); const channelId = pyEnv["discuss.channel"].create({ name: "channel1" }); const [, messageId_2] = pyEnv["mail.message"].create([ { body: "Message without type", res_id: channelId, model: "discuss.channel", }, { body: "Hello world", res_id: channelId, message_type: "comment", model: "discuss.channel", }, ]); await start(); await openDiscuss(channelId); await click(".o-mail-Message:eq(0) [title='Expand']"); await contains("[title='Copy Link']", { count: 0 }); await click(".o-mail-Message:eq(1) [title='Expand']"); await click("[title='Copy Link']"); await assertSteps([url(`/mail/message/${messageId_2}`)]); await press(["ctrl", "v"]); await click(".o-mail-Composer-send:enabled"); await contains(".o-mail-Message", { text: url(`/mail/message/${messageId_2}`) }); });