481 lines
18 KiB
JavaScript
481 lines
18 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { nextAnimationFrame, start, startServer } from '@mail/../tests/helpers/test_utils';
|
|
import { ROUTES_TO_IGNORE } from '@mail/../tests/helpers/webclient_setup';
|
|
|
|
import testUtils from 'web.test_utils';
|
|
import { patchDate, patchWithCleanup, selectDropdownItem, editInput } from '@web/../tests/helpers/utils';
|
|
import { ListController } from "@web/views/list/list_controller";
|
|
|
|
QUnit.module('mail', {}, function () {
|
|
QUnit.module('Chatter');
|
|
|
|
QUnit.test('list activity widget with no activity', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
const pyEnv = await startServer();
|
|
const views = {
|
|
'res.users,false,list': '<list><field name="activity_ids" widget="list_activity"/></list>',
|
|
};
|
|
const { openView } = await start({
|
|
mockRPC: function (route, args) {
|
|
if (
|
|
args.method !== 'get_views' &&
|
|
!['/mail/init_messaging', '/mail/load_message_failures', '/bus/im_status', ...ROUTES_TO_IGNORE].includes(route)
|
|
) {
|
|
assert.step(route);
|
|
}
|
|
},
|
|
serverData: { views },
|
|
session: { uid: pyEnv.currentUserId },
|
|
});
|
|
await openView({
|
|
res_model: 'res.users',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
assert.containsOnce(document.body, '.o_ActivityButtonView_icon.text-muted');
|
|
assert.strictEqual(document.querySelector('.o_ListFieldActivityView_summary').innerText, '');
|
|
|
|
assert.verifySteps(['/web/dataset/call_kw/res.users/web_search_read']);
|
|
});
|
|
|
|
QUnit.test('list activity widget with activities', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const pyEnv = await startServer();
|
|
const [mailActivityId1, mailActivityId2] = pyEnv['mail.activity'].create([{}, {}]);
|
|
const [mailActivityTypeId1, mailActivityTypeId2] = pyEnv['mail.activity.type'].create(
|
|
[{ name: 'Type 1' }, { name: 'Type 2' }],
|
|
);
|
|
pyEnv['res.users'].write([pyEnv.currentUserId], {
|
|
activity_ids: [mailActivityId1, mailActivityId2],
|
|
activity_state: 'today',
|
|
activity_summary: 'Call with Al',
|
|
activity_type_id: mailActivityTypeId1,
|
|
activity_type_icon: 'fa-phone',
|
|
});
|
|
|
|
pyEnv['res.users'].create({
|
|
activity_ids: [mailActivityId2],
|
|
activity_state: 'planned',
|
|
activity_summary: false,
|
|
activity_type_id: mailActivityTypeId2,
|
|
});
|
|
const views = {
|
|
'res.users,false,list': '<list><field name="activity_ids" widget="list_activity"/></list>',
|
|
};
|
|
|
|
const { openView } = await start({
|
|
mockRPC: function (route, args) {
|
|
if (
|
|
args.method !== 'get_views' &&
|
|
!['/mail/init_messaging', '/mail/load_message_failures', '/bus/im_status', ...ROUTES_TO_IGNORE].includes(route)
|
|
) {
|
|
assert.step(route);
|
|
}
|
|
},
|
|
serverData: { views },
|
|
});
|
|
await openView({
|
|
res_model: 'res.users',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
const firstRow = document.querySelector('.o_data_row');
|
|
assert.containsOnce(firstRow, '.o_ActivityButtonView_icon.text-warning.fa-phone');
|
|
assert.strictEqual(firstRow.querySelector('.o_ListFieldActivityView_summary').innerText, 'Call with Al');
|
|
|
|
const secondRow = document.querySelectorAll('.o_data_row')[1];
|
|
assert.containsOnce(secondRow, '.o_ActivityButtonView_icon.text-success.fa-clock-o');
|
|
assert.strictEqual(secondRow.querySelector('.o_ListFieldActivityView_summary').innerText, 'Type 2');
|
|
|
|
assert.verifySteps(['/web/dataset/call_kw/res.users/web_search_read']);
|
|
});
|
|
|
|
QUnit.test('list activity widget with activities, two pages, mark done', async function (assert) {
|
|
patchDate(2023, 0, 11, 12, 0, 0);
|
|
const pyEnv = await startServer();
|
|
const mailActivityTypeId = pyEnv['mail.activity.type'].create({});
|
|
const mailActivityId = pyEnv['mail.activity'].create({
|
|
display_name: "Meet FP",
|
|
date_deadline: moment().add(1, 'day').format("YYYY-MM-DD"), // tomorrow
|
|
can_write: true,
|
|
state: "planned",
|
|
user_id: pyEnv.currentUserId,
|
|
create_uid: pyEnv.currentUserId,
|
|
activity_type_id: mailActivityTypeId,
|
|
});
|
|
|
|
pyEnv['res.users'].create({ display_name: "User 1"});
|
|
pyEnv['res.users'].create({ display_name: "User 2"});
|
|
pyEnv['res.users'].create({
|
|
display_name: "User 3",
|
|
activity_ids: [mailActivityId],
|
|
activity_state: 'planned',
|
|
activity_summary: "Something to do",
|
|
activity_type_id: mailActivityTypeId,
|
|
});
|
|
const views = {
|
|
'res.users,false,list': `
|
|
<list limit="2">
|
|
<field name="activity_ids" widget="list_activity"/>
|
|
</list>`,
|
|
};
|
|
|
|
const { click, openView } = await start({
|
|
serverData: { views },
|
|
});
|
|
await openView({
|
|
res_model: 'res.users',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
assert.containsOnce(document.body, ".o_list_view");
|
|
assert.strictEqual(document.querySelector(".o_cp_pager").innerText, "1-2 / 4");
|
|
|
|
await click(document.querySelector(".o_pager_next"));
|
|
assert.strictEqual(document.querySelector(".o_cp_pager").innerText, "3-4 / 4");
|
|
assert.strictEqual(document.querySelectorAll(".o_data_row")[1].querySelector("[name=activity_ids]").innerText, "Something to do");
|
|
|
|
await click(document.querySelectorAll(".o_ActivityButtonView")[1]);
|
|
await click(document.querySelector(".o_ActivityListViewItem_markAsDone"));
|
|
await click(document.querySelector(".o_ActivityMarkDonePopoverContent_doneButton"));
|
|
assert.strictEqual(document.querySelector(".o_cp_pager").innerText, "3-4 / 4");
|
|
assert.strictEqual(document.querySelectorAll(".o_data_row")[1].querySelector("[name=activity_ids]").innerText, "");
|
|
});
|
|
|
|
QUnit.test('list activity widget with exception', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
const pyEnv = await startServer();
|
|
const mailActivityId1 = pyEnv['mail.activity'].create({});
|
|
const mailActivityTypeId1 = pyEnv['mail.activity.type'].create({});
|
|
pyEnv['res.users'].write([pyEnv.currentUserId], {
|
|
activity_ids: [mailActivityId1],
|
|
activity_state: 'today',
|
|
activity_summary: 'Call with Al',
|
|
activity_type_id: mailActivityTypeId1,
|
|
activity_exception_decoration: 'warning',
|
|
activity_exception_icon: 'fa-warning',
|
|
});
|
|
|
|
const views = {
|
|
'res.users,false,list': '<list><field name="activity_ids" widget="list_activity"/></list>',
|
|
};
|
|
const { openView } = await start({
|
|
mockRPC: function (route, args) {
|
|
if (
|
|
args.method !== 'get_views' &&
|
|
!['/mail/init_messaging', '/mail/load_message_failures', '/bus/im_status', ...ROUTES_TO_IGNORE].includes(route)
|
|
) {
|
|
assert.step(route);
|
|
}
|
|
},
|
|
serverData: { views },
|
|
});
|
|
await openView({
|
|
res_model: 'res.users',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
assert.containsOnce(document.body, '.o_ActivityButtonView_icon.text-warning.fa-warning');
|
|
assert.strictEqual(document.querySelector('.o_ListFieldActivityView_summary').innerText, 'Warning');
|
|
|
|
assert.verifySteps(['/web/dataset/call_kw/res.users/web_search_read']);
|
|
});
|
|
|
|
QUnit.test('list activity widget: open dropdown', async function (assert) {
|
|
assert.expect(9);
|
|
|
|
const pyEnv = await startServer();
|
|
const [mailActivityTypeId1, mailActivityTypeId2] = pyEnv['mail.activity.type'].create([{}, {}]);
|
|
const [mailActivityId1, mailActivityId2] = pyEnv['mail.activity'].create([
|
|
{
|
|
display_name: "Call with Al",
|
|
date_deadline: moment().format("YYYY-MM-DD"), // now
|
|
can_write: true,
|
|
state: "today",
|
|
user_id: pyEnv.currentUserId,
|
|
create_uid: pyEnv.currentUserId,
|
|
activity_type_id: mailActivityTypeId1,
|
|
},
|
|
{
|
|
display_name: "Meet FP",
|
|
date_deadline: moment().add(1, 'day').format("YYYY-MM-DD"), // tomorrow
|
|
can_write: true,
|
|
state: "planned",
|
|
user_id: pyEnv.currentUserId,
|
|
create_uid: pyEnv.currentUserId,
|
|
activity_type_id: mailActivityTypeId2,
|
|
}
|
|
]);
|
|
pyEnv['res.users'].write([pyEnv.currentUserId], {
|
|
activity_ids: [mailActivityId1, mailActivityId2],
|
|
activity_state: 'today',
|
|
activity_summary: 'Call with Al',
|
|
activity_type_id: mailActivityTypeId2,
|
|
});
|
|
|
|
const views = {
|
|
'res.users,false,list': '<list><field name="activity_ids" widget="list_activity"/></list>',
|
|
};
|
|
const { click, openView } = await start({
|
|
mockRPC: function (route, args) {
|
|
if (
|
|
args.method !== 'get_views' &&
|
|
!['/mail/init_messaging', '/mail/load_message_failures', '/bus/im_status', ...ROUTES_TO_IGNORE].includes(route)
|
|
) {
|
|
assert.step(args.method || route);
|
|
}
|
|
if (args.method === 'action_feedback') {
|
|
pyEnv['res.users'].write([pyEnv.currentUserId], {
|
|
activity_ids: [mailActivityId2],
|
|
activity_state: 'planned',
|
|
activity_summary: 'Meet FP',
|
|
activity_type_id: mailActivityTypeId1,
|
|
});
|
|
// random value returned in order for the mock server to know that this route is implemented.
|
|
return true;
|
|
}
|
|
},
|
|
serverData: { views },
|
|
});
|
|
|
|
patchWithCleanup(ListController.prototype, {
|
|
setup() {
|
|
this._super();
|
|
const selectRecord = this.props.selectRecord;
|
|
this.props.selectRecord = (...args) => {
|
|
assert.step(`select_record ${JSON.stringify(args)}`);
|
|
return selectRecord(...args);
|
|
};
|
|
}
|
|
});
|
|
|
|
await openView({
|
|
res_model: 'res.users',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
assert.strictEqual(document.querySelector('.o_ListFieldActivityView_summary').innerText, 'Call with Al');
|
|
|
|
await click('.o_ActivityButtonView'); // open the popover
|
|
await click('.o_ActivityListViewItem_markAsDone'); // mark the first activity as done
|
|
await click('.o_ActivityMarkDonePopoverContent_doneButton'); // confirm
|
|
|
|
assert.strictEqual(document.querySelector('.o_ListFieldActivityView_summary').innerText, 'Meet FP');
|
|
|
|
assert.verifySteps([
|
|
'web_search_read',
|
|
'activity_format',
|
|
'action_feedback',
|
|
'/mail/thread/messages',
|
|
'/mail/thread/data',
|
|
'web_search_read',
|
|
]);
|
|
});
|
|
|
|
QUnit.test('list activity exception widget with activity', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const pyEnv = await startServer();
|
|
const [mailActivityTypeId1, mailActivityTypeId2] = pyEnv['mail.activity.type'].create([{}, {}]);
|
|
const [mailActivityId1, mailActivityId2] = pyEnv['mail.activity'].create([
|
|
{
|
|
display_name: "An activity",
|
|
date_deadline: moment().format("YYYY-MM-DD"), // now
|
|
can_write: true,
|
|
state: "today",
|
|
user_id: pyEnv.currentUserId,
|
|
create_uid: pyEnv.currentUserId,
|
|
activity_type_id: mailActivityTypeId1,
|
|
},
|
|
{
|
|
display_name: "An exception activity",
|
|
date_deadline: moment().format("YYYY-MM-DD"), // now
|
|
can_write: true,
|
|
state: "today",
|
|
user_id: pyEnv.currentUserId,
|
|
create_uid: pyEnv.currentUserId,
|
|
activity_type_id: mailActivityTypeId2,
|
|
}
|
|
]);
|
|
|
|
pyEnv['res.users'].write([pyEnv.currentUserId], { activity_ids: [mailActivityId1] });
|
|
pyEnv['res.users'].create({
|
|
message_attachment_count: 3,
|
|
display_name: "second partner",
|
|
message_follower_ids: [],
|
|
message_ids: [],
|
|
activity_ids: [mailActivityId2],
|
|
activity_exception_decoration: 'warning',
|
|
activity_exception_icon: 'fa-warning',
|
|
});
|
|
const views = {
|
|
'res.users,false,list':
|
|
`<tree>
|
|
<field name="activity_exception_decoration" widget="activity_exception"/>
|
|
</tree>`,
|
|
};
|
|
const { openView } = await start({
|
|
serverData: { views },
|
|
});
|
|
await openView({
|
|
res_model: 'res.users',
|
|
views: [[false, 'list']],
|
|
});
|
|
|
|
assert.containsN(document.body, '.o_data_row', 2, "should have two records");
|
|
assert.containsNone(document.querySelectorAll('.o_data_row .o_activity_exception_cell')[0], '.o_ActivityException', "there is no any exception activity on record");
|
|
assert.containsOnce(document.querySelectorAll('.o_data_row .o_activity_exception_cell')[1], '.o_ActivityException', "there is an exception on a record");
|
|
});
|
|
|
|
QUnit.module('FieldMany2ManyTagsEmail');
|
|
|
|
QUnit.test('fieldmany2many tags email (edition)', async function (assert) {
|
|
assert.expect(17);
|
|
|
|
const pyEnv = await startServer();
|
|
const [resPartnerId1, resPartnerId2] = pyEnv['res.partner'].create([
|
|
{ name: "gold", email: 'coucou@petite.perruche' },
|
|
{ name: "silver", email: '' },
|
|
]);
|
|
const mailMessageId1 = pyEnv['mail.message'].create({
|
|
partner_ids: [resPartnerId1],
|
|
});
|
|
const views = {
|
|
'mail.message,false,form':
|
|
'<form string="Partners">' +
|
|
'<sheet>' +
|
|
'<field name="body"/>' +
|
|
'<field name="partner_ids" widget="many2many_tags_email"/>' +
|
|
'</sheet>' +
|
|
'</form>',
|
|
'res.partner,false,form': '<form string="Types"><field name="name"/><field name="email"/></form>',
|
|
};
|
|
var { openView } = await start({
|
|
serverData: { views },
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'read' && args.model === 'res.partner') {
|
|
assert.step(JSON.stringify(args.args[0]));
|
|
assert.ok(args.args[1].includes('email'), "should read the email");
|
|
} else if (args.method === "get_formview_id") {
|
|
return false;
|
|
}
|
|
},
|
|
});
|
|
await openView(
|
|
{
|
|
res_id: mailMessageId1,
|
|
res_model: 'mail.message',
|
|
views: [[false, 'form']],
|
|
},
|
|
{
|
|
mode: 'edit',
|
|
},
|
|
);
|
|
|
|
assert.verifySteps([`[${resPartnerId1}]`]);
|
|
assert.containsOnce(document.body, '.o_field_many2many_tags_email[name="partner_ids"] .badge.o_tag_color_0',
|
|
"should contain one tag");
|
|
|
|
// add an other existing tag
|
|
await selectDropdownItem(document.body, 'partner_ids', "silver");
|
|
|
|
assert.strictEqual(document.querySelectorAll('.modal-content .o_form_view').length, 1,
|
|
"there should be one modal opened to edit the empty email");
|
|
assert.strictEqual(document.querySelector(".modal-content .o_form_view .o_input#name").value, "silver",
|
|
"the opened modal in edit mode should be a form view dialog with the res.partner 14");
|
|
assert.strictEqual(document.querySelectorAll(".modal-content .o_form_view .o_input#email").length, 1,
|
|
"there should be an email field in the modal");
|
|
|
|
// set the email and save the modal (will rerender the form view)
|
|
await testUtils.fields.editInput($('.modal-content .o_form_view .o_input#email'), 'coucou@petite.perruche');
|
|
await testUtils.dom.click($('.modal-content .o_form_button_save'));
|
|
|
|
assert.containsN(document.body, '.o_field_many2many_tags_email[name="partner_ids"] .badge.o_tag_color_0', 2,
|
|
"should contain the second tag");
|
|
const firstTag = document.querySelector('.o_field_many2many_tags_email[name="partner_ids"] .badge.o_tag_color_0');
|
|
assert.strictEqual(firstTag.querySelector('.o_badge_text').innerText, "gold",
|
|
"tag should only show name");
|
|
assert.hasAttrValue(firstTag.querySelector('.o_badge_text'), 'title', "coucou@petite.perruche",
|
|
"tag should show email address on mouse hover");
|
|
// should have read resPartnerId2 three times: when opening the dropdown, when opening the modal, and
|
|
// after the save
|
|
assert.verifySteps([`[${resPartnerId2}]`, `[${resPartnerId2}]`, `[${resPartnerId2}]`]);
|
|
});
|
|
|
|
QUnit.test('many2many_tags_email widget can load more than 40 records', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const pyEnv = await startServer();
|
|
const messagePartnerIds = [];
|
|
for (let i = 100; i < 200; i++) {
|
|
messagePartnerIds.push(pyEnv['res.partner'].create({ display_name: `partner${i}` }));
|
|
}
|
|
const mailMessageId1 = pyEnv['mail.message'].create({
|
|
partner_ids: messagePartnerIds,
|
|
});
|
|
const views = {
|
|
'mail.message,false,form': '<form><field name="partner_ids" widget="many2many_tags"/></form>',
|
|
};
|
|
var { openView } = await start({
|
|
serverData: { views },
|
|
});
|
|
await openView({
|
|
res_id: mailMessageId1,
|
|
res_model: 'mail.message',
|
|
views: [[false, 'form']],
|
|
});
|
|
|
|
assert.strictEqual(document.querySelectorAll('.o_field_widget[name="partner_ids"] .badge').length, 100);
|
|
|
|
assert.containsOnce(document.body, '.o_form_editable');
|
|
|
|
// add a record to the relation
|
|
await selectDropdownItem(document.body, 'partner_ids', "Public user");
|
|
|
|
assert.strictEqual(document.querySelectorAll('.o_field_widget[name="partner_ids"] .badge').length, 101);
|
|
});
|
|
|
|
QUnit.test("auto save on click of activity widget in list view", async (assert) => {
|
|
const pyEnv = await startServer();
|
|
const activityId = pyEnv["mail.activity"].create({});
|
|
pyEnv["res.users"].write([pyEnv.currentUserId], {
|
|
activity_ids: [activityId],
|
|
activity_state: "today",
|
|
});
|
|
const { click, openView } = await start({
|
|
mockRPC(route) {
|
|
if (route === "/web/dataset/call_kw/res.users/create") {
|
|
pyEnv["res.users"].create({ activity_ids: [activityId] });
|
|
assert.step(route);
|
|
}
|
|
},
|
|
serverData: {
|
|
views: {
|
|
"res.users,false,list": `
|
|
<list editable="bottom">
|
|
<field name="name" required="1"/>
|
|
<field name="activity_ids" widget="list_activity"/>
|
|
</list>`,
|
|
}
|
|
},
|
|
});
|
|
await openView({
|
|
res_model: "res.users",
|
|
views: [[false, "list"]],
|
|
});
|
|
await click(".o_list_button_add");
|
|
assert.containsOnce($, ".o_selected_row .fa-clock-o");
|
|
click(".o_selected_row .fa-clock-o").catch(() => {});
|
|
await nextAnimationFrame();
|
|
assert.containsOnce($, ".o_notification:contains(Invalid fields: Name)");
|
|
await editInput($(".o_selected_row")[0], "[name=name] input", "tommy");
|
|
await click(".o_selected_row .fa-clock-o");
|
|
assert.verifySteps(["/web/dataset/call_kw/res.users/create"]);
|
|
});
|
|
|
|
});
|