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); });