[MERGE] mail: fine-tune activity view improvement
[IMP] mail: add/modify activity client tests We add a test that checks the rendering of the activity view and the activity popover with activities done. We also add a test that marks as done an activity and verify that the activity view is updated accordingly. And we do a correction in the test "activity menu widget: activity menu with 2 models" (assert.ok(selectContaining... was always true as an array is always returned and [] is true in javascript). [FIX] mail: fix invalid date done in the activity list popover Date done field was in camel case instead of snake case leading to undefined value and "Invalid date" in the interface. Task-3558929 closes odoo/odoo#139373 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -111,7 +111,7 @@ export class ActivityListPopoverItem extends Component {
|
||||
}
|
||||
|
||||
get dateDoneFormatted() {
|
||||
return luxon.DateTime.fromISO(this.props.activity.dateDone).toLocaleString(
|
||||
return luxon.DateTime.fromISO(this.props.activity.date_done).toLocaleString(
|
||||
luxon.DateTime.DATE_SHORT
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Domain } from "@web/core/domain";
|
||||
import { groupBy, sortBy, unique } from "@web/core/utils/arrays";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
import { deserializeDate } from "@web/core/l10n/dates";
|
||||
import { deserializeDate, serializeDate, today } from "@web/core/l10n/dates";
|
||||
|
||||
const { DateTime} = luxon;
|
||||
|
||||
@@ -84,8 +84,7 @@ patch(MockServer.prototype, {
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockMailActivityActionDone(ids) {
|
||||
const activities = this.getRecords("mail.activity", [["id", "in", ids]]);
|
||||
this.mockUnlink("mail.activity", [activities.map((activity) => activity.id)]);
|
||||
this._mockMailActivityActionFeedback(ids);
|
||||
},
|
||||
/**
|
||||
* Simulates `action_feedback` on `mail.activity`.
|
||||
@@ -96,7 +95,24 @@ patch(MockServer.prototype, {
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockMailActivityActionFeedback(ids, attachment_ids= null) {
|
||||
this._mockMailActivityActionDone(ids);
|
||||
const activities = this.getRecords("mail.activity", [["id", "in", ids]]);
|
||||
const activityTypes = this.getRecords("mail.activity.type", [
|
||||
["id", "in", unique(activities.map((a) => a.activity_type_id))],
|
||||
]);
|
||||
const activityTypeById = Object.fromEntries(
|
||||
activityTypes.map((actType) => [actType.id, actType])
|
||||
);
|
||||
this.mockWrite("mail.activity", [
|
||||
activities
|
||||
.filter((act) => activityTypeById[act.activity_type_id].keep_done)
|
||||
.map((act) => act.id),
|
||||
{ active: false, date_done: serializeDate(today()), state: "done" },
|
||||
]);
|
||||
this.mockUnlink("mail.activity", [
|
||||
activities
|
||||
.filter((act) => !activityTypeById[act.activity_type_id].keep_done)
|
||||
.map((act) => act.id),
|
||||
]);
|
||||
},
|
||||
/**
|
||||
* Simulates `action_feedback_schedule_next` on `mail.activity`.
|
||||
|
||||
@@ -241,6 +241,162 @@ QUnit.module("test_mail", {}, function () {
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("activity view: Activity rendering with done activities", async function (assert) {
|
||||
function containsN(domElement, selector, containings) {
|
||||
return Array.from(domElement.querySelectorAll(selector)).filter((sel) =>
|
||||
containings.every((containing) => sel.textContent.includes(containing))
|
||||
);
|
||||
}
|
||||
|
||||
const activityTypeUpload = pyEnv["mail.activity.type"].create({
|
||||
category: "upload_file",
|
||||
name: "Test Upload document",
|
||||
keep_done: true,
|
||||
});
|
||||
pyEnv["mail.activity"].create(
|
||||
Object.entries(["done", "done", "done", "done", "planned", "planned", "planned"]).map(
|
||||
([idx, state]) => ({
|
||||
active: state !== "done",
|
||||
activity_type_id: activityTypeUpload,
|
||||
attachment_ids:
|
||||
state === "done"
|
||||
? [
|
||||
pyEnv["ir.attachment"].create({
|
||||
name: `attachment ${idx}`,
|
||||
create_date: serializeDate(
|
||||
DateTime.now().minus({ days: idx })
|
||||
),
|
||||
create_uid: pyEnv.currentUserId,
|
||||
}),
|
||||
]
|
||||
: [],
|
||||
can_write: true,
|
||||
date_deadline: serializeDate(DateTime.now().plus({ days: idx })),
|
||||
date_done:
|
||||
state === "done"
|
||||
? serializeDate(DateTime.now().minus({ days: idx }))
|
||||
: false,
|
||||
display_name: `Upload folders ${idx}`,
|
||||
state: state,
|
||||
user_id: pyEnv["res.users"].create({ display_name: `user${idx}` }),
|
||||
})
|
||||
)
|
||||
);
|
||||
const [meetingRecord, officeRecord] = pyEnv["mail.test.activity"].search([]);
|
||||
const uploadDoneActs = pyEnv["mail.activity"].searchRead([
|
||||
["activity_type_id", "=", activityTypeUpload],
|
||||
["active", "=", false],
|
||||
]);
|
||||
const uploadPlannedActs = pyEnv["mail.activity"].searchRead([
|
||||
["activity_type_id", "=", activityTypeUpload],
|
||||
]);
|
||||
pyEnv["mail.test.activity"].write([meetingRecord], {
|
||||
activity_ids: [
|
||||
uploadPlannedActs[0].id,
|
||||
uploadPlannedActs[1].id,
|
||||
uploadPlannedActs[2].id,
|
||||
uploadDoneActs[0].id,
|
||||
],
|
||||
});
|
||||
pyEnv["mail.test.activity"].write([officeRecord], {
|
||||
activity_ids: [uploadDoneActs[1].id, uploadDoneActs[2].id, uploadDoneActs[3].id],
|
||||
});
|
||||
const { openView } = await start({
|
||||
serverData,
|
||||
});
|
||||
await openView({
|
||||
res_model: "mail.test.activity",
|
||||
views: [[false, "activity"]],
|
||||
});
|
||||
const domActivity = document.querySelector(".o_activity_view");
|
||||
const domHeaderUpload = domActivity.querySelector(
|
||||
"table thead tr:first-child th:nth-child(6)"
|
||||
);
|
||||
const selRowMeetingCellUpload = "table tbody tr:first-child td:nth-child(6)";
|
||||
const domRowMeetingCellUpload = domActivity.querySelector(selRowMeetingCellUpload);
|
||||
const selRowOfficeCellUpload = "table tbody tr:nth-child(2) td:nth-child(6)";
|
||||
const domRowOfficeCellUpload = domActivity.querySelector(selRowOfficeCellUpload);
|
||||
|
||||
// Headers
|
||||
await contains(".o_column_progress .progress-bar:first-child[data-tooltip='3 Planned']", {
|
||||
target: domHeaderUpload,
|
||||
});
|
||||
await contains(".o_animated_number", {
|
||||
target: domHeaderUpload,
|
||||
text: "3",
|
||||
});
|
||||
await contains(".o_column_progress_aggregated_on", {
|
||||
target: domHeaderUpload,
|
||||
text: "7",
|
||||
});
|
||||
// Cells avatars
|
||||
await contains(
|
||||
`.o-mail-Avatar img[data-src='/web/image/res.users/${uploadPlannedActs[0].user_id[0]}/avatar_128'`,
|
||||
{ target: domRowMeetingCellUpload }
|
||||
);
|
||||
await contains(
|
||||
`.o-mail-Avatar img[data-src='/web/image/res.users/${uploadPlannedActs[1].user_id[0]}/avatar_128'`,
|
||||
{ target: domRowMeetingCellUpload }
|
||||
);
|
||||
await contains(
|
||||
`.o-mail-Avatar img[data-src='/web/image/res.users/${uploadPlannedActs[2].user_id[0]}/avatar_128'`,
|
||||
{ target: domRowMeetingCellUpload, count: 0 }
|
||||
);
|
||||
await contains(`.o-mail-Avatar`, { target: domRowOfficeCellUpload, count: 0 }); // all activity are done
|
||||
// Cells counters
|
||||
assert.ok(
|
||||
containsN(domRowMeetingCellUpload, ".o-mail-ActivityCell-counter", ["3", "/", "4"])
|
||||
);
|
||||
await contains(".o-mail-ActivityCell-counter", {
|
||||
text: "3",
|
||||
target: domRowOfficeCellUpload,
|
||||
});
|
||||
// Cells dates
|
||||
await contains(".o-mail-ActivityCell-deadline", {
|
||||
text: luxon.DateTime.fromISO(uploadPlannedActs[0].date_deadline).toLocaleString(
|
||||
luxon.DateTime.DATE_SHORT
|
||||
),
|
||||
target: domRowMeetingCellUpload,
|
||||
});
|
||||
await contains(".o-mail-ActivityCell-deadline", {
|
||||
text: luxon.DateTime.fromISO(uploadDoneActs[1].date_done).toLocaleString(
|
||||
luxon.DateTime.DATE_SHORT
|
||||
),
|
||||
target: domRowOfficeCellUpload,
|
||||
});
|
||||
// Activity list popovers content
|
||||
await click(domActivity, `${selRowMeetingCellUpload} > div`);
|
||||
await contains(".o-mail-ActivityListPopover .badge.text-bg-success", { text: "3" }); // 3 planned
|
||||
for (const actIdx of [0, 1, 2]) {
|
||||
await contains(".o-mail-ActivityListPopoverItem", {
|
||||
text: uploadPlannedActs[actIdx].user_id[1],
|
||||
});
|
||||
}
|
||||
await contains(".o-mail-ActivityListPopoverItem", { text: "Due in 4 days" });
|
||||
await contains(".o-mail-ActivityListPopoverItem", { text: "Due in 5 days" });
|
||||
await contains(".o-mail-ActivityListPopoverItem", { text: "Due in 6 days" });
|
||||
await contains(".o-mail-ActivityListPopover .badge.text-bg-secondary", { text: "1" }); // 1 done
|
||||
await contains(".o-mail-ActivityListPopoverItem", { text: uploadDoneActs[0].user_id[1] });
|
||||
await contains(".o-mail-ActivityListPopoverItem", {
|
||||
text: luxon.DateTime.fromISO(uploadDoneActs[0].date_done).toLocaleString(
|
||||
luxon.DateTime.DATE_SHORT
|
||||
),
|
||||
});
|
||||
|
||||
await click(domActivity, `${selRowOfficeCellUpload} > div`);
|
||||
await contains(".o-mail-ActivityListPopover .badge.text-bg-secondary", { text: "3" }); // 3 done
|
||||
for (const actIdx of [1, 2, 3]) {
|
||||
await contains(".o-mail-ActivityListPopoverItem", {
|
||||
text: luxon.DateTime.fromISO(uploadDoneActs[actIdx].date_done).toLocaleString(
|
||||
luxon.DateTime.DATE_SHORT
|
||||
),
|
||||
});
|
||||
await contains(".o-mail-ActivityListPopoverItem", {
|
||||
text: uploadDoneActs[actIdx].user_id[1],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"activity view: a pager can be used when there are more than the limit of 100 activities to display",
|
||||
async function (assert) {
|
||||
@@ -538,6 +694,49 @@ QUnit.module("test_mail", {}, function () {
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("activity view: Mark as done with keep done enabled", async function (assert) {
|
||||
const emailActType = pyEnv["mail.activity.type"].search([["name", "=", "Email"]])[0];
|
||||
pyEnv["mail.activity.type"].write([emailActType], {
|
||||
keep_done: true,
|
||||
});
|
||||
const { openView } = await start({
|
||||
serverData,
|
||||
});
|
||||
await openView({
|
||||
res_model: "mail.test.activity",
|
||||
views: [[false, "activity"]],
|
||||
context: { active_test: false },
|
||||
});
|
||||
const domActivity = document.querySelector(".o_activity_view");
|
||||
const domHeaderEmail = domActivity.querySelector(
|
||||
"table thead tr:first-child th:nth-child(2)"
|
||||
);
|
||||
const selRowOfficeCellEmail = "table tbody tr:nth-child(2) td:nth-child(2)";
|
||||
|
||||
await contains(".o_animated_number", {
|
||||
target: domHeaderEmail,
|
||||
text: "2",
|
||||
});
|
||||
await contains(".o_column_progress_aggregated_on", {
|
||||
target: domHeaderEmail,
|
||||
text: "2",
|
||||
});
|
||||
await click(domActivity, `${selRowOfficeCellEmail} > div`);
|
||||
await click(
|
||||
document,
|
||||
".o-mail-ActivityListPopoverItem .o-mail-ActivityListPopoverItem-markAsDone"
|
||||
);
|
||||
await click(document, ".o-mail-ActivityMarkAsDone button[aria-label='Done']");
|
||||
await contains(".o_animated_number", {
|
||||
target: domHeaderEmail,
|
||||
text: "1",
|
||||
});
|
||||
await contains(".o_column_progress_aggregated_on", {
|
||||
target: domHeaderEmail,
|
||||
text: "2",
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("activity view: no group_by_menu and no comparison_menu", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
|
||||
@@ -45,10 +45,10 @@ QUnit.test("do not show empty text when at least some future activities", async
|
||||
});
|
||||
|
||||
QUnit.test("activity menu widget: activity menu with 2 models", async (assert) => {
|
||||
function selectContaining(domElement, selector, containings) {
|
||||
function containsN(domElement, selector, containings) {
|
||||
return Array.from(domElement.querySelectorAll(selector)).filter((sel) =>
|
||||
containings.every((containing) => sel.textContent.includes(containing))
|
||||
);
|
||||
).length;
|
||||
}
|
||||
|
||||
const tomorrow = today().plus({ days: 1 });
|
||||
@@ -95,22 +95,22 @@ QUnit.test("activity menu widget: activity menu with 2 models", async (assert) =
|
||||
await contains(".o-mail-ActivityMenu");
|
||||
await contains(".o-mail-ActivityMenu .o-mail-ActivityGroup", { count: 2 });
|
||||
assert.ok(
|
||||
selectContaining(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["res.partner", "0 Late"])
|
||||
containsN(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["res.partner", "0 Late"])
|
||||
);
|
||||
assert.ok(
|
||||
selectContaining(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["res.partner", "1 Today"])
|
||||
containsN(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["res.partner", "1 Today"])
|
||||
);
|
||||
assert.ok(
|
||||
selectContaining(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["res.partner", "0 Future"])
|
||||
containsN(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["res.partner", "0 Future"])
|
||||
);
|
||||
assert.ok(
|
||||
selectContaining(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["mail.test.activity", "1 Late"])
|
||||
containsN(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["mail.test.activity", "1 Late"])
|
||||
);
|
||||
assert.ok(
|
||||
selectContaining(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["mail.test.activity", "1 Today"])
|
||||
containsN(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["mail.test.activity", "1 Today"])
|
||||
);
|
||||
assert.ok(
|
||||
selectContaining(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["mail.test.activity", "2 Future"])
|
||||
containsN(document, ".o-mail-ActivityMenu .o-mail-ActivityGroup", ["mail.test.activity", "2 Future"])
|
||||
);
|
||||
actionChecks.res_model = "res.partner";
|
||||
await click(".o-mail-ActivityMenu .o-mail-ActivityGroup", { text: "res.partner" });
|
||||
|
||||
Reference in New Issue
Block a user