summaryrefslogtreecommitdiff
path: root/addons/mail/static/tests/activity_tests.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/tests/activity_tests.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/static/tests/activity_tests.js')
-rw-r--r--addons/mail/static/tests/activity_tests.js562
1 files changed, 562 insertions, 0 deletions
diff --git a/addons/mail/static/tests/activity_tests.js b/addons/mail/static/tests/activity_tests.js
new file mode 100644
index 00000000..384815c2
--- /dev/null
+++ b/addons/mail/static/tests/activity_tests.js
@@ -0,0 +1,562 @@
+odoo.define('mail.activity_view_tests', function (require) {
+'use strict';
+
+var ActivityView = require('mail.ActivityView');
+var testUtils = require('web.test_utils');
+const ActivityRenderer = require('mail.ActivityRenderer');
+const domUtils = require('web.dom');
+
+var createActionManager = testUtils.createActionManager;
+
+var createView = testUtils.createView;
+
+QUnit.module('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]},
+ ],
+ },
+ partner: {
+ fields: {
+ display_name: { string: "Displayed name", type: "char" },
+ },
+ records: [{
+ id: 2,
+ display_name: "first partner",
+ }]
+ },
+ '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,
+ }
+ ],
+ },
+ '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" },
+ ],
+ },
+ };
+ }
+});
+
+var activityDateFormat = function (date) {
+ return date.toLocaleDateString(moment().locale(), { day: 'numeric', month: 'short' });
+};
+
+QUnit.test('activity view: simple activity rendering', async function (assert) {
+ assert.expect(14);
+ var activity = await createView({
+ View: ActivityView,
+ model: 'task',
+ data: this.data,
+ arch: '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</div>' +
+ '</templates>' +
+ '</activity>',
+ intercepts: {
+ do_action: function (event) {
+ assert.deepEqual(event.data.action, {
+ context: {
+ default_res_id: 30,
+ default_res_model: "task",
+ default_activity_type_id: 3,
+ },
+ res_id: false,
+ res_model: "mail.activity",
+ target: "new",
+ type: "ir.actions.act_window",
+ view_mode: "form",
+ view_type: "form",
+ views: [[false, "form"]]
+ },
+ "should do a do_action with correct parameters");
+ event.data.options.on_close();
+ },
+ },
+ });
+
+ assert.containsOnce(activity, 'table',
+ 'should have a table');
+ var $th1 = activity.$('table thead tr:first th:nth-child(2)');
+ assert.containsOnce($th1, 'span:first:contains(Email)', 'should contain "Email" in header of first column');
+ assert.containsOnce($th1, '.o_kanban_counter', 'should contain a progressbar in header of first column');
+ assert.hasAttrValue($th1.find('.o_kanban_counter_progress .progress-bar:first'), 'data-original-title', '1 Planned',
+ 'the counter progressbars should be correctly displayed');
+ assert.hasAttrValue($th1.find('.o_kanban_counter_progress .progress-bar:nth-child(2)'), 'data-original-title', '1 Today',
+ 'the counter progressbars should be correctly displayed');
+ var $th2 = activity.$('table thead tr:first th:nth-child(3)');
+ assert.containsOnce($th2, 'span:first:contains(Call)', 'should contain "Call" in header of second column');
+ assert.hasAttrValue($th2.find('.o_kanban_counter_progress .progress-bar:nth-child(3)'), 'data-original-title', '1 Overdue',
+ 'the counter progressbars should be correctly displayed');
+ assert.containsNone(activity, 'table thead tr:first th:nth-child(4) .o_kanban_counter',
+ 'should not contain a progressbar in header of 3rd column');
+ assert.ok(activity.$('table tbody tr:first td:first:contains(Office planning)').length,
+ 'should contain "Office planning" in first colum of first row');
+ assert.ok(activity.$('table tbody tr:nth-child(2) td:first:contains(Meeting Room Furnitures)').length,
+ 'should contain "Meeting Room Furnitures" in first colum of second row');
+
+ var today = activityDateFormat(new Date());
+
+ assert.ok(activity.$('table tbody tr:first td:nth-child(2).today .o_closest_deadline:contains(' + today + ')').length,
+ 'should contain an activity for today in second cell of first line ' + today);
+ var td = 'table tbody tr:nth-child(1) td.o_activity_empty_cell';
+ assert.containsN(activity, td, 2, 'should contain an empty cell as no activity scheduled yet.');
+
+ // schedule an activity (this triggers a do_action)
+ 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 = [];
+
+ var activity = await createView({
+ View: ActivityView,
+ model: 'task',
+ data: this.data,
+ arch: '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</div>' +
+ '</templates>' +
+ '</activity>',
+ });
+
+ assert.containsOnce(activity, '.o_view_nocontent',
+ "should display the no content helper");
+ 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({
+ View: ActivityView,
+ model: 'task',
+ data: this.data,
+ arch: '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</div>' +
+ '</templates>' +
+ '</activity>',
+ mockRPC: function(route, args) {
+ if (args.method === 'activity_send_mail'){
+ assert.step(JSON.stringify(args.args));
+ return Promise.resolve();
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+ assert.notOk(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show').length,
+ 'dropdown shouldn\'t be displayed');
+
+ testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) i.fa-ellipsis-v'));
+ assert.ok(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show').length,
+ 'dropdown should have appeared');
+
+ testUtils.dom.click(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show .o_send_mail_template:contains(Template2)'));
+ assert.notOk(activity.$('table thead tr:first th:nth-child(2) span:nth-child(2) .dropdown-menu.show').length,
+ 'dropdown shouldn\'t be displayed');
+
+ 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
+ ]);
+
+ activity.destroy();
+});
+
+QUnit.test('activity view: activity widget', async function (assert) {
+ assert.expect(16);
+
+ const params = {
+ View: ActivityView,
+ model: 'task',
+ data: this.data,
+ arch: '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</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.step('activity_send_mail');
+ return Promise.resolve();
+ }
+ if (args.method === 'action_feedback_schedule_next'){
+ assert.deepEqual([[3]],args.args, "Should execute action_feedback_schedule_next on activity 3 only ");
+ assert.equal(args.kwargs.feedback, "feedback2");
+ assert.step('action_feedback_schedule_next');
+ return Promise.resolve({serverGeneratedAction: true});
+ }
+ return this._super.apply(this, arguments);
+ },
+ intercepts: {
+ do_action: function (ev) {
+ var action = ev.data.action;
+ if (action.serverGeneratedAction) {
+ 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_use_template: true,
+ force_email: true
+ }, action.context);
+ assert.step("do_action_compose");
+ } else if (action.res_model === 'mail.activity') {
+ assert.deepEqual({
+ "default_res_id": 30,
+ "default_res_model": "task"
+ }, action.context);
+ assert.step("do_action_activity");
+ } else {
+ assert.step("Unexpected action");
+ }
+ },
+ },
+ };
+
+ var activity = await createView(params);
+ var today = activity.$('table tbody tr:first td:nth-child(2).today');
+ var dropdown = today.find('.dropdown-menu.o_activity');
+
+ await testUtils.dom.click(today.find('.o_closest_deadline'));
+ assert.hasClass(dropdown,'show', "dropdown should be displayed");
+ assert.ok(dropdown.find('.o_activity_color_today:contains(Today)').length, "Title should be today");
+ assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first div:contains(template8)').length,
+ "template8 should be available");
+ assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:eq(1) div:contains(template9)').length,
+ "template9 should be available");
+
+ await testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_preview'));
+ await testUtils.dom.click(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_send'));
+ var overdue = activity.$('table tbody tr:first td:nth-child(3).overdue');
+ await testUtils.dom.click(overdue.find('.o_closest_deadline'));
+ dropdown = overdue.find('.dropdown-menu.o_activity');
+ assert.notOk(dropdown.find('.o_activity_title div div div:first span').length,
+ "No template should be available");
+
+ await testUtils.dom.click(dropdown.find('.o_schedule_activity'));
+ await testUtils.dom.click(overdue.find('.o_closest_deadline'));
+ await testUtils.dom.click(dropdown.find('.o_mark_as_done'));
+ dropdown.find('#activity_feedback').val("feedback2");
+
+ await testUtils.dom.click(dropdown.find('.o_activity_popover_done_next'));
+ assert.verifySteps([
+ "do_action_compose",
+ "activity_send_mail",
+ "do_action_activity",
+ "action_feedback_schedule_next",
+ "serverGeneratedAction"
+ ]);
+
+ activity.destroy();
+});
+QUnit.test('activity view: no group_by_menu and no comparison_menu', async function (assert) {
+ assert.expect(4);
+
+ var actionManager = await createActionManager({
+ actions: [{
+ id: 1,
+ name: 'Task Action',
+ res_model: 'task',
+ type: 'ir.actions.act_window',
+ views: [[false, 'activity']],
+ }],
+ archs: {
+ 'task,false,activity': '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</div>' +
+ '</templates>' +
+ '</activity>',
+ 'task,false,search': '<search></search>',
+ },
+ data: this.data,
+ session: {
+ user_context: {lang: 'zz_ZZ'},
+ },
+ mockRPC: function(route, args) {
+ if (args.method === 'get_activity_data') {
+ assert.deepEqual(args.kwargs.context, {lang: 'zz_ZZ'},
+ 'The context should have been passed');
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ await actionManager.doAction(1);
+
+ assert.containsN(actionManager, '.o_search_options .o_dropdown button:visible', 2,
+ "only two elements should be available in view search");
+ assert.isVisible(actionManager.$('.o_search_options .o_dropdown.o_filter_menu > button'),
+ "filter should be available in view search");
+ assert.isVisible(actionManager.$('.o_search_options .o_dropdown.o_favorite_menu > button'),
+ "favorites should be available in view search");
+ actionManager.destroy();
+});
+
+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" },
+ });
+ this.data.task.records[2] = { id: 31, name: "Task 3" };
+ var activity = await createView({
+ View: ActivityView,
+ model: 'task',
+ data: this.data,
+ arch: '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</div>' +
+ '</templates>' +
+ '</activity>',
+ archs: {
+ "task,false,list": '<tree string="Task"><field name="name"/></tree>',
+ "task,false,search": '<search></search>',
+ },
+ mockRPC: function(route, args) {
+ if (args.method === 'name_search') {
+ args.kwargs.name = "Task";
+ }
+ return this._super.apply(this, arguments);
+ },
+ intercepts: {
+ do_action: function (ev) {
+ assert.step('doAction');
+ var expectedAction = {
+ context: {
+ default_res_id: { id: 31, display_name: undefined },
+ default_res_model: "task",
+ },
+ name: "Schedule Activity",
+ res_id: false,
+ res_model: "mail.activity",
+ target: "new",
+ type: "ir.actions.act_window",
+ view_mode: "form",
+ views: [[false, "form"]],
+ };
+ assert.deepEqual(ev.data.action, expectedAction,
+ "should execute an action with correct params");
+ ev.data.options.on_close();
+ },
+ },
+ });
+
+ assert.containsOnce(activity, 'table tfoot tr .o_record_selector',
+ 'should contain search more selector to choose the record to schedule an activity for it');
+ await testUtils.dom.click(activity.$('table tfoot tr .o_record_selector'));
+ // search create dialog
+ var $modal = $('.modal-lg');
+ assert.strictEqual($modal.find('.o_data_row').length, 3, "all tasks should be available to select");
+ // select a record to schedule an activity for it (this triggers a do_action)
+ testUtils.dom.click($modal.find('.o_data_row:last'));
+ assert.verifySteps(['doAction']);
+
+ activity.destroy();
+});
+
+QUnit.test('Activity view: discard an activity creation dialog', async function (assert) {
+ assert.expect(2);
+
+ var actionManager = await createActionManager({
+ actions: [{
+ id: 1,
+ name: 'Task Action',
+ res_model: 'task',
+ type: 'ir.actions.act_window',
+ views: [[false, 'activity']],
+ }],
+ archs: {
+ '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>`
+ },
+ data: this.data,
+ intercepts: {
+ do_action(ev) {
+ actionManager.doAction(ev.data.action, ev.data.options);
+ }
+ },
+ async mockRPC(route, args) {
+ if (args.method === 'check_access_rights') {
+ return true;
+ }
+ return this._super(...arguments);
+ },
+ });
+ await actionManager.doAction(1);
+
+ await testUtils.dom.click(actionManager.$('.o_activity_view .o_data_row .o_activity_empty_cell')[0]);
+ assert.containsOnce(
+ $,
+ '.modal.o_technical_modal.show',
+ "Activity Modal should be opened");
+
+ await testUtils.dom.click($('.modal.o_technical_modal.show button[special="cancel"]'));
+ assert.containsNone(
+ $,
+ '.modal.o_technical_modal.show',
+ "Activity Modal should be closed");
+
+ actionManager.destroy();
+});
+
+QUnit.test("Activity view: on_destroy_callback doesn't crash", async function (assert) {
+ assert.expect(3);
+
+ const params = {
+ View: ActivityView,
+ model: 'task',
+ data: this.data,
+ arch: '<activity string="Task">' +
+ '<templates>' +
+ '<div t-name="activity-box">' +
+ '<field name="foo"/>' +
+ '</div>' +
+ '</templates>'+
+ '</activity>',
+ };
+
+ ActivityRenderer.patch('test_mounted_unmounted', T =>
+ class extends T {
+ mounted() {
+ assert.step('mounted');
+ }
+ willUnmount() {
+ assert.step('willUnmount');
+ }
+ });
+
+ const activity = await createView(params);
+ domUtils.detach([{widget: activity}]);
+
+ assert.verifySteps([
+ 'mounted',
+ 'willUnmount'
+ ]);
+
+ ActivityRenderer.unpatch('test_mounted_unmounted');
+ activity.destroy();
+});
+
+});
+});