[IMP] test_mail: adapt activity test to use startServer

This PR helps reducing the noise in the one adapting discuss
to use wowlEnv. Indeed, the start method will now only rely
on the createWebClient method. This means we'll need to adapt
those tests to use real models. To do so, we need to move
those tests in the test_mail module.

task-2582313

closes odoo/odoo#89666

Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
tsm-odoo
2022-04-27 09:00:21 +02:00
parent e58ef79c74
commit c484ff74fc
+160 -253
View File
@@ -2,120 +2,81 @@
import ActivityRenderer from '@mail/js/views/activity/activity_renderer';
import ActivityView from '@mail/js/views/activity/activity_view';
import { start, startServer } from '@mail/../tests/helpers/test_utils';
import testUtils from 'web.test_utils';
import domUtils from 'web.dom';
import { getFixture, legacyExtraNextTick, patchWithCleanup, click } from "@web/../tests/helpers/utils";
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
import { doAction } from "@web/../tests/webclient/helpers";
import { session } from '@web/session';
let serverData;
let pyEnv;
let target;
var createView = testUtils.createView;
QUnit.module('mail', {}, function () {
QUnit.module('test_mail', {}, function () {
QUnit.module('activity view', {
beforeEach: function () {
this.data = {
task: {
fields: {
id: {string: 'ID', type: 'integer'},
foo: {string: "Foo", type: "char"},
activity_ids: {
string: 'Activities',
type: 'one2many',
relation: 'mail.activity',
relation_field: 'res_id',
},
},
records: [
{id: 13, foo: 'Meeting Room Furnitures', activity_ids: [1]},
{id: 30, foo: 'Office planning', activity_ids: [2, 3]},
],
async beforeEach() {
pyEnv = await startServer();
const mailTemplateIds = pyEnv['mail.template'].create([{ name: "Template1" }, { name: "Template2" }]);
// reset incompatible setup
pyEnv['mail.activity.type'].unlink(pyEnv['mail.activity.type'].search([]));
const mailActivityTypeIds = pyEnv['mail.activity.type'].create([
{ name: "Email", mail_template_ids: mailTemplateIds },
{ name: "Call" },
{ name: "Call for Demo" },
{ name: "To Do" },
]);
const resUsersId1 = pyEnv['res.users'].create({ display_name: 'first user' });
const mailActivityIds = pyEnv['mail.activity'].create([
{
display_name: "An activity",
date_deadline: moment().add(3, "days").format("YYYY-MM-DD"), // now
can_write: true,
state: "planned",
activity_type_id: mailActivityTypeIds[0],
mail_template_ids: mailTemplateIds,
user_id: resUsersId1,
},
partner: {
fields: {
display_name: { string: "Displayed name", type: "char" },
},
records: [{
id: 2,
display_name: "first partner",
}]
{
display_name: "An activity",
date_deadline: moment().format("YYYY-MM-DD"), // now
can_write: true,
state: "today",
activity_type_id: mailActivityTypeIds[0],
mail_template_ids: mailTemplateIds,
user_id: resUsersId1,
},
'mail.activity': {
fields: {
res_id: { string: 'Related document id', type: 'integer' },
activity_type_id: { string: "Activity type", type: "many2one", relation: "mail.activity.type" },
display_name: { string: "Display name", type: "char" },
date_deadline: { string: "Due Date", type: "date" },
can_write: { string: "Can write", type: "boolean" },
state: {
string: 'State',
type: 'selection',
selection: [['overdue', 'Overdue'], ['today', 'Today'], ['planned', 'Planned']],
},
mail_template_ids: { string: "Mail templates", type: "many2many", relation: "mail.template" },
user_id: { string: "Assigned to", type: "many2one", relation: 'partner' },
},
records:[
{
id: 1,
res_id: 13,
display_name: "An activity",
date_deadline: moment().add(3, "days").format("YYYY-MM-DD"), // now
can_write: true,
state: "planned",
activity_type_id: 1,
mail_template_ids: [8, 9],
user_id:2,
},{
id: 2,
res_id: 30,
display_name: "An activity",
date_deadline: moment().format("YYYY-MM-DD"), // now
can_write: true,
state: "today",
activity_type_id: 1,
mail_template_ids: [8, 9],
user_id:2,
},{
id: 3,
res_id: 30,
display_name: "An activity",
date_deadline: moment().subtract(2, "days").format("YYYY-MM-DD"), // now
can_write: true,
state: "overdue",
activity_type_id: 2,
mail_template_ids: [],
user_id:2,
}
],
{
res_model: 'mail.test.activity',
display_name: "An activity",
date_deadline: moment().subtract(2, "days").format("YYYY-MM-DD"), // now
can_write: true,
state: "overdue",
activity_type_id: mailActivityTypeIds[1],
user_id: resUsersId1,
},
'mail.template': {
fields: {
name: { string: "Name", type: "char" },
},
records: [
{ id: 8, name: "Template1" },
{ id: 9, name: "Template2" },
],
},
'mail.activity.type': {
fields: {
mail_template_ids: { string: "Mail templates", type: "many2many", relation: "mail.template" },
name: { string: "Name", type: "char" },
},
records: [
{ id: 1, name: "Email", mail_template_ids: [8, 9]},
{ id: 2, name: "Call" },
{ id: 3, name: "Call for Demo" },
{ id: 4, name: "To Do" },
],
]);
pyEnv['mail.test.activity'].create([
{ name: 'Meeting Room Furnitures', activity_ids: [mailActivityIds[0]] },
{ name: 'Office planning', activity_ids: [mailActivityIds[1], mailActivityIds[2]] },
]);
serverData = {
views : {
'mail.test.activity,false,activity':
'<activity string="MailTestActivity">' +
'<templates>' +
'<div t-name="activity-box">' +
'<field name="name"/>' +
'</div>' +
'</templates>' +
'</activity>',
'mail.activity,false,form': '<form/>',
'mail.test.activity,false,form': '<form/>',
'mail.test.activity,false,search': '<search/>',
},
};
serverData = { models: this.data };
target = getFixture();
}
});
@@ -126,14 +87,16 @@ var activityDateFormat = function (date) {
QUnit.test('activity view: simple activity rendering', async function (assert) {
assert.expect(14);
var activity = await createView({
const mailTestActivityIds = pyEnv['mail.test.activity'].search([]);
const mailActivityTypeIds = pyEnv['mail.activity.type'].search([]);
var { widget: activity } = await start({
View: ActivityView,
model: 'task',
data: this.data,
hasView: true,
model: 'mail.test.activity',
arch: '<activity string="Task">' +
'<templates>' +
'<div t-name="activity-box">' +
'<field name="foo"/>' +
'<field name="name"/>' +
'</div>' +
'</templates>' +
'</activity>',
@@ -141,9 +104,9 @@ QUnit.test('activity view: simple activity rendering', async function (assert) {
do_action: function (event) {
assert.deepEqual(event.data.action, {
context: {
default_res_id: 30,
default_res_model: "task",
default_activity_type_id: 3,
default_res_id: mailTestActivityIds[1],
default_res_model: "mail.test.activity",
default_activity_type_id: mailActivityTypeIds[2],
},
res_id: false,
res_model: "mail.activity",
@@ -190,28 +153,22 @@ QUnit.test('activity view: simple activity rendering', async function (assert) {
await testUtils.fields.editAndTrigger(activity.$(td + ':first'), null, ['mouseenter', 'click']);
assert.containsOnce(activity, 'table tfoot tr .o_record_selector',
'should contain search more selector to choose the record to schedule an activity for it');
activity.destroy();
});
QUnit.test('activity view: no content rendering', async function (assert) {
assert.expect(2);
// reset incompatible setup
this.data['mail.activity'].records = [];
this.data.task.records.forEach(function (task) {
task.activity_ids = false;
});
this.data['mail.activity.type'].records = [];
pyEnv['mail.activity.type'].unlink(pyEnv['mail.activity.type'].search([]));
var activity = await createView({
var { widget: activity } = await start({
hasView: true,
View: ActivityView,
model: 'task',
data: this.data,
model: 'mail.test.activity',
arch: '<activity string="Task">' +
'<templates>' +
'<div t-name="activity-box">' +
'<field name="foo"/>' +
'<field name="name"/>' +
'</div>' +
'</templates>' +
'</activity>',
@@ -222,20 +179,21 @@ QUnit.test('activity view: no content rendering', async function (assert) {
assert.strictEqual(activity.$('.o_view_nocontent .o_view_nocontent_empty_folder').text().trim(),
"No data to display",
"should display the no content helper text");
activity.destroy();
});
QUnit.test('activity view: batch send mail on activity', async function (assert) {
assert.expect(6);
var activity = await createView({
const mailTestActivityIds = pyEnv['mail.test.activity'].search([]);
const mailTemplateIds = pyEnv['mail.template'].search([]);
var { widget: activity } = await start({
hasView: true,
View: ActivityView,
model: 'task',
data: this.data,
model: 'mail.test.activity',
arch: '<activity string="Task">' +
'<templates>' +
'<div t-name="activity-box">' +
'<field name="foo"/>' +
'<field name="name"/>' +
'</div>' +
'</templates>' +
'</activity>',
@@ -261,30 +219,31 @@ QUnit.test('activity view: batch send mail on activity', async function (assert)
testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) i.fa-ellipsis-v'));
testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show .o_send_mail_template:contains(Template1)'));
assert.verifySteps([
'[[13,30],9]', // send mail template 9 on tasks 13 and 30
'[[13,30],8]', // send mail template 8 on tasks 13 and 30
`[[${mailTestActivityIds[0]},${mailTestActivityIds[1]}],${mailTemplateIds[1]}]`, // send mail template 1 on mail.test.activity 1 and 2
`[[${mailTestActivityIds[0]},${mailTestActivityIds[1]}],${mailTemplateIds[0]}]`, // send mail template 2 on mail.test.activity 1 and 2
]);
activity.destroy();
});
QUnit.test('activity view: activity widget', async function (assert) {
assert.expect(16);
const mailActivityTypeIds = pyEnv['mail.activity.type'].search([]);
const [mailTestActivityId2] = pyEnv['mail.test.activity'].search([['name', '=', 'Office planning']]);
const [mailTemplateId1] = pyEnv['mail.template'].search([['name', '=', 'Template1']]);
const params = {
hasView: true,
View: ActivityView,
model: 'task',
data: this.data,
model: 'mail.test.activity',
arch: '<activity string="Task">' +
'<templates>' +
'<div t-name="activity-box">' +
'<field name="foo"/>' +
'<field name="name"/>' +
'</div>' +
'</templates>'+
'</activity>',
mockRPC: function(route, args) {
if (args.method === 'activity_send_mail'){
assert.deepEqual([[30],8],args.args, "Should send template 8 on record 30");
assert.deepEqual([[mailTestActivityId2],mailTemplateId1],args.args, "Should send template 8 on record 30");
assert.step('activity_send_mail');
return Promise.resolve();
}
@@ -303,18 +262,18 @@ QUnit.test('activity view: activity widget', async function (assert) {
assert.step('serverGeneratedAction');
} else if (action.res_model === 'mail.compose.message') {
assert.deepEqual({
default_model: "task",
default_res_id: 30,
default_template_id: 8,
default_model: 'mail.test.activity',
default_res_id: mailTestActivityId2,
default_template_id: mailTemplateId1,
default_use_template: true,
force_email: true
}, action.context);
assert.step("do_action_compose");
} else if (action.res_model === 'mail.activity') {
assert.deepEqual({
"default_activity_type_id": 2,
"default_res_id": 30,
"default_res_model": "task"
"default_activity_type_id": mailActivityTypeIds[1],
"default_res_id": mailTestActivityId2,
"default_res_model": 'mail.test.activity',
}, action.context);
assert.step("do_action_activity");
} else {
@@ -324,7 +283,7 @@ QUnit.test('activity view: activity widget', async function (assert) {
},
};
var activity = await createView(params);
var { widget: activity } = await start(params);
var today = activity.$('table tbody tr:first td:nth-child(2).today');
var dropdown = today.find('.dropdown-menu.o_activity');
@@ -367,25 +326,13 @@ QUnit.test("activity view: no group_by_menu and no comparison_menu", async funct
serverData.actions = {
1: {
id: 1,
name: "Task Action",
res_model: "task",
name: "MailTestActivity Action",
res_model: "mail.test.activity",
type: "ir.actions.act_window",
views: [[false, "activity"]],
},
};
serverData.views = {
"task,false,activity":
'<activity string="Task">' +
"<templates>" +
'<div t-name="activity-box">' +
'<field name="foo"/>' +
"</div>" +
"</templates>" +
"</activity>",
"task,false,search": "<search></search>",
};
const mockRPC = (route, args) => {
if (args.method === "get_activity_data") {
assert.strictEqual(
@@ -398,7 +345,7 @@ QUnit.test("activity view: no group_by_menu and no comparison_menu", async funct
patchWithCleanup(session.user_context, { lang: "zz_ZZ" });
const webClient = await createWebClient({ serverData, mockRPC , legacyParams: {withLegacyMockServer: true}});
const { widget: webClient } = await start({ hasWebClient: true, serverData, mockRPC, legacyParams: {withLegacyMockServer: true}});
await doAction(webClient, 1);
@@ -420,24 +367,24 @@ QUnit.test("activity view: no group_by_menu and no comparison_menu", async funct
QUnit.test('activity view: search more to schedule an activity for a record of a respecting model', async function (assert) {
assert.expect(5);
_.extend(this.data.task.fields, {
name: { string: "Name", type: "char" },
const mailTestActivityId1 = pyEnv['mail.test.activity'].create({ name: 'MailTestActivity 3' });
Object.assign(serverData.views, {
'mail.test.activity,false,list': '<tree string="MailTestActivity"><field name="name"/></tree>',
});
this.data.task.records[2] = { id: 31, name: "Task 3" };
var activity = await createView({
var { widget: activity } = await start({
hasView: true,
View: ActivityView,
model: 'task',
data: this.data,
model: 'mail.test.activity',
arch: '<activity string="Task">' +
'<templates>' +
'<div t-name="activity-box">' +
'<field name="foo"/>' +
'<field name="name"/>' +
'</div>' +
'</templates>' +
'</activity>',
archs: {
"task,false,list": '<tree string="Task"><field name="name"/></tree>',
"task,false,search": '<search></search>',
"mail.test.activity,false,list": '<tree string="Task"><field name="name"/></tree>',
"mail.test.activity,false,search": '<search></search>',
},
mockRPC: function(route, args) {
if (args.method === 'name_search') {
@@ -450,8 +397,8 @@ QUnit.test('activity view: search more to schedule an activity for a record of a
assert.step('doAction');
var expectedAction = {
context: {
default_res_id: { id: 31, display_name: undefined },
default_res_model: "task",
default_res_id: { id: mailTestActivityId1, display_name: undefined },
default_res_model: "mail.test.activity",
},
name: "Schedule Activity",
res_id: false,
@@ -487,31 +434,22 @@ QUnit.test("Activity view: discard an activity creation dialog", async function
serverData.actions = {
1: {
id: 1,
name: "Task Action",
res_model: "task",
name: "MailTestActivity Action",
res_model: "mail.test.activity",
type: "ir.actions.act_window",
views: [[false, "activity"]],
},
};
serverData.views = {
"task,false,activity": `
<activity string="Task">
<templates>
<div t-name="activity-box">
<field name="foo"/>
</div>
</templates>
</activity>`,
"task,false,search": "<search></search>",
"mail.activity,false,form": `
<form>
<field name="display_name"/>
<footer>
<button string="Discard" class="btn-secondary" special="cancel"/>
</footer>
</form>`,
};
Object.assign(serverData.views, {
'mail.activity,false,form':
`<form>
<field name="display_name"/>
<footer>
<button string="Discard" class="btn-secondary" special="cancel"/>
</footer>
</form>`,
});
const mockRPC = (route, args) => {
if (args.method === "check_access_rights") {
@@ -519,7 +457,7 @@ QUnit.test("Activity view: discard an activity creation dialog", async function
}
};
const webClient = await createWebClient({ serverData, mockRPC, legacyParams: {withLegacyMockServer: true} });
const { widget: webClient } = await start({ hasWebClient: true, serverData, mockRPC, legacyParams: {withLegacyMockServer: true} });
await doAction(webClient, 1);
await testUtils.dom.click(
@@ -536,51 +474,39 @@ QUnit.test("Activity view: discard an activity creation dialog", async function
QUnit.test('Activity view: many2one_avatar_user widget in activity view', async function (assert) {
assert.expect(3);
const taskModel = serverData.models.task;
serverData.models['res.users'] = {
fields: {
display_name: { string: "Displayed name", type: "char" },
avatar_128: { string: "Image 128", type: 'image' },
},
records: [{
id: 1,
display_name: "first user",
avatar_128: "Atmaram Bhide",
}],
};
taskModel.fields.user_id = { string: "Related User", type: "many2one", relation: 'res.users' };
taskModel.records[0].user_id = 1;
const [mailTestActivityId1] = pyEnv['mail.test.activity'].search([['name', '=', 'Meeting Room Furnitures']]);
const resUsersId1 = pyEnv['res.users'].create({
display_name: "first user",
avatar_128: "Atmaram Bhide",
});
pyEnv['mail.test.activity'].write([mailTestActivityId1], { activity_user_id: resUsersId1 });
Object.assign(serverData.views, {
'mail.test.activity,false,activity':
`<activity string="MailTestActivity">
<templates>
<div t-name="activity-box">
<field name="activity_user_id" widget="many2one_avatar_user"/>
<field name="name"/>
</div>
</templates>
</activity>`,
});
serverData.actions = {
1: {
id: 1,
name: 'Task Action',
res_model: 'task',
name: 'MailTestActivity Action',
res_model: 'mail.test.activity',
type: 'ir.actions.act_window',
views: [[false, 'activity']],
}
};
serverData.views = {
'task,false,activity': `
<activity string="Task">
<templates>
<div t-name="activity-box">
<field name="user_id" widget="many2one_avatar_user"/>
<field name="foo"/>
</div>
</templates>
</activity>`,
'task,false,search': '<search></search>'
};
const webClient = await createWebClient({ serverData, legacyParams: { withLegacyMockServer: true } });
const { widget: webClient } = await start({ hasWebClient: true, serverData, legacyParams: { withLegacyMockServer: true } });
await doAction(webClient, 1);
await legacyExtraNextTick();
assert.containsN(target, '.o_m2o_avatar', 2);
assert.containsOnce(target, 'tr[data-res-id=13] .o_m2o_avatar > img[data-src="/web/image/res.users/1/avatar_128"]',
assert.containsOnce(target, `tr[data-res-id=${mailTestActivityId1}] .o_m2o_avatar > img[data-src="/web/image/res.users/${resUsersId1}/avatar_128"]`,
"should have m2o avatar image");
assert.containsNone(target, '.o_m2o_avatar > span',
"should not have text on many2one_avatar_user if onlyImage node option is passed");
@@ -590,13 +516,14 @@ QUnit.test("Activity view: on_destroy_callback doesn't crash", async function (a
assert.expect(3);
const params = {
hasView: true,
View: ActivityView,
model: 'task',
model: 'mail.test.activity',
data: this.data,
arch: `<activity string="Task">
<templates>
<div t-name="activity-box">
<field name="foo"/>
<field name="name"/>
</div>
</templates>
</activity>`,
@@ -614,7 +541,7 @@ QUnit.test("Activity view: on_destroy_callback doesn't crash", async function (a
}
});
const activity = await createView(params);
const { widget: activity } = await start(params);
domUtils.detach([{ widget: activity }]);
assert.verifySteps([
@@ -627,21 +554,12 @@ QUnit.test("Activity view: on_destroy_callback doesn't crash", async function (a
QUnit.test("Schedule activity dialog uses the same search view as activity view", async function (assert) {
assert.expect(8);
serverData.models.task.records = [];
serverData.views = {
"task,false,activity": `
<activity string="Task">
<templates>
<div t-name="activity-box">
<field name="foo"/>
</div>
</templates>
</activity>
`,
"task,false,list": `<list><field name="foo"/></list>`,
"task,false,search": `<search/>`,
'task,1,search': `<search/>`,
};
pyEnv['mail.test.activity'].unlink(pyEnv['mail.test.activity'].search([]));
Object.assign(serverData.views, {
"mail.test.activity,false,list": `<list><field name="name"/></list>`,
"mail.test.activity,false,search": `<search/>`,
'mail.test.activity,1,search': `<search/>`,
});
function mockRPC(route, args) {
if (args.method === "load_views") {
@@ -649,12 +567,12 @@ QUnit.test("Schedule activity dialog uses the same search view as activity view"
}
}
const webClient = await createWebClient({ serverData, mockRPC, legacyParams: {withLegacyMockServer: true} });
const { widget: webClient } = await start({ hasWebClient: true, serverData, mockRPC, legacyParams: {withLegacyMockServer: true} });
// open an activity view (with default search arch)
await doAction(webClient, {
name: 'Dashboard',
res_model: 'task',
res_model: 'mail.test.activity',
type: 'ir.actions.act_window',
views: [[false, 'activity']],
});
@@ -673,7 +591,7 @@ QUnit.test("Schedule activity dialog uses the same search view as activity view"
// open an activity view (with search arch 1)
await doAction(webClient, {
name: 'Dashboard',
res_model: 'task',
res_model: 'mail.test.activity',
type: 'ir.actions.act_window',
views: [[false, 'activity']],
search_view_id: [1,"search"],
@@ -697,25 +615,14 @@ QUnit.test('Activity view: apply progressbar filter', async function (assert) {
serverData.actions = {
1: {
id: 1,
name: 'Task Action',
res_model: 'task',
name: 'MailTestActivity Action',
res_model: 'mail.test.activity',
type: 'ir.actions.act_window',
views: [[false, 'activity']],
}
};
serverData.views = {
'task,false,activity':
`<activity string="Task" >
<templates>
<div t-name="activity-box">
<field name="foo"/>
</div>
</templates>
</activity>`,
'task,false,search': '<search></search>',
};
const webClient = await createWebClient({ serverData, legacyParams: { withLegacyMockServer: true } });
const { widget: webClient } = await start({ hasWebClient:true, serverData, legacyParams: { withLegacyMockServer: true } });
await doAction(webClient, 1);