import { beforeEach, describe, expect, test } from "@odoo/hoot";
import { queryAll, queryAllTexts, queryFirst } from "@odoo/hoot-dom";
import { animationFrame, mockDate } from "@odoo/hoot-mock";
import { contains, getService, mountWithCleanup, onRpc } from "@web/../tests/web_test_helpers";
import { Domain } from "@web/core/domain";
import { deserializeDateTime } from "@web/core/l10n/dates";
import { WebClient } from "@web/webclient/webclient";
import { Tasks, defineGanttModels } from "./gantt_mock_models";
import {
CLASSES,
SELECTORS,
getActiveScale,
getGridContent,
mountGanttView,
} from "./web_gantt_test_helpers";
defineGanttModels();
describe.current.tags("mobile");
beforeEach(() => mockDate("2018-12-20T08:00:00", +1));
test("empty ungrouped gantt rendering", async () => {
await mountGanttView({
resModel: "tasks",
arch: ``,
domain: [["id", "=", 0]],
});
const { viewTitle, range, columnHeaders, rows } = getGridContent();
expect(viewTitle).toBe(null);
expect(range).toBe("From: 12/01/2018 to: 02/28/2019");
expect(columnHeaders).toHaveLength(10);
expect(columnHeaders.at(0).title).toBe("15");
expect(columnHeaders.at(-1).title).toBe("24");
expect(rows).toEqual([{}]);
expect(SELECTORS.noContentHelper).toHaveCount(0);
});
test("ungrouped gantt rendering", async () => {
const task2 = Tasks._records[1];
const startDateLocalString = deserializeDateTime(task2.start).toFormat("f");
const stopDateLocalString = deserializeDateTime(task2.stop).toFormat("f");
Tasks._views.gantt = ``;
Tasks._views.search = ``;
onRpc("get_gantt_data", ({ model }) => expect.step(model));
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "tasks",
type: "ir.actions.act_window",
views: [[false, "gantt"]],
});
expect.verifySteps(["tasks"]);
await animationFrame();
const { viewTitle, range, columnHeaders, rows } = getGridContent();
expect(viewTitle).toBe(null);
expect(range).toBe("From: 12/01/2018 to: 02/28/2019");
expect(columnHeaders).toHaveLength(10);
expect(columnHeaders.at(0).title).toBe("15");
expect(columnHeaders.at(-1).title).toBe("24");
expect(getActiveScale()).toBe(2);
expect(SELECTORS.expandCollapseButtons).not.toBeVisible();
expect(rows).toEqual([
{
pills: [
{ title: "Task 1", level: 1, colSpan: "Out of bounds (1) -> Out of bounds (63) " },
{
title: "Task 2",
level: 0,
colSpan: "17 (1/2) Dec 2018 -> 22 (1/2) Dec 2018",
},
{
title: "Task 4",
level: 2,
colSpan: "20 Dec 2018 -> 20 (1/2) Dec 2018",
},
{
title: "Task 7",
level: 2,
colSpan: "20 (1/2) Dec 2018 -> 20 Dec 2018",
},
],
},
]);
// test popover and local timezone
expect(`.o_popover`).toHaveCount(0);
const task2Pill = queryAll(SELECTORS.pill)[1];
expect(task2Pill).toHaveText("Task 2");
await contains(task2Pill).click();
expect(`.o_popover`).toHaveCount(1);
expect(queryAllTexts`.o_popover .popover-body span`).toEqual([
"Task 2",
startDateLocalString,
stopDateLocalString,
]);
await contains(`.o_popover .popover-header i.fa.fa-close`).click();
expect(`.o_popover`).toHaveCount(0);
});
test("ordered gantt view", async () => {
await mountGanttView({
resModel: "tasks",
arch: ``,
groupBy: ["stage_id"],
});
const { viewTitle, range, columnHeaders, rows } = getGridContent();
expect(viewTitle).toBe("Gantt View");
expect(range).toBe("From: 12/01/2018 to: 02/28/2019");
expect(columnHeaders).toHaveLength(10);
expect(columnHeaders.at(0).title).toBe("16");
expect(columnHeaders.at(-1).title).toBe("25");
expect(SELECTORS.noContentHelper).toHaveCount(0);
expect(rows).toEqual([
{
title: "todo",
},
{
title: "in_progress",
pills: [
{ level: 0, colSpan: "Out of bounds (1) -> Out of bounds (63) ", title: "Task 1" },
{
level: 1,
colSpan: "20 (1/2) Dec 2018 -> 20 Dec 2018",
title: "Task 7",
},
],
},
{
title: "done",
pills: [
{
level: 0,
colSpan: "17 (1/2) Dec 2018 -> 22 (1/2) Dec 2018",
title: "Task 2",
},
],
},
{
title: "cancel",
pills: [
{
level: 0,
colSpan: "20 Dec 2018 -> 20 (1/2) Dec 2018",
title: "Task 4",
},
],
},
]);
});
test("empty single-level grouped gantt rendering", async () => {
await mountGanttView({
resModel: "tasks",
arch: ``,
groupBy: ["project_id"],
domain: Domain.FALSE.toList(),
});
const { viewTitle, range, columnHeaders, rows } = getGridContent();
expect(viewTitle).toBe("Gantt View");
expect(range).toBe("From: 12/01/2018 to: 02/28/2019");
expect(columnHeaders).toHaveLength(10);
expect(columnHeaders.at(0).title).toBe("16");
expect(columnHeaders.at(-1).title).toBe("25");
expect(rows).toEqual([{ title: "" }]);
expect(SELECTORS.noContentHelper).toHaveCount(0);
});
test("single-level grouped gantt rendering", async () => {
await mountGanttView({
resModel: "tasks",
arch: ``,
groupBy: ["project_id"],
});
expect(getActiveScale()).toBe(2);
expect(SELECTORS.expandCollapseButtons).not.toBeVisible();
const { range, viewTitle, columnHeaders, rows } = getGridContent();
expect(range).toBe("From: 12/01/2018 to: 02/28/2019");
expect(viewTitle).toBe("Tasks");
expect(columnHeaders).toHaveLength(10);
expect(columnHeaders.at(0).title).toBe("16");
expect(columnHeaders.at(-1).title).toBe("25");
expect(rows).toEqual([
{
title: "Project 1",
pills: [
{
title: "Task 1",
colSpan: "Out of bounds (1) -> Out of bounds (63) ",
level: 0,
},
{
title: "Task 2",
colSpan: "17 (1/2) Dec 2018 -> 22 (1/2) Dec 2018",
level: 1,
},
{
title: "Task 4",
colSpan: "20 Dec 2018 -> 20 (1/2) Dec 2018",
level: 2,
},
],
},
{
title: "Project 2",
pills: [
{
title: "Task 7",
colSpan: "20 (1/2) Dec 2018 -> 20 Dec 2018",
level: 0,
},
],
},
]);
});
test("Controls: rendering is mobile friendly", async () => {
await mountGanttView({
resModel: "tasks",
arch: ``,
});
// check toolbar's dropdown
await contains("button.dropdown-toggle").click();
expect(queryAllTexts`.o-dropdown-item`).toEqual(["Activate sparse mode"]);
// check that pickers open in dialog
await contains(SELECTORS.rangeMenuToggler).click();
expect(".modal").toHaveCount(0);
await contains(SELECTORS.startDatePicker).click();
expect(".modal").toHaveCount(1);
expect(".modal-title").toHaveText("Gantt start date");
expect(".modal-body .o_datetime_picker").toHaveCount(1);
await contains(".modal-header .btn").click();
expect(".modal").toHaveCount(0);
await contains(SELECTORS.stopDatePicker).click();
expect(".modal").toHaveCount(1);
expect(".modal-title").toHaveText("Gantt stop date");
expect(".modal-body .o_datetime_picker").toHaveCount(1);
await contains(".modal-header .btn").click();
expect(".modal").toHaveCount(0);
});
test("Progressbar: check the progressbar percentage visibility.", async () => {
onRpc("get_gantt_data", async ({ kwargs, parent }) => {
expect.step("get_gantt_data");
const result = await parent();
expect(kwargs.progress_bar_fields).toEqual(["user_id"]);
result.progress_bars.user_id = {
1: { value: 50, max_value: 100 },
2: { value: 25, max_value: 200 },
};
return result;
});
await mountGanttView({
resModel: "tasks",
arch: `
`,
});
expect.verifySteps(["get_gantt_data"]);
expect(SELECTORS.progressBar).toHaveCount(2);
const [progressBar1, progressBar2] = queryAll(SELECTORS.progressBar);
expect(progressBar1).toHaveClass("o_gantt_group_success");
expect(progressBar2).toHaveClass("o_gantt_group_success");
const [rowHeader1, rowHeader2] = [progressBar1.parentElement, progressBar2.parentElement];
expect(rowHeader1.matches(SELECTORS.rowHeader)).toBe(true);
expect(rowHeader2.matches(SELECTORS.rowHeader)).toBe(true);
expect(rowHeader1).not.toHaveClass(CLASSES.group);
expect(rowHeader2).not.toHaveClass(CLASSES.group);
expect(queryAll(SELECTORS.progressBarBackground).map((el) => el.style.width)).toEqual([
"50%",
"12.5%",
]);
expect(SELECTORS.progressBarForeground).toHaveCount(2);
expect(queryAllTexts(SELECTORS.progressBarForeground)).toEqual(["50h / 100h", "25h / 200h"]);
// Check the style of one of the progress bars
expect(rowHeader1.children).toHaveLength(2);
const rowTitle1 = rowHeader1.children[0];
expect(rowTitle1.matches(SELECTORS.rowTitle)).toBe(true);
expect(rowTitle1.nextElementSibling).toBe(progressBar1);
expect(rowHeader1).toHaveStyle({ gridTemplateRows: "36px 35px" });
expect(rowTitle1).toHaveStyle({ height: "36px" });
expect(progressBar1).toHaveStyle({ height: "35px" });
});
test("Progressbar: grouped row", async () => {
onRpc("get_gantt_data", async ({ kwargs, parent }) => {
expect.step("get_gantt_data");
const result = await parent();
expect(kwargs.progress_bar_fields).toEqual(["user_id"]);
result.progress_bars.user_id = {
1: { value: 50, max_value: 100 },
2: { value: 25, max_value: 200 },
};
return result;
});
await mountGanttView({
resModel: "tasks",
arch: `
`,
});
expect.verifySteps(["get_gantt_data"]);
expect(SELECTORS.progressBar).toHaveCount(4);
const [progressBar1, progressBar2] = queryAll(SELECTORS.progressBar);
expect(progressBar1).toHaveClass("o_gantt_group_success");
expect(progressBar2).toHaveClass("o_gantt_group_success");
const [rowHeader1, rowHeader2] = [progressBar1.parentElement, progressBar2.parentElement];
expect(rowHeader1.matches(SELECTORS.rowHeader)).toBe(true);
expect(rowHeader2.matches(SELECTORS.rowHeader)).toBe(true);
expect(rowHeader1).toHaveClass(CLASSES.group);
expect(rowHeader2).not.toHaveClass(CLASSES.group);
expect(queryAll(SELECTORS.progressBarBackground).map((el) => el.style.width)).toEqual([
"50%",
"50%",
"12.5%",
"12.5%",
]);
expect(SELECTORS.progressBarForeground).toHaveCount(4);
expect(queryAllTexts(SELECTORS.progressBarForeground)).toEqual([
"50h / 100h",
"50h / 100h",
"25h / 200h",
"25h / 200h",
]);
// Check the style of one of the progress bars
expect(rowHeader1.children).toHaveLength(2);
const rowTitle1 = rowHeader1.children[0];
expect(rowTitle1.matches(SELECTORS.rowTitle)).toBe(true);
expect(rowTitle1.nextElementSibling).toBe(progressBar1);
expect(rowHeader1).toHaveStyle({ gridTemplateRows: "24px 35px" });
expect(rowTitle1).toHaveStyle({ height: "24px" });
expect(progressBar1).toHaveStyle({ height: "35px" });
});
test("horizontal scroll applies to the content [SMALL SCREEN]", async () => {
Tasks._views.search = ``;
Tasks._views.gantt = ``;
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "tasks",
type: "ir.actions.act_window",
views: [[false, "gantt"]],
});
await animationFrame();
const o_view_controller = queryFirst(".o_view_controller");
const o_content = queryFirst(".o_content");
const firstColumnHeader = queryFirst(SELECTORS.columnHeader);
const initialXHeaderCell = firstColumnHeader.getBoundingClientRect().x;
expect(o_view_controller).toHaveClass("o_action_delegate_scroll");
expect(o_view_controller).toHaveStyle({ overflow: "hidden" });
expect(o_content).toHaveStyle({ overflow: "auto" });
expect(o_content).toHaveProperty("scrollLeft", 762);
// Horizontal scroll
const newScrollLeft = o_content.scrollLeft - 50;
await contains(".o_content").scroll({ left: newScrollLeft });
expect(o_content).toHaveProperty("scrollLeft", newScrollLeft);
expect(firstColumnHeader.getBoundingClientRect().x).toBe(initialXHeaderCell + 50);
});