Files
odoo_source/addons/im_livechat/static/tests/sidebar_patch_tests.js
T
Alexandre Kühn 631ae1c4d8 [REF] mail: simplify JS discuss models further
- Remove "channel" field in thread formatter.
- Pass `type` for all persona formatters.
- Remove some snake_case to camelCase conversions.
- Add support for inverse fields in discuss JS models.

Details:

1. [REF] mail: remove field 'channel' in thread formatter

This field was used for channel-specific fields, which made
sense when there was a dedicated Channel model that was modeled
with composition with Thread.

To simplify formatter of threads, it's best to flatten props
so that channel-specific fields are immediately available on
thread model. This will improve insertion of data with Threads.

This commit also makes the following other changes:

- remove `discuss.channel/legacy_insert` to use
   `mail.record/insert` instead.

- Introduce `toData()` on record, which is helpful to have record
  in data format e.g. to pass as a JSON.stringifiable object.

2. [REF] mail: slightly simplify Message.insert from notif

The handling of `mail.record/insert` for Message was handling
transition from starred non-empty message to starred empty message.

To simplify all record insert from server formatted data, the notif
data is just inserted in Message. The adjustment of starred counter
is managed at model level.

This is a prerequisite to significantly simplify all
`mail.record/insert` handling.

3. [REF] mail: rename 'res.users.settings' notifications

Before this commit, notifications related to changes of user
settings were using named notification `mail.record/insert`.

This named notification should be only used for Discuss data that
should be inserted in models. `res.users.settings` is not integrated
in Discuss model, thus it has no reason to use this named
notification.

This commit rename the notification name to `res.users.settings` for
these specific notifications. This prepares simplification on
handling any `mail.record/insert` notifications that should simply
call `Record.insert()`

4. [REF] mail: make dedicate notif for Thread/fold_state

This was using named notif "mail.record/insert", which should
be used to immediately insert data in models. This is however
a dedicated notification to imperatively manager chat window
state based on timing of receiving thread data.

This may eventually become a `mail.record/insert` in the future,
but right now it's much simpler to define it as its own named
notification, in preparation to simplify `mail.record/insert`
notifications handling.

5. [REF] mail: remove Channel in mail.record/insert

This is replaced by `Thread`, so that these data can be
immediately inserted in Thread model.

6. [REF] mail: simplify slightly Attachment.update()

Now that data containing commands is supported, we could
just assign with the command rather than destructure and pick
the dict data part.

7. [REF] mail: introduce assignIn() utils

This function helps reduce LOCs from using the "in" conditional
in sequence:
```js
if (a in data) {
	this[a] = data[a];
}
if (b in data) {
	this[b] = data[b];
}
if (c in data) {
	this[c] = data[c];
}
```

To simply:
```js
assignIn(this, data, [a, b, c]);
```

8. [REF] mail: remove snake_case to camelCase conversion in models

They exist for the sake of keeping Python code snake_case and
JS camelCase. While it's good that each language have a community
that prefer syntax convention, when a codebase uses both languages
and they should work with the same data, it's not great to convert
snake_case to camelCase and vice-versa all the time.

Since server has authority over the data, the server chooses the
format for the keys. Most of them are snake_cased, therefore this
is usually the one we pick.

9. [REF] mail: rename Message.messageReactionGroups to Message.reactions

Easier to read, and matches relation name in JS model

10. [REF] mail: remove explicit assign of some many relations in Message

This reduce amount of custom code in insert(), in preparation to make
all models behave the same in response to inserting data.

11. [REF] mail: rename Thread.customName to Thread.channel_custom_name

To match server data field name, and avoid useless conversion in JS.

12. [REF] mail: simplify Message.insert for recipients

Have formatted data contain `type: "partner"` so it can be assigned
in relational field without adding `type: "partner"` manually in JS.

13. [REF] mail: introduce inverse field in discuss models

With this commit, fields in different models can be linked
together, so that one is mirror of the other field.

This simplifies some `onAdd`/`onDelete` that were added to
sync such fields, and this also simplifies insertion in
relational fields for discuss models that are identified
by records, such as the `MessageReactions` that is identified
by the message and the emoji.

14. [REF] mail: rename CannedResponse.name to 'source'

To make JS model and server data more alike.

15. [REF] mail: remove assignDefined in Persona model

So that eventually all model inserts use `Object.assign()`.

16. [REF] mail: remove 'last_message_id' from channel_info

At some point it was used to display last message in messaging menu.
This is already covered by `channel_fetch_preview` when opening the
messaging menu for the 1st time, so passing `last_message_id` in
channel_info is obsolete.

closes odoo/odoo#137750

Related: odoo/enterprise#48484
Signed-off-by: Matthieu Stockbauer (tsm) <tsm@odoo.com>
2023-10-13 11:46:13 +00:00

475 lines
18 KiB
JavaScript

/* @odoo-module */
import { startServer } from "@bus/../tests/helpers/mock_python_environment";
import { Command } from "@mail/../tests/helpers/command";
import { start } from "@mail/../tests/helpers/test_utils";
import { url } from "@web/core/utils/urls";
import { nextTick } from "@web/../tests/helpers/utils";
import { click, contains } from "@web/../tests/utils";
QUnit.module("discuss sidebar (patch)");
QUnit.test("Unknown visitor", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebar .o-mail-DiscussSidebarCategory-livechat");
await contains(".o-mail-DiscussSidebarChannel", { text: "Visitor 11" });
});
QUnit.test("Known user with country", async () => {
const pyEnv = await startServer();
const countryId = pyEnv["res.country"].create({
code: "be",
name: "Belgium",
});
const partnerId = pyEnv["res.partner"].create({
country_id: countryId,
name: "Jean",
});
pyEnv["discuss.channel"].create({
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
[0, 0, { partner_id: partnerId }],
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarChannel", { text: "Jean (Belgium)" });
});
QUnit.test("Do not show channel when visitor is typing", async () => {
const pyEnv = await startServer();
pyEnv["res.users"].write([pyEnv.currentUserId], { im_status: "online" });
const livechatChannelId = pyEnv["im_livechat.channel"].create({
user_ids: [pyEnv.currentUserId],
});
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
const channelId = pyEnv["discuss.channel"].create({
channel_member_ids: [
[
0,
0,
{
is_pinned: false,
partner_id: pyEnv.currentPartnerId,
},
],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_channel_id: livechatChannelId,
livechat_operator_id: pyEnv.currentPartnerId,
});
const { env, openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory", { count: 2 });
await contains(".o-mail-DiscussSidebarCategory-livechat", { count: 0 });
// simulate livechat visitor typing
const channel = pyEnv["discuss.channel"].searchRead([["id", "=", channelId]])[0];
await pyEnv.withGuest(guestId, () =>
env.services.rpc("/im_livechat/notify_typing", {
is_typing: true,
uuid: channel.uuid,
})
);
// weak test, no guaranteed that we waited long enough for the livechat to potentially appear
await nextTick();
await contains(".o-mail-DiscussSidebarCategory-livechat", { count: 0 });
});
QUnit.test("Close should update the value on the server", async (assert) => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: true,
});
const currentUserId = pyEnv.currentUserId;
const { env, openDiscuss } = await start();
openDiscuss();
const initalSettings = await env.services.orm.call(
"res.users.settings",
"_find_or_create_for_user",
[[currentUserId]]
);
assert.ok(initalSettings.is_discuss_sidebar_category_livechat_open);
await click(".o-mail-DiscussSidebarCategory-livechat .btn");
const newSettings = await env.services.orm.call(
"res.users.settings",
"_find_or_create_for_user",
[[currentUserId]]
);
assert.notOk(newSettings.is_discuss_sidebar_category_livechat_open);
});
QUnit.test("Open should update the value on the server", async (assert) => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: false,
});
const currentUserId = pyEnv.currentUserId;
const { env, openDiscuss } = await start();
openDiscuss();
const initalSettings = await env.services.orm.call(
"res.users.settings",
"_find_or_create_for_user",
[[currentUserId]]
);
assert.notOk(initalSettings.is_discuss_sidebar_category_livechat_open);
await click(".o-mail-DiscussSidebarCategory-livechat .btn");
const newSettings = await env.services.orm.call(
"res.users.settings",
"_find_or_create_for_user",
[[currentUserId]]
);
assert.ok(newSettings.is_discuss_sidebar_category_livechat_open);
});
QUnit.test("Open from the bus", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const settingsId = pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: false,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat");
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel", {
count: 0,
});
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "res.users.settings", {
id: settingsId,
is_discuss_sidebar_category_livechat_open: true,
});
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
});
QUnit.test("Close from the bus", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const settingsId = pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: true,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "res.users.settings", {
id: settingsId,
is_discuss_sidebar_category_livechat_open: false,
});
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel", {
count: 0,
});
});
QUnit.test("Smiley face avatar for livechat item linked to a guest", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
const channelId = pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(
`.o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel img[data-src='${url(
`/discuss/channel/${channelId}/guest/${guestId}/avatar_128`
)}']`
);
});
QUnit.test("Partner profile picture for livechat item linked to a partner", async () => {
const pyEnv = await startServer();
const partnerId = pyEnv["res.partner"].create({ name: "Jean" });
const channelId = pyEnv["discuss.channel"].create({
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
[0, 0, { partner_id: partnerId }],
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss(channelId);
await contains(
`.o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel img[data-src='${url(
`/discuss/channel/${channelId}/partner/${partnerId}/avatar_128`
)}']`
);
});
QUnit.test("No counter if the category is unfolded and with unread messages", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[
0,
0,
{
message_unread_counter: 10,
partner_id: pyEnv.currentPartnerId,
},
],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat");
await contains(".o-mail-DiscussSidebarCategory-livechat .o-mail-Discuss-category-counter", {
count: 0,
});
});
QUnit.test("No counter if category is folded and without unread messages", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: false,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat");
await contains(".o-mail-DiscussSidebarCategory-livechat .o-discuss-badge", { count: 0 });
});
QUnit.test(
"Counter should have correct value of unread threads if category is folded and with unread messages",
async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[
0,
0,
{
message_unread_counter: 10,
partner_id: pyEnv.currentPartnerId,
},
],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: false,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat .o-discuss-badge", { text: "1" });
}
);
QUnit.test("Close manually by clicking the title", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: true,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
// fold the livechat category
await click(".o-mail-DiscussSidebarCategory-livechat .btn");
await contains(".o-mail-DiscussSidebarChannel", { count: 0 });
});
QUnit.test("Open manually by clicking the title", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
pyEnv["res.users.settings"].create({
user_id: pyEnv.currentUserId,
is_discuss_sidebar_category_livechat_open: false,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat");
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel", {
count: 0,
});
// open the livechat category
await click(".o-mail-DiscussSidebarCategory-livechat .btn");
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
});
QUnit.test("Category item should be invisible if the category is closed", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
await click(".o-mail-DiscussSidebarCategory-livechat .btn");
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel", {
count: 0,
});
});
QUnit.test("Active category item should be visible even if the category is closed", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
await click(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
await contains(
".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel.o-active"
);
await click(".o-mail-DiscussSidebarCategory-livechat .btn");
await contains(".o-mail-DiscussSidebarCategory-livechat + .o-mail-DiscussSidebarChannel");
});
QUnit.test("Clicking on unpin button unpins the channel", async () => {
const pyEnv = await startServer();
pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: pyEnv["mail.guest"].create({ name: "Visitor 11" }) }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { openDiscuss } = await start();
openDiscuss();
await click(".o-mail-DiscussSidebarChannel [title='Unpin Conversation']");
await contains(".o_notification", { text: "You unpinned your conversation with Visitor 11" });
});
QUnit.test("Message unread counter", async () => {
const pyEnv = await startServer();
const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" });
const channelId = pyEnv["discuss.channel"].create({
anonymous_name: "Visitor 11",
channel_member_ids: [
[0, 0, { partner_id: pyEnv.currentPartnerId }],
Command.create({ guest_id: guestId }),
],
channel_type: "livechat",
livechat_operator_id: pyEnv.currentPartnerId,
});
const { env, openDiscuss } = await start();
openDiscuss();
pyEnv.withGuest(guestId, () =>
env.services.rpc("/im_livechat/chat_post", {
message_content: "hu",
uuid: pyEnv["discuss.channel"].searchRead([["id", "=", channelId]])[0].uuid,
})
);
await contains(".o-mail-DiscussSidebarChannel .badge", { text: "1" });
});