summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components/activity/activity_tests.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/mail/static/src/components/activity/activity_tests.js')
-rw-r--r--addons/mail/static/src/components/activity/activity_tests.js1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/activity/activity_tests.js b/addons/mail/static/src/components/activity/activity_tests.js
new file mode 100644
index 00000000..1c260f07
--- /dev/null
+++ b/addons/mail/static/src/components/activity/activity_tests.js
@@ -0,0 +1,1157 @@
+odoo.define('mail/static/src/components/activity/activity_tests.js', function (require) {
+'use strict';
+
+const components = {
+ Activity: require('mail/static/src/components/activity/activity.js'),
+};
+
+const {
+ afterEach,
+ afterNextRender,
+ beforeEach,
+ createRootComponent,
+ start,
+} = require('mail/static/src/utils/test_utils.js');
+const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
+
+const Bus = require('web.Bus');
+const { date_to_str } = require('web.time');
+
+const { Component, tags: { xml } } = owl;
+
+QUnit.module('mail', {}, function () {
+QUnit.module('components', {}, function () {
+QUnit.module('activity', {}, function () {
+QUnit.module('activity_tests.js', {
+ beforeEach() {
+ beforeEach(this);
+
+ this.createActivityComponent = async function (activity) {
+ await createRootComponent(this, components.Activity, {
+ props: { activityLocalId: activity.localId },
+ target: this.widget.el,
+ });
+ };
+
+ this.start = async params => {
+ const { env, widget } = await start(Object.assign({}, params, {
+ data: this.data,
+ }));
+ this.env = env;
+ this.widget = widget;
+ };
+ },
+ afterEach() {
+ afterEach(this);
+ },
+});
+
+QUnit.test('activity simplest layout', async function (assert) {
+ assert.expect(12);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_sidebar').length,
+ 1,
+ "should have activity sidebar"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_core').length,
+ 1,
+ "should have activity core"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_user').length,
+ 1,
+ "should have activity user"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_info').length,
+ 1,
+ "should have activity info"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_note').length,
+ 0,
+ "should not have activity note"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_details').length,
+ 0,
+ "should not have activity details"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_mailTemplates').length,
+ 0,
+ "should not have activity mail templates"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_editButton').length,
+ 0,
+ "should not have activity Edit button"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_cancelButton').length,
+ 0,
+ "should not have activity Cancel button"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_markDoneButton').length,
+ 0,
+ "should not have activity Mark as Done button"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_uploadButton').length,
+ 0,
+ "should not have activity Upload button"
+ );
+});
+
+QUnit.test('activity with note layout', async function (assert) {
+ assert.expect(3);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ note: 'There is no good or bad note',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_note').length,
+ 1,
+ "should have activity note"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_note').textContent,
+ "There is no good or bad note",
+ "activity note should be 'There is no good or bad note'"
+ );
+});
+
+QUnit.test('activity info layout when planned after tomorrow', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const today = new Date();
+ const fiveDaysFromNow = new Date();
+ fiveDaysFromNow.setDate(today.getDate() + 5);
+ const activity = this.env.models['mail.activity'].create({
+ dateDeadline: date_to_str(fiveDaysFromNow),
+ id: 12,
+ state: 'planned',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_dueDateText').length,
+ 1,
+ "should have activity delay"
+ );
+ assert.ok(
+ document.querySelector('.o_Activity_dueDateText').classList.contains('o-planned'),
+ "activity delay should have the right color modifier class (planned)"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_dueDateText').textContent,
+ "Due in 5 days:",
+ "activity delay should have 'Due in 5 days:' as label"
+ );
+});
+
+QUnit.test('activity info layout when planned tomorrow', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const today = new Date();
+ const tomorrow = new Date();
+ tomorrow.setDate(today.getDate() + 1);
+ const activity = this.env.models['mail.activity'].create({
+ dateDeadline: date_to_str(tomorrow),
+ id: 12,
+ state: 'planned',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_dueDateText').length,
+ 1,
+ "should have activity delay"
+ );
+ assert.ok(
+ document.querySelector('.o_Activity_dueDateText').classList.contains('o-planned'),
+ "activity delay should have the right color modifier class (planned)"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_dueDateText').textContent,
+ 'Tomorrow:',
+ "activity delay should have 'Tomorrow:' as label"
+ );
+});
+
+QUnit.test('activity info layout when planned today', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const today = new Date();
+ const activity = this.env.models['mail.activity'].create({
+ dateDeadline: date_to_str(today),
+ id: 12,
+ state: 'today',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_dueDateText').length,
+ 1,
+ "should have activity delay"
+ );
+ assert.ok(
+ document.querySelector('.o_Activity_dueDateText').classList.contains('o-today'),
+ "activity delay should have the right color modifier class (today)"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_dueDateText').textContent,
+ "Today:",
+ "activity delay should have 'Today:' as label"
+ );
+});
+
+QUnit.test('activity info layout when planned yesterday', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const today = new Date();
+ const yesterday = new Date();
+ yesterday.setDate(today.getDate() - 1);
+ const activity = this.env.models['mail.activity'].create({
+ dateDeadline: date_to_str(yesterday),
+ id: 12,
+ state: 'overdue',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_dueDateText').length,
+ 1,
+ "should have activity delay"
+ );
+ assert.ok(
+ document.querySelector('.o_Activity_dueDateText').classList.contains('o-overdue'),
+ "activity delay should have the right color modifier class (overdue)"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_dueDateText').textContent,
+ "Yesterday:",
+ "activity delay should have 'Yesterday:' as label"
+ );
+});
+
+QUnit.test('activity info layout when planned before yesterday', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const today = new Date();
+ const fiveDaysBeforeNow = new Date();
+ fiveDaysBeforeNow.setDate(today.getDate() - 5);
+ const activity = this.env.models['mail.activity'].create({
+ dateDeadline: date_to_str(fiveDaysBeforeNow),
+ id: 12,
+ state: 'overdue',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_dueDateText').length,
+ 1,
+ "should have activity delay"
+ );
+ assert.ok(
+ document.querySelector('.o_Activity_dueDateText').classList.contains('o-overdue'),
+ "activity delay should have the right color modifier class (overdue)"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_dueDateText').textContent,
+ "5 days overdue:",
+ "activity delay should have '5 days overdue:' as label"
+ );
+});
+
+QUnit.test('activity with a summary layout', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ summary: 'test summary',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_summary').length,
+ 1,
+ "should have activity summary"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_type').length,
+ 0,
+ "should not have the activity type as summary"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_summary').textContent.trim(),
+ "“test summary”",
+ "should have the specific activity summary in activity summary"
+ );
+});
+
+QUnit.test('activity without summary layout', async function (assert) {
+ assert.expect(5);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ type: [['insert', { id: 1, displayName: "Fake type" }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_type').length,
+ 1,
+ "activity details should have an activity type section"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_type').textContent.trim(),
+ "Fake type",
+ "activity details should have the activity type display name in type section"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_summary.o_Activity_type').length,
+ 1,
+ "should have activity type as summary"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_summary:not(.o_Activity_type)').length,
+ 0,
+ "should not have a specific summary"
+ );
+});
+
+QUnit.test('activity details toggle', async function (assert) {
+ assert.expect(5);
+
+ await this.start();
+ const today = new Date();
+ const tomorrow = new Date();
+ tomorrow.setDate(today.getDate() + 1);
+ const activity = this.env.models['mail.activity'].create({
+ creator: [['insert', { id: 1, display_name: "Admin" }]],
+ dateCreate: date_to_str(today),
+ dateDeadline: date_to_str(tomorrow),
+ id: 12,
+ state: 'planned',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ type: [['insert', { id: 1, displayName: "Fake type" }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_details').length,
+ 0,
+ "activity details should not be visible by default"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_detailsButton').length,
+ 1,
+ "activity should have a details button"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_Activity_detailsButton').click()
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_details').length,
+ 1,
+ "activity details should be visible after clicking on details button"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_Activity_detailsButton').click()
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_details').length,
+ 0,
+ "activity details should no longer be visible after clicking again on details button"
+ );
+});
+
+QUnit.test('activity details layout', async function (assert) {
+ assert.expect(11);
+
+ await this.start();
+ const today = new Date();
+ const tomorrow = new Date();
+ tomorrow.setDate(today.getDate() + 1);
+ const activity = this.env.models['mail.activity'].create({
+ assignee: [['insert', { id: 10, display_name: "Pauvre pomme" }]],
+ creator: [['insert', { id: 1, display_name: "Admin" }]],
+ dateCreate: date_to_str(today),
+ dateDeadline: date_to_str(tomorrow),
+ id: 12,
+ state: 'planned',
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ type: [['insert', { id: 1, displayName: "Fake type" }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_userAvatar').length,
+ 1,
+ "should have activity user avatar"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_detailsButton').length,
+ 1,
+ "activity should have a details button"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_Activity_detailsButton').click()
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_details').length,
+ 1,
+ "activity details should be visible after clicking on details button"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_details .o_Activity_type').length,
+ 1,
+ "activity details should have type"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_details .o_Activity_type').textContent,
+ "Fake type",
+ "activity details type should be 'Fake type'"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_detailsCreation').length,
+ 1,
+ "activity details should have creation date "
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_detailsCreator').length,
+ 1,
+ "activity details should have creator"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_detailsAssignation').length,
+ 1,
+ "activity details should have assignation information"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_Activity_detailsAssignation').textContent.indexOf('Pauvre pomme'),
+ 0,
+ "activity details assignation information should contain creator display name"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_detailsAssignationUserAvatar').length,
+ 1,
+ "activity details should have user avatar"
+ );
+});
+
+QUnit.test('activity with mail template layout', async function (assert) {
+ assert.expect(8);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ mailTemplates: [['insert', { id: 1, name: "Dummy mail template" }]],
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_sidebar').length,
+ 1,
+ "should have activity sidebar"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_mailTemplates').length,
+ 1,
+ "should have activity mail templates"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_mailTemplate').length,
+ 1,
+ "should have activity mail template"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_MailTemplate_name').length,
+ 1,
+ "should have activity mail template name"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_MailTemplate_name').textContent,
+ "Dummy mail template",
+ "should have activity mail template name"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_MailTemplate_preview').length,
+ 1,
+ "should have activity mail template name preview button"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_MailTemplate_send').length,
+ 1,
+ "should have activity mail template name send button"
+ );
+});
+
+QUnit.test('activity with mail template: preview mail', async function (assert) {
+ assert.expect(10);
+
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.step('do_action');
+ assert.strictEqual(
+ payload.action.context.default_res_id,
+ 42,
+ 'Action should have the activity res id as default res id in context'
+ );
+ assert.strictEqual(
+ payload.action.context.default_model,
+ 'res.partner',
+ 'Action should have the activity res model as default model in context'
+ );
+ assert.ok(
+ payload.action.context.default_use_template,
+ 'Action should have true as default use_template in context'
+ );
+ assert.strictEqual(
+ payload.action.context.default_template_id,
+ 1,
+ 'Action should have the selected mail template id as default template id in context'
+ );
+ assert.strictEqual(
+ payload.action.type,
+ "ir.actions.act_window",
+ 'Action should be of type "ir.actions.act_window"'
+ );
+ assert.strictEqual(
+ payload.action.res_model,
+ "mail.compose.message",
+ 'Action should have "mail.compose.message" as res_model'
+ );
+ });
+
+ await this.start({ env: { bus } });
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ mailTemplates: [['insert', {
+ id: 1,
+ name: "Dummy mail template",
+ }]],
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_MailTemplate_preview').length,
+ 1,
+ "should have activity mail template name preview button"
+ );
+
+ document.querySelector('.o_MailTemplate_preview').click();
+ assert.verifySteps(
+ ['do_action'],
+ "should have called 'compose email' action correctly"
+ );
+});
+
+QUnit.test('activity with mail template: send mail', async function (assert) {
+ assert.expect(7);
+
+ await this.start({
+ async mockRPC(route, args) {
+ if (args.method === 'activity_send_mail') {
+ assert.step('activity_send_mail');
+ assert.strictEqual(args.args[0].length, 1);
+ assert.strictEqual(args.args[0][0], 42);
+ assert.strictEqual(args.args[1], 1);
+ return;
+ } else {
+ return this._super(...arguments);
+ }
+ },
+ });
+ const activity = this.env.models['mail.activity'].create({
+ id: 12,
+ mailTemplates: [['insert', {
+ id: 1,
+ name: "Dummy mail template",
+ }]],
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_MailTemplate_send').length,
+ 1,
+ "should have activity mail template name send button"
+ );
+
+ document.querySelector('.o_MailTemplate_send').click();
+ assert.verifySteps(
+ ['activity_send_mail'],
+ "should have called activity_send_mail rpc"
+ );
+});
+
+QUnit.test('activity upload document is available', async function (assert) {
+ assert.expect(3);
+
+ await this.start();
+ const today = new Date();
+ const tomorrow = new Date();
+ tomorrow.setDate(today.getDate() + 1);
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ category: 'upload_file',
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_uploadButton').length,
+ 1,
+ "should have activity upload button"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_FileUploader').length,
+ 1,
+ "should have a file uploader"
+ );
+});
+
+QUnit.test('activity click on mark as done', async function (assert) {
+ assert.expect(4);
+
+ await this.start();
+ const today = new Date();
+ const tomorrow = new Date();
+ tomorrow.setDate(today.getDate() + 1);
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ category: 'not_upload_file',
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_markDoneButton').length,
+ 1,
+ "should have activity Mark as Done button"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Activity_markDoneButton').click();
+ });
+ assert.strictEqual(
+ document.querySelectorAll('.o_ActivityMarkDonePopover').length,
+ 1,
+ "should have opened the mark done popover"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Activity_markDoneButton').click();
+ });
+ assert.strictEqual(
+ document.querySelectorAll('.o_ActivityMarkDonePopover').length,
+ 0,
+ "should have closed the mark done popover"
+ );
+});
+
+QUnit.test('activity mark as done popover should focus feedback input on open [REQUIRE FOCUS]', async function (assert) {
+ assert.expect(3);
+
+ await this.start();
+ const today = new Date();
+ const tomorrow = new Date();
+ tomorrow.setDate(today.getDate() + 1);
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ category: 'not_upload_file',
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+
+ assert.containsOnce(
+ document.body,
+ '.o_Activity',
+ "should have activity component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Activity_markDoneButton',
+ "should have activity Mark as Done button"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Activity_markDoneButton').click();
+ });
+ assert.strictEqual(
+ document.querySelector('.o_ActivityMarkDonePopover_feedback'),
+ document.activeElement,
+ "the popover textarea should have the focus"
+ );
+});
+
+QUnit.test('activity click on edit', async function (assert) {
+ assert.expect(9);
+
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.step('do_action');
+ assert.strictEqual(
+ payload.action.context.default_res_id,
+ 42,
+ 'Action should have the activity res id as default res id in context'
+ );
+ assert.strictEqual(
+ payload.action.context.default_res_model,
+ 'res.partner',
+ 'Action should have the activity res model as default res model in context'
+ );
+ assert.strictEqual(
+ payload.action.type,
+ "ir.actions.act_window",
+ 'Action should be of type "ir.actions.act_window"'
+ );
+ assert.strictEqual(
+ payload.action.res_model,
+ "mail.activity",
+ 'Action should have "mail.activity" as res_model'
+ );
+ assert.strictEqual(
+ payload.action.res_id,
+ 12,
+ 'Action should have activity id as res_id'
+ );
+ });
+
+ await this.start({ env: { bus } });
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ id: 12,
+ mailTemplates: [['insert', { id: 1, name: "Dummy mail template" }]],
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_editButton').length,
+ 1,
+ "should have activity edit button"
+ );
+
+ document.querySelector('.o_Activity_editButton').click();
+ assert.verifySteps(
+ ['do_action'],
+ "should have called 'schedule activity' action correctly"
+ );
+});
+
+QUnit.test('activity edition', async function (assert) {
+ assert.expect(14);
+
+ this.data['mail.activity'].records.push({
+ can_write: true,
+ icon: 'fa-times',
+ id: 12,
+ res_id: 42,
+ res_model: 'res.partner',
+ });
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.step('do_action');
+ assert.strictEqual(
+ payload.action.context.default_res_id,
+ 42,
+ 'Action should have the activity res id as default res id in context'
+ );
+ assert.strictEqual(
+ payload.action.context.default_res_model,
+ 'res.partner',
+ 'Action should have the activity res model as default res model in context'
+ );
+ assert.strictEqual(
+ payload.action.type,
+ 'ir.actions.act_window',
+ 'Action should be of type "ir.actions.act_window"'
+ );
+ assert.strictEqual(
+ payload.action.res_model,
+ 'mail.activity',
+ 'Action should have "mail.activity" as res_model'
+ );
+ assert.strictEqual(
+ payload.action.res_id,
+ 12,
+ 'Action should have activity id as res_id'
+ );
+ this.data['mail.activity'].records[0].icon = 'fa-check';
+ payload.options.on_close();
+ });
+
+ await this.start({ env: { bus } });
+ const activity = this.env.models['mail.activity'].insert(
+ this.env.models['mail.activity'].convertData(
+ this.data['mail.activity'].records[0]
+ )
+ );
+ await this.createActivityComponent(activity);
+
+ assert.containsOnce(
+ document.body,
+ '.o_Activity',
+ "should have activity component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Activity_editButton',
+ "should have activity edit button"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Activity_icon',
+ "should have activity icon"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Activity_icon.fa-times',
+ "should have initial activity icon"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_Activity_icon.fa-check',
+ "should not have new activity icon when not edited yet"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Activity_editButton').click();
+ });
+ assert.verifySteps(
+ ['do_action'],
+ "should have called 'schedule activity' action correctly"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_Activity_icon.fa-times',
+ "should no more have initial activity icon once edited"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Activity_icon.fa-check',
+ "should now have new activity icon once edited"
+ );
+});
+
+QUnit.test('activity click on cancel', async function (assert) {
+ assert.expect(7);
+
+ await this.start({
+ async mockRPC(route, args) {
+ if (route === '/web/dataset/call_kw/mail.activity/unlink') {
+ assert.step('unlink');
+ assert.strictEqual(args.args[0].length, 1);
+ assert.strictEqual(args.args[0][0], 12);
+ return;
+ } else {
+ return this._super(...arguments);
+ }
+ },
+ });
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ id: 12,
+ mailTemplates: [['insert', {
+ id: 1,
+ name: "Dummy mail template",
+ }]],
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+
+ // Create a parent component to surround the Activity component in order to be able
+ // to check that activity component has been destroyed
+ class ParentComponent extends Component {
+ constructor(...args) {
+ super(... args);
+ useStore(props => {
+ const activity = this.env.models['mail.activity'].get(props.activityLocalId);
+ return {
+ activity: activity ? activity.__state : undefined,
+ };
+ });
+ }
+
+ /**
+ * @returns {mail.activity}
+ */
+ get activity() {
+ return this.env.models['mail.activity'].get(this.props.activityLocalId);
+ }
+ }
+ ParentComponent.env = this.env;
+ Object.assign(ParentComponent, {
+ components,
+ props: { activityLocalId: String },
+ template: xml`
+ <div>
+ <p>parent</p>
+ <t t-if="activity">
+ <Activity activityLocalId="activity.localId"/>
+ </t>
+ </div>
+ `,
+ });
+ await createRootComponent(this, ParentComponent, {
+ props: { activityLocalId: activity.localId },
+ target: this.widget.el,
+ });
+
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 1,
+ "should have activity component"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity_cancelButton').length,
+ 1,
+ "should have activity cancel button"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_Activity_cancelButton').click()
+ );
+ assert.verifySteps(
+ ['unlink'],
+ "should have called unlink rpc after clicking on cancel"
+ );
+ assert.strictEqual(
+ document.querySelectorAll('.o_Activity').length,
+ 0,
+ "should no longer display activity after clicking on cancel"
+ );
+});
+
+QUnit.test('activity mark done popover close on ESCAPE', async function (assert) {
+ // This test is not in activity_mark_done_popover_tests.js as it requires the activity mark done
+ // component to have a parent in order to allow testing interactions the popover.
+ assert.expect(2);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ category: 'not_upload_file',
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+
+ await this.createActivityComponent(activity);
+ await afterNextRender(() => {
+ document.querySelector('.o_Activity_markDoneButton').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_ActivityMarkDonePopover',
+ "Popover component should be present"
+ );
+
+ await afterNextRender(() => {
+ const ev = new window.KeyboardEvent('keydown', { bubbles: true, key: "Escape" });
+ document.querySelector(`.o_ActivityMarkDonePopover`).dispatchEvent(ev);
+ });
+ assert.containsNone(
+ document.body,
+ '.o_ActivityMarkDonePopover',
+ "ESCAPE pressed should have closed the mark done popover"
+ );
+});
+
+QUnit.test('activity mark done popover click on discard', async function (assert) {
+ // This test is not in activity_mark_done_popover_tests.js as it requires the activity mark done
+ // component to have a parent in order to allow testing interactions the popover.
+ assert.expect(3);
+
+ await this.start();
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ category: 'not_upload_file',
+ id: 12,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ await afterNextRender(() => {
+ document.querySelector('.o_Activity_markDoneButton').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_ActivityMarkDonePopover',
+ "Popover component should be present"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ActivityMarkDonePopover_discardButton',
+ "Popover component should contain the discard button"
+ );
+ await afterNextRender(() =>
+ document.querySelector('.o_ActivityMarkDonePopover_discardButton').click()
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ActivityMarkDonePopover',
+ "Discard button clicked should have closed the mark done popover"
+ );
+});
+
+QUnit.test('data-oe-id & data-oe-model link redirection on click', async function (assert) {
+ assert.expect(7);
+
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.strictEqual(
+ payload.action.type,
+ 'ir.actions.act_window',
+ "action should open view"
+ );
+ assert.strictEqual(
+ payload.action.res_model,
+ 'some.model',
+ "action should open view on 'some.model' model"
+ );
+ assert.strictEqual(
+ payload.action.res_id,
+ 250,
+ "action should open view on 250"
+ );
+ assert.step('do-action:openFormView_some.model_250');
+ });
+ await this.start({ env: { bus } });
+ const activity = this.env.models['mail.activity'].create({
+ canWrite: true,
+ category: 'not_upload_file',
+ id: 12,
+ note: `<p><a href="#" data-oe-id="250" data-oe-model="some.model">some.model_250</a></p>`,
+ thread: [['insert', { id: 42, model: 'res.partner' }]],
+ });
+ await this.createActivityComponent(activity);
+ assert.containsOnce(
+ document.body,
+ '.o_Activity_note',
+ "activity should have a note"
+ );
+ assert.containsOnce(
+ document.querySelector('.o_Activity_note'),
+ 'a',
+ "activity note should have a link"
+ );
+
+ document.querySelector(`.o_Activity_note a`).click();
+ assert.verifySteps(
+ ['do-action:openFormView_some.model_250'],
+ "should have open form view on related record after click on link"
+ );
+});
+
+});
+});
+});
+
+});