import { mailModels } from "@mail/../tests/mail_test_helpers"; import { beforeEach, describe, destroy, expect, test } from "@odoo/hoot"; import { hover, keyDown, queryAll, queryAllTexts, queryOne } from "@odoo/hoot-dom"; import { animationFrame, mockDate } from "@odoo/hoot-mock"; import { clickModalButton, contains, defineModels, fields, mockService, models, onRpc, } from "@web/../tests/web_test_helpers"; import { clickCell, dragPill, getGridContent, hoverGridCell, mountGanttView, SELECTORS, } from "@web_gantt/../tests/web_gantt_test_helpers"; import { defineProjectModels, projectModels, ProjectProject, } from "@project/../tests/project_models"; describe.current.tags("desktop"); const ganttViewParams = { arch: '', resModel: "project.task", groupBy: ["user_ids"], }; beforeEach(() => { mockDate("2021-06-22 08:00:00"); mailModels.ResUsers._records.push({ id: 100, name: "Jane Doe" }, { id: 101, name: "John Doe" }); ProjectProject._records = [ { id: 1, name: "My Project", date_start: "2021-01-01", date: "2021-06-24" }, ]; }); class Task extends projectModels.ProjectTask { stuff_id = fields.Many2one({ string: "Stuff", relation: "stuff" }); _records = [ { id: 1, name: "Blop", planned_date_begin: "2021-06-14 08:00:00", date_deadline: "2021-06-24 08:00:00", user_ids: [100], project_id: 1, milestone_id: 3, }, { id: 2, name: "Yop", planned_date_begin: "2021-06-02 08:00:00", date_deadline: "2021-06-12 08:00:00", user_ids: [101], stuff_id: 1, project_id: 1, }, ]; _views = { list: '', form: '
', }; } class Stuff extends models.Model { name = fields.Char(); _records = [{ id: 1, name: "Bruce Willis" }]; } projectModels.ProjectTask = Task; defineModels([Stuff]); defineProjectModels(); test("not user_ids grouped: empty groups are displayed first and user avatar is not displayed", async () => { await mountGanttView({ ...ganttViewParams, groupBy: ["stuff_id"] }); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual([ "Undefined Stuff", "Bruce Willis", ]); expect(".o_gantt_row_headers .o-mail-Avatar").toHaveCount(0); }); test("Unschedule button is displayed", async () => { onRpc(({ method, model }) => { if (model === "project.task" && method == "action_unschedule_task") { expect.step("unschedule task"); return false; } }); await mountGanttView({ arch: `
`, resModel: "project.task", }); await contains(".o_gantt_pill").click(); expect(".btn.btn-sm.btn-secondary").toHaveCount(1); expect(".btn.btn-sm.btn-secondary").toHaveText("Unschedule"); await contains(".btn.btn-sm.btn-secondary").click(); expect.verifySteps(["unschedule task"]); }); test("not user_ids grouped: no empty group if no records", async () => { // delete the record having no stuff_id Task._records.splice(0, 1); await mountGanttView({ ...ganttViewParams, groupBy: ["stuff_id"] }); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual(["Bruce Willis"]); }); test("user_ids grouped: specific empty group added, even if no records", async () => { await mountGanttView(ganttViewParams); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual( ["👤 Unassigned", "Jane Doe", "John Doe"], { message: "'👤 Unassigned' should be the first group, even if no record exist without user_ids", } ); expect(".o_gantt_row_headers .o-mail-Avatar").toHaveCount(2); }); test("[user_ids, ...] grouped", async () => { // add an unassigned task (no user_ids) that has a linked stuff Task._records.push({ id: 3, name: "Gnop", planned_date_begin: "2021-06-02 08:00:00", date_deadline: "2021-06-12 08:00:00", stuff_id: 1, }); await mountGanttView({ ...ganttViewParams, groupBy: ["user_ids", "stuff_id"] }); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual([ "👤 Unassigned", "Undefined Stuff", "Bruce Willis", "Jane Doe", "Undefined Stuff", "John Doe", "Bruce Willis", ]); }); test("[..., user_ids(, ...)] grouped", async () => { await mountGanttView({ ...ganttViewParams, groupBy: ["stuff_id", "user_ids"] }); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual([ "Undefined Stuff", "👤 Unassigned", "Jane Doe", "Bruce Willis", "👤 Unassigned", "John Doe", ]); }); test('Empty groupby "Assigned To" and "Project" can be rendered', async function (assert) { Task._records = []; await mountGanttView({ ...ganttViewParams, groupBy: ["user_ids", "project_id"] }); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual([ "👤 Unassigned", "🔒 Private", ]); }); test("progress bar has the correct unit", async () => { onRpc("get_gantt_data", ({ kwargs, parent }) => { const result = parent(); expect(kwargs.progress_bar_fields).toEqual(["user_ids"]); result.progress_bars.user_ids = { 100: { value: 100, max_value: 100 }, }; return result; }); await mountGanttView({ arch: '', resModel: "project.task", type: "gantt", groupBy: ["user_ids"], }); expect(SELECTORS.progressBar).toHaveCount(1); expect(SELECTORS.progressBarBackground).toHaveCount(1); expect(queryOne(SELECTORS.progressBarBackground).style.width).toBe("100%"); expect(SELECTORS.progressBarForeground).toHaveCount(0); await hoverGridCell("10", "June 2021", "Jane Doe"); expect(SELECTORS.progressBarForeground).toHaveCount(1); expect(SELECTORS.progressBarForeground).toHaveText("100h / 100h"); }); test("open a dialog to schedule task", async () => { Task._records.push({ id: 51, name: "Task 51", project_id: 1, user_ids: [100], }); onRpc("schedule_tasks", () => { expect.step("schedule_tasks"); return {}; }); await mountGanttView({ arch: '', resModel: "project.task", type: "gantt", }); await clickCell("10", "June 2021"); await contains(".modal .o_list_view tbody tr:nth-child(1) input").click(); await animationFrame(); expect(".modal .o_list_view .o_data_row").toHaveClass("o_data_row_selected"); await contains(".modal footer .o_auto_plan_button").click(); expect.verifySteps(["schedule_tasks"]); }); test("Lines are displayed in alphabetic order, except for the first one", async () => { for (const user of [ { id: 102, name: "Omega" }, { id: 103, name: "Theta" }, { id: 104, name: "Rho" }, { id: 105, name: "Zeta" }, { id: 106, name: "Kappa" }, ]) { mailModels.ResUsers._records.push(user); Task._records.push({ id: user.id, name: "Citron en Suédois", planned_date_begin: "2021-06-02 08:00:00", date_deadline: "2021-06-12 08:00:00", project_id: 1, user_ids: [user.id], }); } await mountGanttView(ganttViewParams); expect(queryAllTexts(".o_gantt_row_headers .o_gantt_row_title")).toEqual( ["👤 Unassigned", "Jane Doe", "John Doe", "Kappa", "Omega", "Rho", "Theta", "Zeta"], { message: "The lines should be sorted by alphabetical order ('👤 Unassigned' is always first)", } ); }); test("Display milestones deadline in project.task gantt view", async () => { onRpc("get_all_deadlines", function () { const [milestone1, milestone2] = this.env["project.milestone"].browse([1, 2]); return { milestone_id: [ { ...milestone1, project_id: [1, "My Project"], }, { ...milestone2, project_id: [1, "My Project"], }, ], project_id: [], }; }); await mountGanttView({ ...ganttViewParams, }); expect(".o_project_milestone_diamond").toHaveCount(2); expect(".o_project_milestone_diamond .o_milestones_reached").toHaveCount(1); expect(".o_project_milestone_diamond.o_unreached_milestones").toHaveCount(1); await hover(".o_project_milestone_diamond"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .o_milestones_reached").toHaveCount(1); expect(".o_popover strong").toHaveText("Milestone 1"); await hover(".o_project_milestone_diamond.o_unreached_milestones"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .o_unreached_milestones").toHaveCount(1); expect(".o_popover strong").toHaveText("Milestone 2"); }); test("Display milestones deadline in gantt view of tasks in a project", async () => { onRpc("get_all_deadlines", function () { const [milestone1, milestone2] = this.env["project.milestone"].browse([1, 2]); return { milestone_id: [ { ...milestone1, project_id: [1, "My Project"], }, { ...milestone2, project_id: [1, "My Project"], }, ], project_id: [], }; }); await mountGanttView({ ...ganttViewParams, context: { default_project_id: 1, }, }); expect(".o_project_milestone_diamond").toHaveCount(2); expect(".o_project_milestone_diamond .o_milestones_reached").toHaveCount(1); expect(".o_project_milestone_diamond.o_unreached_milestones").toHaveCount(1); await hover(".o_project_milestone_diamond"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(0); expect(".o_popover .o_milestones_reached").toHaveCount(1); expect(".o_popover strong").toHaveText("Milestone 1"); await hover(".o_project_milestone_diamond.o_unreached_milestones"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(0); expect(".o_popover .o_unreached_milestones").toHaveCount(1); expect(".o_popover strong").toHaveText("Milestone 2"); }); test("Display project deadline in the gantt view of task", async () => { ProjectProject._records.push({ id: 2, name: "Other Project", date_start: "2021-06-12", date: "2021-06-28", }); await mountGanttView(ganttViewParams); expect(".o_gantt_header_cell .o_project_startdate_circle").toHaveCount(1); expect(".o_gantt_header_cell .o_project_deadline_circle").toHaveCount(2); expect(".o_popover").toHaveCount(0); await hover(".o_gantt_header_cell .o_project_startdate_circle"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover .popover-body u").toHaveText("Other Project"); expect(".o_popover .popover-body em").toHaveText("Project start"); const [myProjectDeadlineCircleEl, otherProjectDeadlineCircleEl] = queryAll( ".o_gantt_header_cell .o_project_deadline_circle" ); await hover(myProjectDeadlineCircleEl); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .popover-body em").toHaveText("Project due"); await hover(otherProjectDeadlineCircleEl); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("Other Project"); expect(".o_popover .popover-body em").toHaveText("Project due"); }); test("Display project and milestones deadline in the gantt view of task", async () => { ProjectProject._records.push({ id: 2, name: "Other Project", date_start: "2021-06-12", date: "2021-06-28", }); onRpc("get_all_deadlines", function () { const [milestone1, milestone2] = this.env["project.milestone"].browse([1, 2]); return { milestone_id: [ { ...milestone1, project_id: [1, "My Project"], }, { ...milestone2, project_id: [1, "My Project"], }, ], project_id: this.env["project.project"].search_read(), }; }); await mountGanttView(ganttViewParams); expect(".o_project_milestone_diamond").toHaveCount(2); expect(".o_project_milestone_diamond .o_milestones_reached").toHaveCount(1); expect(".o_project_milestone_diamond.o_unreached_milestones").toHaveCount(1); await hover(".o_project_milestone_diamond"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .o_milestones_reached").toHaveCount(1); expect(".o_popover strong").toHaveText("Milestone 1"); await hover(".o_project_milestone_diamond.o_unreached_milestones"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .o_unreached_milestones").toHaveCount(1); expect(".o_popover strong").toHaveText("Milestone 2"); expect(".o_gantt_header_cell .o_project_startdate_circle").toHaveCount(1); expect(".o_gantt_header_cell .o_project_deadline_circle").toHaveCount(2); await hover(".o_gantt_header_cell .o_project_startdate_circle"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover .popover-body u").toHaveText("Other Project"); expect(".o_popover .popover-body em").toHaveText("Project start"); const [myProjectDeadlineCircleEl, otherProjectDeadlineCircleEl] = queryAll( ".o_gantt_header_cell .o_project_deadline_circle" ); await hover(myProjectDeadlineCircleEl); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .popover-body em").toHaveText("Project due"); await hover(otherProjectDeadlineCircleEl); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("Other Project"); expect(".o_popover .popover-body em").toHaveText("Project due"); }); test("Display project deadline and milestone date in the same date", async () => { onRpc("get_all_deadlines", function () { const [milestone3] = this.env["project.milestone"].browse(3); return { milestone_id: [ { ...milestone3, project_id: [1, "My Project"], }, ], project_id: this.env["project.project"].search_read(), }; }); await mountGanttView(ganttViewParams); expect( ".o_gantt_header_cell .o_project_milestone_diamond.o_project_deadline_milestone" ).toHaveCount(1); await hover(".o_gantt_header_cell .o_project_milestone_diamond.o_project_deadline_milestone"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(1); expect(".o_popover u").toHaveText("My Project"); expect(".o_popover .popover-body").toHaveCount(1); expect(".o_popover .popover-body em").toHaveText("Project due"); expect(".o_popover .popover-body strong").toHaveText("Milestone 3"); }); test("Display 2 milestones in different project at the same date", async () => { ProjectProject._records.push({ id: 2, name: "Other Project", }); projectModels.ProjectMilestone._records.push({ id: 4, name: "Milestone 4", deadline: "2021-06-24", project_id: 2, }); onRpc("get_all_deadlines", function () { const [milestone3, milestone4] = this.env["project.milestone"].browse([3, 4]); return { milestone_id: [ { ...milestone3, project_id: [1, "My Project"], }, { ...milestone4, project_id: [2, "Other Project"], }, ], project_id: [], }; }); await mountGanttView(ganttViewParams); expect(".o_project_milestone_diamond").toHaveCount(1); await hover(".o_project_milestone_diamond"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(2); expect(queryAllTexts(".o_popover u")).toEqual(["My Project", "Other Project"]); expect(".o_popover .popover-body i.fa-square-o").toHaveCount(2); expect(".o_popover .popover-body strong").toHaveCount(2); expect(queryAllTexts(".o_popover .popover-body strong")).toEqual([ "Milestone 3", "Milestone 4", ]); expect(queryAllTexts(".o_popover .popover-body")).toEqual([ "My Project\nMilestone 3\nOther Project\nMilestone 4", ]); }); test("Display project deadline of 2 projects with the same deadline", async () => { ProjectProject._records.push({ id: 2, name: "Other Project", date_start: "2021-05-12", date: "2021-06-24", }); await mountGanttView(ganttViewParams); expect(".o_gantt_header_cell .o_project_deadline_circle").toHaveCount(1); await hover(".o_gantt_header_cell .o_project_deadline_circle"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(2); expect(queryAllTexts(".o_popover u")).toEqual(["My Project", "Other Project"]); expect(".o_popover .popover-body em").toHaveCount(2); expect(queryAllTexts(".o_popover .popover-body em")).toEqual(["Project due", "Project due"]); expect(queryAllTexts(".o_popover .popover-body")).toEqual([ "My Project\nProject due\nOther Project\nProject due", ]); }); test("Display project deadline one day before the start date of the other project", async () => { ProjectProject._records.push({ id: 2, name: "Other Project", date_start: "2021-06-25", date: "2021-10-01", }); await mountGanttView(ganttViewParams); expect(".o_gantt_header_cell .o_project_deadline_circle").toHaveCount(1); await hover(".o_gantt_header_cell .o_project_deadline_circle"); await animationFrame(); expect(".o_popover").toHaveCount(1); expect(".o_popover u").toHaveCount(2); expect(queryAllTexts(".o_popover u")).toEqual(["My Project", "Other Project"]); expect(".o_popover .popover-body em").toHaveCount(2); expect(queryAllTexts(".o_popover .popover-body em")).toEqual(["Project due", "Project start"]); expect(queryAllTexts(".o_popover .popover-body")).toEqual([ "My Project\nProject due\nOther Project\nProject start", ]); }); test("Copy pill in another row", async () => { await mountGanttView(ganttViewParams); expect(getGridContent().rows).toEqual([ { title: "👤 Unassigned", }, { title: "Jane Doe", pills: [{ title: "Blop", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }], }, { title: "John Doe", pills: [{ title: "Yop", level: 0, colSpan: "02 June 2021 -> 12 (1/2) June 2021" }], }, ]); await keyDown("Control"); // move blop to John Doe const { drop, moveTo } = await dragPill("Blop"); await moveTo({ columnHeader: "14", groupHeader: "June 2021", row: "John Doe" }); expect(SELECTORS.renderer).toHaveClass("o_copying"); await drop({ columnHeader: "14", groupHeader: "June 2021", row: "John Doe" }); expect(getGridContent().rows).toEqual([ { title: "👤 Unassigned", }, { title: "Jane Doe", pills: [{ title: "Blop", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }], }, { title: "John Doe", pills: [ { title: "Yop", level: 0, colSpan: "Out of bounds (3) -> 12 (1/2) June 2021" }, { title: "Blop (copy)", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }, ], }, ]); }); test("Smart scheduling", async () => { Task._records.push({ id: 3, name: "Gnop", user_ids: [100], }); onRpc("schedule_tasks", function (request) { expect.step("schedule_tasks"); return this.env["project.task"].write(...request.args); }); await mountGanttView(ganttViewParams); expect(getGridContent().rows).toEqual([ { title: "👤 Unassigned", }, { title: "Jane Doe", pills: [{ title: "Blop", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }], }, { title: "John Doe", pills: [{ title: "Yop", level: 0, colSpan: "02 June 2021 -> 12 (1/2) June 2021" }], }, ]); await clickCell("10", "June 2021", "Jane Doe"); expect(".o_dialog").toHaveCount(1); await contains(".o_dialog .o_data_row .o-checkbox").click(); await contains(".o_dialog .o_auto_plan_button:enabled").click(); expect(getGridContent().rows).toEqual([ { title: "👤 Unassigned", }, { title: "Jane Doe", pills: [ { title: "Gnop", level: 0, colSpan: "10 June 2021 -> 10 June 2021" }, { title: "Blop", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }, ], }, { title: "John Doe", pills: [{ title: "Yop", level: 0, colSpan: "02 June 2021 -> 12 (1/2) June 2021" }], }, ]); expect.verifySteps(["schedule_tasks"]); }); test("Smart scheduling: display warnings", async () => { Task._records.push({ id: 3, name: "Gnop", user_ids: [100], }); onRpc("schedule_tasks", () => { expect.step("schedule_tasks"); return [ {}, { 3: { planned_date_begin: false, date_deadline: false, }, }, ]; }); let notifications = []; mockService("notification", { add: (message, options) => { expect.step("notification added"); expect(options["type"]).toBe("success"); expect(options["buttons"].length).toBe(1); expect(options["buttons"][0].icon).toBe("fa-undo"); expect(options["buttons"][0].name).toBe("Undo"); notifications.push({ message, options }); return () => { notifications = notifications.filter( (n) => n.message !== message || n.options !== options ); }; }, }); const ganttView = await mountGanttView(ganttViewParams); await clickCell("10", "June 2021", "Jane Doe"); expect(".o_dialog").toHaveCount(1); await contains(".o_dialog .o_data_row .o-checkbox").click(); await contains(".o_dialog .o_auto_plan_button:enabled").click(); expect.verifySteps(["schedule_tasks", "notification added"]); expect(notifications).toHaveLength(1); destroy(ganttView); expect(notifications).toHaveLength(0); }); test("Schedule a task and verify its display in the gantt view", async () => { Task._records.push({ id: 3, name: "Gnop", user_ids: [100], }); onRpc("web_gantt_write", ({ args }) => { expect.step("web_gantt_write"); expect(args[0]).toEqual([3], { message: "should write on the correct record" }); expect(args[1]).toEqual( { planned_date_begin: "2021-06-09 23:00:00", date_deadline: "2021-06-10 23:00:00", user_ids: [100], }, { message: "should write these changes" } ); }); await mountGanttView(ganttViewParams); expect(getGridContent().rows).toEqual([ { title: "👤 Unassigned", }, { title: "Jane Doe", pills: [{ title: "Blop", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }], }, { title: "John Doe", pills: [{ title: "Yop", level: 0, colSpan: "02 June 2021 -> 12 (1/2) June 2021" }], }, ]); await clickCell("10", "June 2021", "Jane Doe"); expect(".o_dialog").toHaveCount(1); await contains(".o_dialog .o_data_row .o-checkbox input").check(); await clickModalButton({ text: "Select" }); expect(getGridContent().rows).toEqual([ { title: "👤 Unassigned", }, { title: "Jane Doe", pills: [ { title: "Gnop", level: 0, colSpan: "10 June 2021 -> 10 June 2021" }, { title: "Blop", level: 0, colSpan: "14 June 2021 -> 24 (1/2) June 2021" }, ], }, { title: "John Doe", pills: [{ title: "Yop", level: 0, colSpan: "02 June 2021 -> 12 (1/2) June 2021" }], }, ]); expect.verifySteps(["web_gantt_write"]); }); test("Should open the list dialog with 'Open Tasks' filter on Gantt cell click", async () => { Task._records.push({ id: 4, name: "Task 4", is_closed: true }, { id: 5, name: "Task 5" }); await mountGanttView({ ...ganttViewParams, searchViewArch: ` `, }); await clickCell("10", "June 2021", "Jane Doe"); expect(".o_dialog").toHaveCount(1); expect(".o_searchview_facet").toHaveCount(1, { message: "Open Tasks filter applied" }); expect(".o_data_row").toHaveCount(1, { message: "Only open task should appear" }); }); test("template dropdown in gantt view of a project with no template", async () => { await mountGanttView({ ...ganttViewParams, context: { default_project_id: 1, }, }); expect(".o_gantt_button_add").toHaveCount(1, { message: "The “New” button should be displayed", }); expect(".o_gantt_button_add").not.toHaveClass("dropdown-toggle", { message: "The “New” button should not be a dropdown since there is no template", }); // Test that we can create a new record without errors await contains(".o_gantt_button_add").click(); }); test("template dropdown in gantt view of a project with one template", async () => { Task._records.push( { id: 3, name: "Template Task 1", project_id: 1, is_template: true, planned_date_begin: "2021-06-02 08:00:00", date_deadline: "2021-06-12 08:00:00", }, { id: 4, name: "Template Task 2", project_id: 1, is_template: true, planned_date_begin: "2021-06-02 08:00:00", date_deadline: "2021-06-12 08:00:00", } ); await mountGanttView({ ...ganttViewParams, context: { default_project_id: 1, }, }); expect(".o_gantt_button_add").toHaveCount(1, { message: "The “New” button should be displayed", }); expect(".o_gantt_button_add").toHaveClass("dropdown-toggle", { message: "The “New” button should be a dropdown since there is a template", }); await contains(".o_gantt_button_add").click(); expect("button.dropdown-item:contains('New Task')").toHaveCount(1, { message: "The “New Task” button should be in the dropdown", }); expect("button.dropdown-item:contains('Template Task 1')").toHaveCount(1, { message: "There should be a button named after the task template", }); }); test("Closed tasks in gantt view have lower opacity than open tasks", async () => { Task._records = [{ ...Task._records[0], is_closed: true }]; await mountGanttView({ ...ganttViewParams, groupBy: ["user_ids"], }); expect(".o_gantt_pill").toHaveClass("opacity-50"); });