mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 23:32:03 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
|
|
@ -0,0 +1,464 @@
|
|||
import {
|
||||
click,
|
||||
contains,
|
||||
defineMailModels,
|
||||
onRpcBefore,
|
||||
openDiscuss,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/mail_test_helpers";
|
||||
import { describe, expect, test } from "@odoo/hoot";
|
||||
import { mockUserAgent } from "@odoo/hoot-mock";
|
||||
import { asyncStep, patchWithCleanup, waitForSteps } from "@web/../tests/web_test_helpers";
|
||||
|
||||
import { download } from "@web/core/network/download";
|
||||
import { getOrigin } from "@web/core/utils/urls";
|
||||
import { isMobileOS } from "@web/core/browser/feature_detection";
|
||||
|
||||
describe.current.tags("desktop");
|
||||
defineMailModels();
|
||||
|
||||
test("simplest layout", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.txt",
|
||||
mimetype: "text/plain",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-Message .o-mail-AttachmentList");
|
||||
expect(".o-mail-AttachmentContainer:first").toHaveAttribute("title", "test.txt");
|
||||
await contains(".o-mail-AttachmentCard-image");
|
||||
expect(".o-mail-AttachmentCard-image:first").toHaveClass("o_image"); // required for mimetype.scss style
|
||||
expect(".o-mail-AttachmentCard-image:first").toHaveAttribute("data-mimetype", "text/plain"); // required for mimetype.scss style
|
||||
await contains(".o-mail-AttachmentButtons button", { count: 2 });
|
||||
await contains(".o-mail-Attachment-unlink");
|
||||
await contains(".o-mail-AttachmentButtons button[title='Download']");
|
||||
});
|
||||
|
||||
test("layout with card details and filename and extension", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.txt",
|
||||
mimetype: "text/plain",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentContainer", { text: "test.txt" });
|
||||
});
|
||||
|
||||
test("link-type attachment should have open button instead of download button", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachment_ids = pyEnv["ir.attachment"].create([
|
||||
{
|
||||
name: "url.example",
|
||||
mimetype: "text/plain",
|
||||
type: "url",
|
||||
url: "https://www.odoo.com",
|
||||
},
|
||||
{
|
||||
name: "test.txt",
|
||||
mimetype: "text/plain",
|
||||
},
|
||||
]);
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids,
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentCard", { count: 2 });
|
||||
await contains(".o-mail-AttachmentCard:eq(0)", { text: "url.example" });
|
||||
await contains(".o-mail-AttachmentCard:eq(1)", { text: "test.txt" });
|
||||
await contains(
|
||||
".o-mail-AttachmentContainer:eq(0) .o-mail-AttachmentButtons a[title='Open Link']"
|
||||
);
|
||||
await contains(
|
||||
".o-mail-AttachmentContainer:eq(0) .o-mail-AttachmentButtons button[title='Download']",
|
||||
{ count: 0 }
|
||||
);
|
||||
await contains(
|
||||
".o-mail-AttachmentContainer:eq(1) .o-mail-AttachmentButtons button[title='Download']"
|
||||
);
|
||||
await contains(`.o-mail-AttachmentButtons a[title='Open Link'][target='_blank']`);
|
||||
});
|
||||
|
||||
test("clicking on the delete attachment button multiple times should do the rpc only once", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.txt",
|
||||
mimetype: "text/plain",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
onRpcBefore("/mail/attachment/delete", () => asyncStep("attachment_unlink"));
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await click(".o-mail-Attachment-unlink");
|
||||
await click(".modal-footer .btn-primary");
|
||||
await click(".modal-footer .btn-primary");
|
||||
await click(".modal-footer .btn-primary");
|
||||
await contains(".o-mail-Attachment-unlink", { count: 0 });
|
||||
await waitForSteps(["attachment_unlink"]); // The unlink method must be called once
|
||||
});
|
||||
|
||||
test("view attachment", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.png",
|
||||
mimetype: "image/png",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentImage");
|
||||
await click(".o-mail-AttachmentImage");
|
||||
await contains(".o-FileViewer");
|
||||
});
|
||||
|
||||
test("can view pdf url", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "url.pdf.example",
|
||||
mimetype: "application/pdf",
|
||||
type: "url",
|
||||
url: "https://pdfobject.com/pdf/sample.pdf",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await click(".o-mail-AttachmentContainer", { text: "url.pdf.example" });
|
||||
await contains(".o-FileViewer");
|
||||
await contains(
|
||||
`iframe.o-FileViewer-view[data-src="/web/static/lib/pdfjs/web/viewer.html?file=${encodeURIComponent(
|
||||
`${getOrigin()}/web/content/${attachmentId}?filename=url.pdf.example`
|
||||
)}#pagemode=none"]`
|
||||
);
|
||||
});
|
||||
|
||||
test("close attachment viewer", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.png",
|
||||
mimetype: "image/png",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentImage");
|
||||
await click(".o-mail-AttachmentImage");
|
||||
await contains(".o-FileViewer");
|
||||
await click(".o-FileViewer div[aria-label='Close']");
|
||||
await contains(".o-FileViewer", { count: 0 });
|
||||
});
|
||||
|
||||
test("[technical] does not crash when the viewer is closed before image load", async () => {
|
||||
/**
|
||||
* When images are displayed using "src" attribute for the 1st time, it fetches the resource.
|
||||
* In this case, images are actually displayed (fully fetched and rendered on screen) when
|
||||
* "<image>" intercepts "load" event.
|
||||
*
|
||||
* Current code needs to be aware of load state of image, to display spinner when loading
|
||||
* and actual image when loaded. This test asserts no crash from mishandling image becoming
|
||||
* loaded from being viewed for 1st time, but viewer being closed while image is loading.
|
||||
*/
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.png",
|
||||
mimetype: "image/png",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await click(".o-mail-AttachmentImage");
|
||||
await contains(".o-FileViewer-viewImage");
|
||||
await click(".o-FileViewer div[aria-label='Close']");
|
||||
// Simulate image becoming loaded.
|
||||
expect(() => {
|
||||
document
|
||||
.querySelector(".o-FileViewer-viewImage")
|
||||
.dispatchEvent(new Event("load", { bubbles: true }));
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test("plain text file is viewable", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.txt",
|
||||
mimetype: "text/plain",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentContainer.o-viewable");
|
||||
});
|
||||
|
||||
test("HTML file is viewable", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.html",
|
||||
mimetype: "text/html",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentContainer.o-viewable");
|
||||
});
|
||||
|
||||
test("ODT file is not viewable", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.odt",
|
||||
mimetype: "application/vnd.oasis.opendocument.text",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentContainer:not(.o-viewable)");
|
||||
});
|
||||
|
||||
test("DOCX file is not viewable", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.docx",
|
||||
mimetype: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentContainer:not(.o-viewable)");
|
||||
});
|
||||
|
||||
test("should not view attachment from click on non-viewable attachment in list containing a viewable attachment", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const [attachmentId_1, attachmentId_2] = pyEnv["ir.attachment"].create([
|
||||
{
|
||||
name: "test.png",
|
||||
mimetype: "image/png",
|
||||
},
|
||||
{
|
||||
name: "test.odt",
|
||||
mimetype: "application/vnd.oasis.opendocument.text",
|
||||
},
|
||||
]);
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId_1, attachmentId_2],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-AttachmentContainer[title='test.png'].o-viewable");
|
||||
await contains(".o-mail-AttachmentContainer:not(.o-viewable)", { text: "test.odt" });
|
||||
await click(".o-mail-AttachmentContainer", { text: "test.odt" });
|
||||
// weak test, no guarantee that we waited long enough for the potential file viewer to show
|
||||
await contains(".o-FileViewer", { count: 0 });
|
||||
await click(".o-mail-AttachmentContainer[title='test.png']");
|
||||
await contains(".o-FileViewer");
|
||||
});
|
||||
|
||||
test("img file has proper src in discuss.channel", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.png",
|
||||
mimetype: "image/png",
|
||||
res_id: channelId,
|
||||
res_model: "discuss.channel",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(
|
||||
`.o-mail-AttachmentContainer[title='test.png'] img[data-src*='${getOrigin()}/web/image/${attachmentId}?filename=test.png']`
|
||||
);
|
||||
});
|
||||
|
||||
test("download url of non-viewable binary file", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.o",
|
||||
mimetype: "application/octet-stream",
|
||||
type: "binary",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".fa-download");
|
||||
|
||||
patchWithCleanup(download, {
|
||||
_download: (options) => {
|
||||
expect(options.url).toBe(`${getOrigin()}/web/content/${attachmentId}?filename=test.o&download=true`);
|
||||
},
|
||||
});
|
||||
await click(".fa-download");
|
||||
});
|
||||
|
||||
test("check actions in mobile view", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "channel",
|
||||
name: "channel1",
|
||||
});
|
||||
const attachmentId = pyEnv["ir.attachment"].create({
|
||||
name: "test.txt",
|
||||
mimetype: "text/plain",
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
attachment_ids: [attachmentId],
|
||||
body: "<p>Test</p>",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
message_type: "comment",
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
mockUserAgent("android");
|
||||
expect(isMobileOS()).toBe(true);
|
||||
await click(".o-mail-AttachmentContainer [title='Actions']");
|
||||
await contains(".dropdown-item", { text: "Remove" });
|
||||
await contains(".dropdown-item", { text: "Download" });
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
click,
|
||||
contains,
|
||||
defineMailModels,
|
||||
inputFiles,
|
||||
openDiscuss,
|
||||
openFormView,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/mail_test_helpers";
|
||||
import { describe, test } from "@odoo/hoot";
|
||||
import { Deferred } from "@odoo/hoot-mock";
|
||||
import { onRpc } from "@web/../tests/web_test_helpers";
|
||||
|
||||
describe.current.tags("desktop");
|
||||
defineMailModels();
|
||||
|
||||
test("no conflicts between file uploads", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const partnerId = pyEnv["res.partner"].create({});
|
||||
const channelId = pyEnv["discuss.channel"].create({});
|
||||
const text = new File(["hello, world"], "text1.txt", { type: "text/plain" });
|
||||
const text2 = new File(["hello, world"], "text2.txt", { type: "text/plain" });
|
||||
pyEnv["mail.message"].create({
|
||||
body: "not empty",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
await start();
|
||||
// Uploading file in the first thread: res.partner chatter.
|
||||
await openFormView("res.partner", partnerId);
|
||||
await click("button", { text: "Send message" });
|
||||
await inputFiles(".o-mail-Chatter .o-mail-Composer input[type=file]", [text]);
|
||||
// Uploading file in the second thread: discuss.channel in chatWindow.
|
||||
await click("i[aria-label='Messages']");
|
||||
await click(".o-mail-NotificationItem");
|
||||
await inputFiles(".o-mail-ChatWindow .o-mail-Composer input[type=file]", [text2]);
|
||||
await contains(".o-mail-Chatter .o-mail-AttachmentContainer");
|
||||
await contains(".o-mail-ChatWindow .o-mail-AttachmentContainer");
|
||||
await contains(
|
||||
".o-mail-Chatter .o-mail-AttachmentContainer:not(.o-isUploading):contains(text1.txt)"
|
||||
);
|
||||
await contains(
|
||||
".o-mail-ChatWindow .o-mail-AttachmentContainer:not(.o-isUploading):contains(text2.txt)"
|
||||
);
|
||||
});
|
||||
|
||||
test("Attachment shows spinner during upload", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "channel_1" });
|
||||
const text2 = new File(["hello, world"], "text2.txt", { type: "text/plain" });
|
||||
onRpc("/mail/attachment/upload", () => new Deferred()); // never fulfill the attachment upload promise.
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await inputFiles(".o-mail-Composer input[type=file]", [text2]);
|
||||
await contains(
|
||||
".o-mail-AttachmentContainer.o-isUploading:contains(text2.txt) .fa-circle-o-notch"
|
||||
);
|
||||
});
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
import {
|
||||
click,
|
||||
contains,
|
||||
defineMailModels,
|
||||
isInViewportOf,
|
||||
openDiscuss,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/mail_test_helpers";
|
||||
import { Thread } from "@mail/core/common/thread";
|
||||
import { describe, test } from "@odoo/hoot";
|
||||
import { advanceTime, Deferred, tick, waitFor } from "@odoo/hoot-dom";
|
||||
import { disableAnimations } from "@odoo/hoot-mock";
|
||||
import { router, routerBus } from "@web/core/browser/router";
|
||||
import { range } from "@web/core/utils/numbers";
|
||||
import { mountWebClient, patchWithCleanup } from "@web/../tests/web_test_helpers";
|
||||
|
||||
defineMailModels();
|
||||
describe.current.tags("desktop");
|
||||
|
||||
test("can highlight messages that are not yet loaded", async () => {
|
||||
disableAnimations();
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
let middleMessageId;
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const messageId = pyEnv["mail.message"].create({
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
if (i === 100) {
|
||||
middleMessageId = messageId;
|
||||
}
|
||||
}
|
||||
await pyEnv["discuss.channel"].set_message_pin(channelId, middleMessageId, true);
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await tick(); // Wait for the scroll to first unread to complete.
|
||||
await isInViewportOf(".o-mail-Message:contains(message 199)", ".o-mail-Thread");
|
||||
await click("a[data-oe-type='highlight']");
|
||||
await isInViewportOf(".o-mail-Message:contains(message 100)", ".o-mail-Thread");
|
||||
});
|
||||
|
||||
test("can highlight message (slow ref registration)", async () => {
|
||||
disableAnimations();
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
let middleMessageId;
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const messageId = pyEnv["mail.message"].create({
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
if (i === 100) {
|
||||
middleMessageId = messageId;
|
||||
}
|
||||
}
|
||||
await pyEnv["discuss.channel"].set_message_pin(channelId, middleMessageId, true);
|
||||
let slowRegisterMessageDef;
|
||||
patchWithCleanup(Thread.prototype, {
|
||||
async registerMessageRef(...args) {
|
||||
// Ensure scroll is made even when messages are mounted later.
|
||||
await slowRegisterMessageDef;
|
||||
return super.registerMessageRef(...args);
|
||||
},
|
||||
});
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await tick(); // Wait for the scroll to first unread to complete.
|
||||
await isInViewportOf(".o-mail-Message:contains(message 199)", ".o-mail-Thread");
|
||||
slowRegisterMessageDef = new Deferred();
|
||||
await click("a[data-oe-type='highlight']");
|
||||
await advanceTime(1000);
|
||||
slowRegisterMessageDef.resolve();
|
||||
await isInViewportOf(".o-mail-Message:contains(message 100)", ".o-mail-Thread");
|
||||
});
|
||||
|
||||
test("highlight scrolls to beginning of long message", async () => {
|
||||
disableAnimations();
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
const [messageId1] = pyEnv["mail.message"].create([
|
||||
{
|
||||
body: `long message `.repeat(500),
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
},
|
||||
{
|
||||
body: `short message`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
},
|
||||
]);
|
||||
await pyEnv["discuss.channel"].set_message_pin(channelId, messageId1, true);
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await waitFor(".o-mail-Message:contains('short message')");
|
||||
await isInViewportOf(".o-mail-Message:contains('short message')", ".o-mail-Thread");
|
||||
await click("a[data-oe-type='highlight']");
|
||||
await advanceTime(1000);
|
||||
await isInViewportOf(".o-mail-Message:contains('long message')", ".o-mail-Thread");
|
||||
await isInViewportOf(
|
||||
".o-mail-Message:contains('long message') .o-mail-Message-avatar", // avatar is at beginning of message
|
||||
".o-mail-Thread"
|
||||
);
|
||||
});
|
||||
|
||||
test("Chatter jumps when navigating to a specific message link", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const partnerId = pyEnv["res.partner"].create({ name: "John Doe" });
|
||||
const messageIds = range(0, 31).map((i) =>
|
||||
pyEnv["mail.message"].create({
|
||||
body: `message ${i}`,
|
||||
model: "res.partner",
|
||||
res_id: partnerId,
|
||||
})
|
||||
);
|
||||
await mountWebClient();
|
||||
router.pushState(
|
||||
router.urlToState(
|
||||
new URL(
|
||||
`${window.location.origin}/odoo/res.partner/${partnerId}?highlight_message_id=${messageIds[0]}`
|
||||
)
|
||||
),
|
||||
{ sync: true }
|
||||
);
|
||||
routerBus.trigger("ROUTE_CHANGE");
|
||||
await contains(".o-mail-Message.o-highlighted .o-mail-Message-content", { text: "message 0" });
|
||||
});
|
||||
1019
odoo-bringout-oca-ocb-mail/mail/static/tests/thread/thread.test.js
Normal file
1019
odoo-bringout-oca-ocb-mail/mail/static/tests/thread/thread.test.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,249 @@
|
|||
import {
|
||||
click,
|
||||
contains,
|
||||
defineMailModels,
|
||||
focus,
|
||||
openDiscuss,
|
||||
patchUiSize,
|
||||
scroll,
|
||||
SIZES,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/mail_test_helpers";
|
||||
import { describe, test } from "@odoo/hoot";
|
||||
import { mockUserAgent, tick } from "@odoo/hoot-mock";
|
||||
import {
|
||||
asyncStep,
|
||||
Command,
|
||||
onRpc,
|
||||
serverState,
|
||||
waitForSteps,
|
||||
withUser,
|
||||
} from "@web/../tests/web_test_helpers";
|
||||
import { rpc } from "@web/core/network/rpc";
|
||||
|
||||
describe.current.tags("desktop");
|
||||
defineMailModels();
|
||||
|
||||
test("show unread messages banner when there are unread messages", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
}
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", { count: 30 });
|
||||
await contains("span", { text: "30 new messagesMark as Read" });
|
||||
});
|
||||
|
||||
test("mark thread as read from unread messages banner", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
}
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", { count: 30 });
|
||||
await click("span", {
|
||||
text: "Mark as Read",
|
||||
parent: ["span", { text: "30 new messagesMark as Read" }],
|
||||
});
|
||||
});
|
||||
|
||||
test("reset new message separator from unread messages banner", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
}
|
||||
pyEnv["discuss.channel.member"].search([
|
||||
["partner_id", "=", serverState.partnerId],
|
||||
["channel_id", "=", channelId],
|
||||
]);
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", { count: 30 });
|
||||
await contains(".o-mail-Message", {
|
||||
text: "message 0",
|
||||
});
|
||||
await click("span", {
|
||||
text: "Mark as Read",
|
||||
parent: ["span", { text: "30 new messagesMark as Read" }],
|
||||
});
|
||||
await contains("span", { text: "30 new messagesMark as Read", count: 0 });
|
||||
});
|
||||
|
||||
test("remove banner when scrolling to bottom", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
for (let i = 0; i < 50; ++i) {
|
||||
pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
}
|
||||
onRpc("/discuss/channel/mark_as_read", () => asyncStep("mark_as_read"));
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", { count: 30 });
|
||||
await contains(".o-mail-Composer.o-focused");
|
||||
await focus(".o-mail-Thread");
|
||||
await contains(".o-mail-Thread-banner", { text: "50 new messages" });
|
||||
await tick(); // wait for the scroll to first unread to complete
|
||||
await scroll(".o-mail-Thread", "bottom");
|
||||
await contains(".o-mail-Message", { count: 50 });
|
||||
// Banner is still present as there are more messages to load so we did not
|
||||
// reach the actual bottom.
|
||||
await contains(".o-mail-Thread-banner", { text: "50 new messages" });
|
||||
await scroll(".o-mail-Thread", "bottom");
|
||||
await contains(".o-mail-Thread-banner", { text: "50 new messages", count: 0 });
|
||||
await waitForSteps(["mark_as_read"]);
|
||||
});
|
||||
|
||||
test("remove banner when opening thread at the bottom", async () => {
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["res.users"].write(serverState.userId, { notification_type: "inbox" });
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
const messageId = pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `Hello World`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
const [selfMemberId] = pyEnv["discuss.channel.member"].search([
|
||||
["partner_id", "=", serverState.partnerId],
|
||||
["channel_id", "=", channelId],
|
||||
]);
|
||||
pyEnv["discuss.channel.member"].write([selfMemberId], { new_message_separator: messageId + 1 });
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await click("[title='Expand']", { parent: [".o-mail-Message", { text: "Hello World" }] });
|
||||
await click(".o-dropdown-item:contains('Mark as Unread')");
|
||||
await contains(".o-mail-Thread-banner", { text: "1 new message" });
|
||||
await click(".o-mail-DiscussSidebar-item", { text: "Inbox" });
|
||||
await contains(".o-mail-DiscussContent-threadName[title='Inbox']");
|
||||
await click(".o-mail-DiscussSidebarChannel", { text: "general" });
|
||||
await contains(".o-mail-DiscussContent-threadName[title='general']");
|
||||
await contains(".o-mail-Thread-banner", { text: "1 new message", count: 0 });
|
||||
});
|
||||
|
||||
test("keep banner after mark as unread when scrolling to bottom", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
}
|
||||
await start();
|
||||
await openDiscuss(channelId);
|
||||
await click("[title='Expand']", { parent: [".o-mail-Message", { text: "message 29" }] });
|
||||
await click(".o-dropdown-item:contains('Mark as Unread')");
|
||||
await scroll(".o-mail-Thread", "bottom");
|
||||
await contains(".o-mail-Thread-banner", { text: "30 new messages" });
|
||||
});
|
||||
|
||||
test("sidebar and banner counters display same value", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "Bob" });
|
||||
const bobUserId = pyEnv["res.users"].create({ name: "Bob", partner_id: bobPartnerId });
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "chat",
|
||||
channel_member_ids: [
|
||||
Command.create({ partner_id: serverState.partnerId }),
|
||||
Command.create({ partner_id: bobPartnerId }),
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
pyEnv["mail.message"].create({
|
||||
author_id: bobPartnerId,
|
||||
body: `message ${i}`,
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
}
|
||||
await start();
|
||||
await openDiscuss();
|
||||
await contains(".o-mail-DiscussSidebar-badge", {
|
||||
text: "30",
|
||||
parent: [".o-mail-DiscussSidebarChannel", { text: "Bob" }],
|
||||
});
|
||||
await click(".o-mail-DiscussSidebarChannel", { text: "Bob" });
|
||||
await contains(".o-mail-Thread-banner", { text: "30 new messages" });
|
||||
await contains(".o-mail-DiscussSidebar-badge", { text: "30" });
|
||||
await withUser(bobUserId, () =>
|
||||
rpc("/mail/message/post", {
|
||||
post_data: {
|
||||
body: "Hello!",
|
||||
message_type: "comment",
|
||||
subtype_xmlid: "mail.mt_comment",
|
||||
},
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
);
|
||||
await contains(".o-mail-Thread-banner", { text: "31 new messages" });
|
||||
await contains(".o-mail-DiscussSidebar-badge", {
|
||||
text: "31",
|
||||
parent: [".o-mail-DiscussSidebarChannel", { text: "Bob" }],
|
||||
});
|
||||
});
|
||||
|
||||
test("mobile: mark as read when opening chat", async () => {
|
||||
mockUserAgent("android");
|
||||
const pyEnv = await startServer();
|
||||
const bobPartnerId = pyEnv["res.partner"].create({ name: "bob" });
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_type: "chat",
|
||||
channel_member_ids: [
|
||||
Command.create({ partner_id: serverState.partnerId }),
|
||||
Command.create({ partner_id: bobPartnerId }),
|
||||
],
|
||||
});
|
||||
pyEnv["mail.message"].create({
|
||||
body: "Hello!",
|
||||
model: "discuss.channel",
|
||||
author_id: bobPartnerId,
|
||||
res_id: channelId,
|
||||
});
|
||||
patchUiSize({ size: SIZES.SM });
|
||||
await start();
|
||||
await openDiscuss();
|
||||
await contains("button.active", { text: "Notifications" });
|
||||
await click("button:has(.badge:contains('1'))", { text: "Chats" });
|
||||
await contains(".o-mail-NotificationItem:has(.badge:contains(1))", { text: "bob" });
|
||||
await click(".o-mail-NotificationItem", { text: "bob" });
|
||||
await contains(".o-mail-Message");
|
||||
await contains(".o-mail-Thread.o-focused");
|
||||
await contains(".o-mail-Composer:not(.o-focused)");
|
||||
await click(".o-mail-ChatWindow-header [title*='Close Chat Window']");
|
||||
await contains(".o-mail-NotificationItem:has(.badge:contains(1))", { text: "bob", count: 0 });
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue