mirror of
https://github.com/bringout/oca-ocb-mail.git
synced 2026-04-20 17:22:03 +02:00
Initial commit: Mail packages
This commit is contained in:
commit
4e53507711
1948 changed files with 751201 additions and 0 deletions
|
|
@ -0,0 +1,198 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import '@mail/../tests/helpers/mock_server'; // ensure mail overrides are applied first
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat', {
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async _performRPC(route, args) {
|
||||
if (route === '/im_livechat/get_session') {
|
||||
const channel_id = args.channel_id;
|
||||
const anonymous_name = args.anonymous_name;
|
||||
const previous_operator_id = args.previous_operator_id;
|
||||
const context = args.context;
|
||||
return this._mockRouteImLivechatGetSession(channel_id, anonymous_name, previous_operator_id, context);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private Mocked Routes
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Simulates the `/im_livechat/get_session` route.
|
||||
*
|
||||
* @private
|
||||
* @param {integer} channel_id
|
||||
* @param {string} anonymous_name
|
||||
* @param {integer} [previous_operator_id]
|
||||
* @param {Object} [context={}]
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockRouteImLivechatGetSession(channel_id, anonymous_name, previous_operator_id, context = {}) {
|
||||
let user_id;
|
||||
let country_id;
|
||||
if ('mockedUserId' in context) {
|
||||
// can be falsy to simulate not being logged in
|
||||
user_id = context.mockedUserId;
|
||||
} else {
|
||||
user_id = this.currentUserId;
|
||||
}
|
||||
// don't use the anonymous name if the user is logged in
|
||||
if (user_id) {
|
||||
const user = this.getRecords('res.users', [['id', '=', user_id]])[0];
|
||||
country_id = user.country_id;
|
||||
} else {
|
||||
// simulate geoip
|
||||
const countryCode = context.mockedCountryCode;
|
||||
const country = this.getRecords('res.country', [['code', '=', countryCode]])[0];
|
||||
if (country) {
|
||||
country_id = country.id;
|
||||
anonymous_name = anonymous_name + ' (' + country.name + ')';
|
||||
}
|
||||
}
|
||||
return this._mockImLivechatChannel_openLivechatMailChannel(channel_id, anonymous_name, previous_operator_id, user_id, country_id);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private Mocked Methods
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_mockMailChannelChannelInfo(ids) {
|
||||
const channelInfos = this._super(...arguments);
|
||||
for (const channelInfo of channelInfos) {
|
||||
const channel = this.getRecords('mail.channel', [['id', '=', channelInfo.id]])[0];
|
||||
channelInfo['channel']['anonymous_name'] = channel.anonymous_name;
|
||||
// add the last message date
|
||||
if (channel.channel_type === 'livechat') {
|
||||
// add the operator id
|
||||
if (channel.livechat_operator_id) {
|
||||
const operator = this.getRecords('res.partner', [['id', '=', channel.livechat_operator_id]])[0];
|
||||
// livechat_username ignored for simplicity
|
||||
channelInfo.operator_pid = [operator.id, operator.display_name.replace(',', '')];
|
||||
}
|
||||
}
|
||||
}
|
||||
return channelInfos;
|
||||
},
|
||||
/**
|
||||
* Simulates `_get_available_users` on `im_livechat.channel`.
|
||||
*
|
||||
* @private
|
||||
* @param {integer} id
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_getAvailableUsers(id) {
|
||||
const livechatChannel = this.getRecords('im_livechat.channel', [['id', '=', id]])[0];
|
||||
const users = this.getRecords('res.users', [['id', 'in', livechatChannel.user_ids]]);
|
||||
return users.filter(user => user.im_status === 'online');
|
||||
},
|
||||
/**
|
||||
* Simulates `_get_livechat_mail_channel_vals` on `im_livechat.channel`.
|
||||
*
|
||||
* @private
|
||||
* @param {integer} id
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_getLivechatMailChannelVals(id, anonymous_name, operator, user_id, country_id) {
|
||||
// partner to add to the mail.channel
|
||||
const operator_partner_id = operator.partner_id;
|
||||
const membersToAdd = [[0, 0, {
|
||||
is_pinned: false,
|
||||
partner_id: operator_partner_id,
|
||||
}]];
|
||||
let visitor_user;
|
||||
if (user_id) {
|
||||
const visitor_user = this.getRecords('res.users', [['id', '=', user_id]])[0];
|
||||
if (visitor_user && visitor_user.active && visitor_user !== operator) {
|
||||
// valid session user (not public)
|
||||
membersToAdd.push([0, 0, { partner_id: visitor_user.partner_id.id }]);
|
||||
}
|
||||
} else {
|
||||
membersToAdd.push([0, 0, { partner_id: this.publicPartnerId }]);
|
||||
}
|
||||
const membersName = [
|
||||
visitor_user ? visitor_user.display_name : anonymous_name,
|
||||
operator.livechat_username ? operator.livechat_username : operator.name,
|
||||
];
|
||||
return {
|
||||
'channel_member_ids': membersToAdd,
|
||||
'livechat_active': true,
|
||||
'livechat_operator_id': operator_partner_id,
|
||||
'livechat_channel_id': id,
|
||||
'anonymous_name': user_id ? false : anonymous_name,
|
||||
'country_id': country_id,
|
||||
'channel_type': 'livechat',
|
||||
'name': membersName.join(' '),
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Simulates `_get_random_operator` on `im_livechat.channel`.
|
||||
* Simplified mock implementation: returns the first available operator.
|
||||
*
|
||||
* @private
|
||||
* @param {integer} id
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_getRandomOperator(id) {
|
||||
const availableUsers = this._mockImLivechatChannel_getAvailableUsers(id);
|
||||
return availableUsers[0];
|
||||
},
|
||||
/**
|
||||
* Simulates `_open_livechat_mail_channel` on `im_livechat.channel`.
|
||||
*
|
||||
* @private
|
||||
* @param {integer} id
|
||||
* @param {string} anonymous_name
|
||||
* @param {integer} [previous_operator_id]
|
||||
* @param {integer} [user_id]
|
||||
* @param {integer} [country_id]
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_openLivechatMailChannel(id, anonymous_name, previous_operator_id, user_id, country_id) {
|
||||
let operator;
|
||||
if (previous_operator_id) {
|
||||
const availableUsers = this._mockImLivechatChannel_getAvailableUsers(id);
|
||||
operator = availableUsers.find(user => user.partner_id === previous_operator_id);
|
||||
}
|
||||
if (!operator) {
|
||||
operator = this._mockImLivechatChannel_getRandomOperator(id);
|
||||
}
|
||||
if (!operator) {
|
||||
// no one available
|
||||
return false;
|
||||
}
|
||||
// create the session, and add the link with the given channel
|
||||
const mailChannelVals = this._mockImLivechatChannel_getLivechatMailChannelVals(id, anonymous_name, operator, user_id, country_id);
|
||||
const mailChannelId = this.pyEnv['mail.channel'].create(mailChannelVals);
|
||||
this._mockMailChannel_broadcast([mailChannelId], [operator.partner_id]);
|
||||
return this._mockMailChannelChannelInfo([mailChannelId])[0];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_mockResPartner_GetChannelsAsMember(ids) {
|
||||
const partner = this.getRecords('res.partner', [['id', 'in', ids]])[0];
|
||||
const members = this.getRecords('mail.channel.member', [['partner_id', '=', partner.id], ['is_pinned', '=', true]]);
|
||||
const livechats = this.getRecords('mail.channel', [
|
||||
['channel_type', '=', 'livechat'],
|
||||
['channel_member_ids', 'in', members.map(member => member.id)],
|
||||
]);
|
||||
return [
|
||||
...this._super(ids),
|
||||
...livechats,
|
||||
];
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import '@mail/../tests/helpers/mock_server'; // ensure mail overrides are applied first
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/controllers/main', {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async _performRPC(route, args) {
|
||||
if (route === '/im_livechat/notify_typing') {
|
||||
const uuid = args.uuid;
|
||||
const is_typing = args.is_typing;
|
||||
const context = args.context;
|
||||
return this._mockRouteImLivechatNotifyTyping(uuid, is_typing, context);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* Simulates the `/im_livechat/notify_typing` route.
|
||||
*
|
||||
* @private
|
||||
* @param {string} uuid
|
||||
* @param {boolean} is_typing
|
||||
* @param {Object} [context={}]
|
||||
*/
|
||||
_mockRouteImLivechatNotifyTyping(uuid, is_typing, context = {}) {
|
||||
const [mailChannel] = this.getRecords('mail.channel', [['uuid', '=', uuid]]);
|
||||
const partnerId = context.mockedPartnerId || this.currentPartnerId;
|
||||
const [memberOfCurrentUser] = this.getRecords('mail.channel.member', [['channel_id', '=', mailChannel.id], ['partner_id', '=', partnerId]]);
|
||||
this._mockMailChannelMember_NotifyTyping([memberOfCurrentUser.id], is_typing);
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import '@mail/../tests/helpers/mock_server/models/mail_channel_member'; // ensure mail overrides are applied first
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/models/mail_channel_member', {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_mockMailChannelMember_GetPartnerData(ids) {
|
||||
const [member] = this.getRecords('mail.channel.member', [['id', 'in', ids]]);
|
||||
const [channel] = this.getRecords('mail.channel', [['id', '=', member.channel_id]]);
|
||||
const [partner] = this.getRecords('res.partner', [['id', '=', member.partner_id]], { active_test: false });
|
||||
if (channel.channel_type === 'livechat') {
|
||||
const data = {
|
||||
'id': partner.id,
|
||||
'is_public': partner.is_public,
|
||||
};
|
||||
if (partner.user_livechat_username) {
|
||||
data['user_livechat_username'] = partner.user_livechat_username;
|
||||
} else {
|
||||
data['name'] = partner.name;
|
||||
}
|
||||
if (!partner.is_public) {
|
||||
const [country] = this.getRecords('res.country', [['id', '=', partner.country_id]]);
|
||||
data['country'] = country
|
||||
? {
|
||||
'code': country.code,
|
||||
'id': country.id,
|
||||
'name': country.name,
|
||||
}
|
||||
: [['clear']];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
return this._super(ids);
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { addModelNamesToFetch, insertModelFields } from '@bus/../tests/helpers/model_definitions_helpers';
|
||||
|
||||
addModelNamesToFetch(['im_livechat.channel']);
|
||||
insertModelFields('res.users.settings', {
|
||||
is_discuss_sidebar_category_livechat_open: { default: true },
|
||||
});
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('chat_window_manager', {}, function () {
|
||||
QUnit.module('chat_window_manager_tests.js');
|
||||
|
||||
QUnit.test('closing a chat window with no message from admin side unpins it', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo" });
|
||||
pyEnv['res.users'].create({ partner_id: resPartnerId1 });
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create(
|
||||
{
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
is_pinned: true,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
uuid: 'channel-10-uuid',
|
||||
},
|
||||
);
|
||||
const { messaging } = await start();
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_NotificationList_preview`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_ChatWindowHeader_commandClose`).click());
|
||||
const channels = await messaging.rpc({
|
||||
model: 'mail.channel',
|
||||
method: 'channel_info',
|
||||
args: [mailChannelId1],
|
||||
}, { shadow: true });
|
||||
assert.strictEqual(
|
||||
channels[0].is_pinned,
|
||||
false,
|
||||
'Livechat channel should not be pinned',
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { start, startServer } from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('composer_tests.js');
|
||||
|
||||
QUnit.test('livechat: no add attachment button', async function (assert) {
|
||||
// Attachments are not yet supported in livechat, especially from livechat
|
||||
// visitor PoV. This may likely change in the future with task-2029065.
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const livechatId = pyEnv['mail.channel'].create({ channel_type: 'livechat' });
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: livechatId },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(document.body, '.o_Composer', "should have a composer");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_Composer_buttonAttachment',
|
||||
"composer linked to livechat should not have a 'Add attachment' button"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat: disable attachment upload via drag and drop', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const livechatId = pyEnv['mail.channel'].create({ channel_type: 'livechat' });
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: livechatId },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(document.body, '.o_Composer', "should have a composer");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_Composer_dropZone',
|
||||
"composer linked to livechat should not have a dropzone"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('discuss_sidebar_category_item_tests.js');
|
||||
|
||||
QUnit.test('livechat - avatar: should have a smiley face avatar for an anonymous livechat item', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const livechatItem = document.querySelector(`
|
||||
.o_DiscussSidebarCategoryItem[data-channel-id="${mailChannelId1}"]
|
||||
`);
|
||||
assert.containsOnce(
|
||||
livechatItem,
|
||||
`.o_DiscussSidebarCategoryItem_image`,
|
||||
"should have an avatar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechatItem.querySelector(`:scope .o_DiscussSidebarCategoryItem_image`).dataset.src,
|
||||
'/mail/static/src/img/smiley/avatar.jpg',
|
||||
'should have the smiley face as the avatar for anonymous users'
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - avatar: should have a partner profile picture for a livechat item linked with a partner', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv['res.partner'].create({
|
||||
name: "Jean",
|
||||
});
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const livechatItem = document.querySelector(`
|
||||
.o_DiscussSidebarCategoryItem[data-channel-id="${mailChannelId1}"]
|
||||
`);
|
||||
assert.containsOnce(
|
||||
livechatItem,
|
||||
`.o_DiscussSidebarCategoryItem_image`,
|
||||
"should have an avatar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechatItem.querySelector(`:scope .o_DiscussSidebarCategoryItem_image`).dataset.src,
|
||||
`/web/image/res.partner/${resPartnerId1}/avatar_128`,
|
||||
'should have the partner profile picture as the avatar for partners'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,449 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('discuss_sidebar_category_tests.js');
|
||||
|
||||
QUnit.test('livechat - counter: should not have a counter if the category is unfolded and without unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is unfolded and without unread messages",
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - counter: should not have a counter if the category is unfolded and with unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
message_unread_counter: 10,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is unfolded and with unread messages",
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - counter: should not have a counter if category is folded and without unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
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();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is folded and without unread messages"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - counter: should have correct value of unread threads if category is folded and with unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
message_unread_counter: 10,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
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();
|
||||
await openDiscuss();
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector(`.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategory_counter`).textContent,
|
||||
"1",
|
||||
"should have correct value of unread threads if category is folded and with unread messages"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: close manually by clicking the title', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
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 { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
// fold the livechat category
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be closed and the content should be invisible"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: open manually by clicking the title', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
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 { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
// open the livechat category
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be open and the content should be visible"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: close should update the value on the server', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
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 { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const initalSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
initalSettings.is_discuss_sidebar_category_livechat_open,
|
||||
true,
|
||||
"the value in server side should be true"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
const newSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
newSettings.is_discuss_sidebar_category_livechat_open,
|
||||
false,
|
||||
"the value in server side should be false"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: open should update the value on the server', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
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 { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const initalSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
initalSettings.is_discuss_sidebar_category_livechat_open,
|
||||
false,
|
||||
"the value in server side should be false"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
const newSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
newSettings.is_discuss_sidebar_category_livechat_open,
|
||||
true,
|
||||
"the value in server side should be true"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: close from the bus', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const resUsersSettingsId1 = pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
await afterNextRender(() => {
|
||||
pyEnv['bus.bus']._sendone(pyEnv.currentPartner, 'res.users.settings/insert', {
|
||||
id: resUsersSettingsId1,
|
||||
'is_discuss_sidebar_category_livechat_open': false,
|
||||
});
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be closed and the content should be invisible"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: open from the bus', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const resUsersSettingsId1 = pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
await afterNextRender(() => {
|
||||
pyEnv['bus.bus']._sendone(pyEnv.currentPartner, 'res.users.settings/insert', {
|
||||
id: resUsersSettingsId1,
|
||||
'is_discuss_sidebar_category_livechat_open': true,
|
||||
});
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be open and the content should be visible"
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('livechat - states: category item should be invisible if the category is closed', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"inactive item should be invisible if the category is folded"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: the active category item should be visble even if the category is closed', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
const livechat = document.querySelector(`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`);
|
||||
await afterNextRender(() => {
|
||||
livechat.click();
|
||||
});
|
||||
assert.ok(livechat.classList.contains('o-active'));
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
'the active livechat item should remain open even if the category is folded'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,435 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
import { datetime_to_str } from 'web.time';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('discuss_tests.js');
|
||||
|
||||
QUnit.test('livechat in the sidebar: basic rendering', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(document.body, '.o_Discuss_sidebar',
|
||||
"should have a sidebar section"
|
||||
);
|
||||
const groupLivechat = document.querySelector('.o_DiscussSidebar_categoryLivechat');
|
||||
assert.ok(groupLivechat,
|
||||
"should have a channel group livechat"
|
||||
);
|
||||
const titleText = groupLivechat.querySelector('.o_DiscussSidebarCategory_titleText');
|
||||
assert.strictEqual(
|
||||
titleText.textContent.trim(),
|
||||
"Livechat",
|
||||
"should have a channel group named 'Livechat'"
|
||||
);
|
||||
const livechat = groupLivechat.querySelector(`
|
||||
.o_DiscussSidebarCategoryItem[data-channel-id="${mailChannelId1}"]
|
||||
`);
|
||||
assert.ok(
|
||||
livechat,
|
||||
"should have a livechat in sidebar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechat.textContent,
|
||||
"Visitor 11",
|
||||
"should have 'Visitor 11' as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat in the sidebar: existing user with country', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resCountryId1 = pyEnv['res.country'].create({
|
||||
code: 'be',
|
||||
name: "Belgium",
|
||||
});
|
||||
const resPartnerId1 = pyEnv['res.partner'].create({
|
||||
country_id: resCountryId1,
|
||||
name: "Jean",
|
||||
});
|
||||
pyEnv['mail.channel'].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should have a channel group livechat in the side bar"
|
||||
);
|
||||
const livechat = document.querySelector('.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategoryItem');
|
||||
assert.ok(
|
||||
livechat,
|
||||
"should have a livechat in sidebar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechat.textContent,
|
||||
"Jean (Belgium)",
|
||||
"should have user name and country as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('do not add livechat in the sidebar on visitor opening his chat', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['res.users'].write([pyEnv.currentUserId], { im_status: 'online' });
|
||||
const imLivechatChannelId1 = pyEnv['im_livechat.channel'].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor opening his chat
|
||||
await messaging.rpc({
|
||||
route: '/im_livechat/get_session',
|
||||
params: {
|
||||
context: {
|
||||
mockedUserId: false,
|
||||
},
|
||||
channel_id: imLivechatChannelId1,
|
||||
},
|
||||
});
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should still not have any livechat in the sidebar after visitor opened his chat"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('do not add livechat in the sidebar on visitor typing', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['res.users'].write([pyEnv.currentUserId], { im_status: 'online' });
|
||||
const imLivechatChannelId1 = pyEnv['im_livechat.channel'].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
is_pinned: false,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_channel_id: imLivechatChannelId1,
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor typing
|
||||
const channel = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
await messaging.rpc({
|
||||
route: '/im_livechat/notify_typing',
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: channel.uuid,
|
||||
},
|
||||
});
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should still not have any livechat in the sidebar after visitor started typing"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('add livechat in the sidebar on visitor sending first message', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['res.users'].write([pyEnv.currentUserId], { im_status: 'online' });
|
||||
const resCountryId1 = pyEnv['res.country'].create({
|
||||
code: 'be',
|
||||
name: "Belgium",
|
||||
});
|
||||
const imLivechatChannelId1 = pyEnv['im_livechat.channel'].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor (Belgium)",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
is_pinned: false,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
country_id: resCountryId1,
|
||||
livechat_channel_id: imLivechatChannelId1,
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor sending a message
|
||||
const channel = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
await afterNextRender(async () => messaging.rpc({
|
||||
route: '/mail/chat_post',
|
||||
params: {
|
||||
context: {
|
||||
mockedUserId: false,
|
||||
},
|
||||
uuid: channel.uuid,
|
||||
message_content: "new message",
|
||||
},
|
||||
}));
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat',
|
||||
"should have a channel group livechat in the side bar after receiving first message"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategoryItem',
|
||||
"should have a livechat in the sidebar after receiving first message"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategoryItem .o_DiscussSidebarCategoryItem_name').textContent,
|
||||
"Visitor (Belgium)",
|
||||
"should have visitor name and country as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechats are sorted by last activity time in the sidebar: most recent at the top', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const [mailChannelId1, mailChannelId2] = pyEnv['mail.channel'].create([
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
last_interest_dt: datetime_to_str(new Date(2021, 0, 1)),
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
{
|
||||
anonymous_name: "Visitor 12",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
last_interest_dt: datetime_to_str(new Date(2021, 0, 2)),
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
]);
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const initialLivechats = document.querySelectorAll('.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategory_item');
|
||||
assert.strictEqual(
|
||||
initialLivechats.length,
|
||||
2,
|
||||
"should have 2 livechat items"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(initialLivechats[0].dataset.channelId),
|
||||
mailChannelId2,
|
||||
"first livechat should be the one with the more recent last activity time"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(initialLivechats[1].dataset.channelId),
|
||||
mailChannelId1,
|
||||
"second livechat should be the one with the less recent last activity time"
|
||||
);
|
||||
|
||||
// post a new message on the last channel
|
||||
await afterNextRender(() => initialLivechats[1].click());
|
||||
await afterNextRender(() => document.execCommand('insertText', false, "Blabla"));
|
||||
await afterNextRender(() => document.querySelector('.o_Composer_buttonSend').click());
|
||||
|
||||
const newLivechats = document.querySelectorAll('.o_DiscussSidebar_categoryLivechat .o_DiscussSidebarCategory_item');
|
||||
assert.strictEqual(
|
||||
newLivechats.length,
|
||||
2,
|
||||
"should have 2 livechat items"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(newLivechats[0].dataset.channelId),
|
||||
mailChannelId1,
|
||||
"first livechat should be the one with the more recent last activity time"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(newLivechats[1].dataset.channelId),
|
||||
mailChannelId2,
|
||||
"second livechat should be the one with the less recent last activity time"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('invite button should be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create(
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
);
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadViewTopbar_inviteButton',
|
||||
"Invite button should be visible in top bar when livechat is active thread"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('call buttons should not be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create(
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
);
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_ThreadViewTopbar_callButton',
|
||||
"Call buttons should not be visible in top bar when livechat is active thread"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('reaction button should not be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
channel_partner_ids: [pyEnv.currentPartnerId, pyEnv.publicPartnerId],
|
||||
});
|
||||
const { click, insertText, openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
await insertText('.o_ComposerTextInput_textarea', "Test");
|
||||
await click('.o_Composer_buttonSend');
|
||||
await click('.o_Message');
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_MessageActionView_actionReaction',
|
||||
"should not have action to add a reaction"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('reply button should not be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
channel_partner_ids: [pyEnv.currentPartnerId, pyEnv.publicPartnerId],
|
||||
});
|
||||
const { click, insertText, openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
await insertText('.o_ComposerTextInput_textarea', "Test");
|
||||
await click('.o_Composer_buttonSend');
|
||||
await click('.o_Message');
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_MessageActionView_actionReplyTo',
|
||||
"should not have reply action"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('messaging_menu_tests.js');
|
||||
|
||||
QUnit.test('livechats should be in "chat" filter', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
await start();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenu',
|
||||
"should have messaging menu"
|
||||
);
|
||||
|
||||
await afterNextRender(() => document.querySelector('.o_MessagingMenu_toggler').click());
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenuTab[data-tab-id="all"]',
|
||||
"should have a tab/filter 'all' in messaging menu"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenuTab[data-tab-id="chat"]',
|
||||
"should have a tab/filter 'chat' in messaging menu"
|
||||
);
|
||||
assert.hasClass(
|
||||
document.querySelector('.o_MessagingMenuTab[data-tab-id="all"]'),
|
||||
'o-active',
|
||||
"tab/filter 'all' of messaging menu should be active initially"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_ChannelPreviewView[data-channel-id="${mailChannelId1}"]`,
|
||||
"livechat should be listed in 'all' tab/filter of messaging menu"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector('.o_MessagingMenuTab[data-tab-id="chat"]').click()
|
||||
);
|
||||
assert.hasClass(
|
||||
document.querySelector('.o_MessagingMenuTab[data-tab-id="chat"]'),
|
||||
'o-active',
|
||||
"tab/filter 'chat' of messaging menu should become active after click"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_ChannelPreviewView[data-channel-id="${mailChannelId1}"]`,
|
||||
"livechat should be listed in 'chat' tab/filter of messaging menu"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('thread_icon_tests.js');
|
||||
|
||||
QUnit.test('livechat: public website visitor is typing', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 20",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: mailChannelId1 },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadViewTopbar .o_ThreadIcon',
|
||||
"should have thread icon"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadIcon .fa.fa-comments',
|
||||
"should have default livechat icon"
|
||||
);
|
||||
|
||||
const mailChannel1 = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
// simulate receive typing notification from livechat visitor "is typing"
|
||||
await afterNextRender(() => messaging.rpc({
|
||||
route: '/im_livechat/notify_typing',
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: mailChannel1.uuid,
|
||||
},
|
||||
}));
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadIcon_typing',
|
||||
"should have thread icon with visitor currently typing"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ThreadIcon_typing').title,
|
||||
"Visitor 20 is typing...",
|
||||
"title of icon should tell visitor is currently typing"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('thread_textual_typing_status_tests.js');
|
||||
|
||||
QUnit.test('receive visitor typing status "is typing"', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 20",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: mailChannelId1 },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ThreadTextualTypingStatus').textContent,
|
||||
"",
|
||||
"Should display no one is currently typing"
|
||||
);
|
||||
|
||||
const mailChannel1 = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
// simulate receive typing notification from livechat visitor "is typing"
|
||||
await afterNextRender(() => messaging.rpc({
|
||||
route: '/im_livechat/notify_typing',
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: mailChannel1.uuid,
|
||||
},
|
||||
}));
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ThreadTextualTypingStatus').textContent,
|
||||
"Visitor 20 is typing...",
|
||||
"Should display that visitor is typing"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import tour from "web_tour.tour";
|
||||
|
||||
const requestChatSteps = [
|
||||
{
|
||||
trigger: ".o_livechat_button",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".o_thread_window",
|
||||
},
|
||||
];
|
||||
|
||||
tour.register("im_livechat_request_chat", { test: true }, requestChatSteps);
|
||||
|
||||
tour.register("im_livechat_request_chat_and_send_message", { test: true }, [
|
||||
...requestChatSteps,
|
||||
{
|
||||
trigger: ".o_composer_text_field",
|
||||
run: "text Hello, I need help please !",
|
||||
},
|
||||
{
|
||||
trigger: '.o_composer_text_field',
|
||||
run() {
|
||||
$(".o_composer_text_field").trigger($.Event("keydown", { which: 13 }));
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger: ".o_thread_message:contains('Hello, I need help')",
|
||||
},
|
||||
]);
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import tour from "web_tour.tour";
|
||||
|
||||
const commonSteps = [tour.stepUtils.showAppsMenuItem(), {
|
||||
trigger: '.o_app[data-menu-xmlid="im_livechat.menu_livechat_root"]',
|
||||
}, {
|
||||
trigger: 'button[data-menu-xmlid="im_livechat.livechat_config"]',
|
||||
}, {
|
||||
trigger: 'a[data-menu-xmlid="im_livechat.chatbot_config"]',
|
||||
}, {
|
||||
trigger: '.o_list_button_add',
|
||||
}, {
|
||||
trigger: 'input[id="title"]',
|
||||
run: 'text Test Chatbot Sequence'
|
||||
}, {
|
||||
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a'
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 1'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 1")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 2'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 2")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 3'
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
* Simply create a few steps in order to check the sequences.
|
||||
*/
|
||||
tour.register('im_livechat_chatbot_steps_sequence_tour', {
|
||||
test: true,
|
||||
url: '/web',
|
||||
}, [
|
||||
...commonSteps, {
|
||||
trigger: 'button:contains("Save & Close")'
|
||||
}, {
|
||||
trigger: 'body.o_web_client:not(.modal-open)',
|
||||
run() {},
|
||||
}, ...tour.stepUtils.discardForm()
|
||||
]);
|
||||
|
||||
/**
|
||||
* Same as above, with an extra drag&drop at the end.
|
||||
*/
|
||||
tour.register('im_livechat_chatbot_steps_sequence_with_move_tour', {
|
||||
test: true,
|
||||
url: '/web',
|
||||
}, [
|
||||
...commonSteps, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 3")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 4'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 4")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 5'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & Close")'
|
||||
}, {
|
||||
trigger: 'body.o_web_client:not(.modal-open)',
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 5") .o_row_handle',
|
||||
run: () => {
|
||||
// move 'step 5' between 'step 1' and 'step 2'
|
||||
const from = document.querySelector('div[name="script_step_ids"] tr:nth-child(5) .o_row_handle');
|
||||
const fromPosition = from.getBoundingClientRect();
|
||||
fromPosition.x += from.offsetWidth / 2;
|
||||
fromPosition.y += from.offsetHeight / 2;
|
||||
|
||||
const to = document.querySelector('div[name="script_step_ids"] tr:nth-child(2) .o_row_handle');
|
||||
from.dispatchEvent(new Event("mouseenter", { bubbles: true }));
|
||||
from.dispatchEvent(new MouseEvent("mousedown", {
|
||||
bubbles: true,
|
||||
which: 1,
|
||||
button: 0,
|
||||
clientX: fromPosition.x,
|
||||
clientY: fromPosition.y}));
|
||||
from.dispatchEvent(new MouseEvent("mousemove", {
|
||||
bubbles: true,
|
||||
which: 1,
|
||||
button: 0,
|
||||
// dragging is only enabled when the mouse have moved from at least 10 pixels from the original position
|
||||
clientX: fromPosition.x + 20,
|
||||
clientY: fromPosition.y + 20,
|
||||
}));
|
||||
to.dispatchEvent(new Event("mouseenter", { bubbles: true }));
|
||||
from.dispatchEvent(new Event("mouseup", { bubbles: true }));
|
||||
}
|
||||
}, {
|
||||
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a'
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 6'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & Close")'
|
||||
}, {
|
||||
trigger: 'body.o_web_client:not(.modal-open)',
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 6")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, ...tour.stepUtils.discardForm(),
|
||||
]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue