`,
list: `
`,
grid: `
`,
"grid,1": `
`,
search: `
`,
};
}
class Project extends models.Model {
name = fields.Char();
_records = [
{ id: 31, name: "P1" },
{ id: 142, name: "Webocalypse Now" },
];
}
class Task extends models.Model {
name = fields.Char();
project_id = fields.Many2one({ string: "Project", relation: "project" });
_records = [
{ id: 1, name: "BS task", project_id: 31 },
{ id: 12, name: "Another BS task", project_id: 142 },
{ id: 54, name: "yet another task", project_id: 142 },
];
_views = {
form: ``,
search: ``,
};
}
defineModels([Line, Project, Task]);
beforeEach(() => {
mockDate("2017-01-30 00:00:00");
});
onRpc("grid_unavailability", () => {
return {};
});
onRpc("has_group", () => true);
describe.tags("desktop");
describe("grid_view_desktop", () => {
test("basic empty grid view", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
domain: Domain.FALSE.toList({}),
});
expect(".o_grid_view").toHaveCount(1);
expect(".o_grid_renderer").toHaveCount(1);
expect(".o_grid_buttons:visible").toHaveCount(1);
expect(".o_grid_custom_buttons").toHaveCount(0);
expect(".o_grid_navigation_buttons").toHaveCount(1);
expect(".o_grid_navigation_buttons button:eq(0)").toHaveText("Today", {
message: "The first navigation button should be the Today one.",
});
expect(".o_grid_navigation_buttons button > span.oi-arrow-left").toHaveCount(1, {
message: "The previous button should be there",
});
expect(".o_grid_navigation_buttons button > span.oi-arrow-right").toHaveCount(1, {
message: "The next button should be there",
});
expect(".o_view_scale_selector").toHaveCount(1);
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Day", {
message: "The default active range should be the first one define in the grid view",
});
await contains(".scale_button_selection").click();
expect(".o-dropdown--menu .o_scale_button_day").toHaveCount(1, {
message: "The Day scale should be in the dropdown menu",
});
expect(".o-dropdown--menu .o_scale_button_week").toHaveCount(1, {
message: "The week scale should be in the dropdown menu",
});
expect(".o-dropdown--menu .o_scale_button_month").toHaveCount(1, {
message: "The month scale should be in the dropdown menu",
});
expect(".o_grid_column_title.fw-bolder").toHaveCount(1, {
message: "The column title containing the date should be the current date",
});
expect(".o_grid_column_title.fw-bolder").toHaveText("Mon,\nJan 30", {
message: "The current date should be Monday on 30 January 2023",
});
expect(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)").toHaveCount(
1,
{
message: "It should have 1 column",
}
);
expect(".o_grid_column_title.o_grid_row_total").toHaveCount(1, {
message: "It should have 1 column for the total",
});
expect(".o_grid_column_title.o_grid_row_total").toHaveCount(1);
expect(".o_grid_column_title.o_grid_row_total").toHaveText("Unit Amount", {
message: "The column title of row totals should be the string of the measure field",
});
expect(".o_grid_add_line a").toHaveCount(0, {
message:
"No Add a line button should be displayed when create_inline is false (default behavior)",
});
});
test("basic empty grid view using a specific range by default", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
domain: Domain.FALSE.toList({}),
});
expect(".o_grid_view").toHaveCount(1);
expect(".o_grid_renderer").toHaveCount(1);
expect(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)").toHaveCount(
7,
{
message: "It should have 7 column representing the dates on a week.",
}
);
expect(
queryAllTexts(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)")
).toEqual(
[
"Sun,\nJan 29",
"Mon,\nJan 30",
"Tue,\nJan 31",
"Wed,\nFeb 1",
"Thu,\nFeb 2",
"Fri,\nFeb 3",
"Sat,\nFeb 4",
],
{ message: "check the columns title is correctly formatted when the range is week" }
);
expect(".o_grid_column_title.o_grid_row_total").toHaveCount(1, {
message: "It should have 1 column for the total",
});
expect(".o_grid_column_title.fw-bolder").toHaveCount(1, {
message: "The column title containing the current date should not be there.",
});
expect(".o_grid_column_title.fw-bolder").toHaveText("Mon,\nJan 30", {
message: "The current date should be Monday on 30 January",
});
});
test("basic grid view", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(
".o_grid_row.o_grid_highlightable:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveCount(14, {
message:
"The number of cells containing numeric value and whom is not a total cell should be 14 (2 rows and 7 cells to represent the week)",
});
expect(
".o_grid_row.o_grid_highlightable.text-danger:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveCount(1, {
message: "In those 14 cells, one has a value less than 0 and so the text should be red",
});
expect(
".o_grid_row.o_grid_highlightable.text-danger:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveText("-3.50", {
message: "The cell with text color in red should contain `-3.50`",
});
expect(".o_grid_row.o_grid_highlightable.o_grid_column_total.text-danger").toHaveCount(1, {
message:
"The cell containing the column total and in that column a cell is negative to also get a total negative should have text color in red",
});
expect(".o_grid_row.o_grid_highlightable.o_grid_column_total.text-danger").toHaveText(
"-3.50"
);
expect(".o_grid_row.o_grid_highlightable.o_grid_row_total.bg-danger").toHaveCount(1);
expect(".o_grid_row.o_grid_highlightable.o_grid_row_total.bg-danger").toHaveText("-3.50");
expect(".o_grid_row.o_grid_highlightable > div.bg-info").toHaveCount(3, {
message:
"The cell in the column of the current should have `bg-info` class as the header",
});
expect(".o_grid_row.o_grid_row_title.o_grid_highlightable").toHaveCount(2);
expect(queryAllTexts(".o_grid_row.o_grid_row_title.o_grid_highlightable")).toEqual([
"P1\n|\nBS task",
"Webocalypse Now\n|\nAnother BS task",
]);
await contains(".o_grid_navigation_buttons button span.oi-arrow-right").click();
expect(
".o_grid_row.o_grid_highlightable:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveCount(0, {
message: "No cell should be found because no records is found next week",
});
expect(".o_view_nocontent").toHaveCount(1, {
message: "No content div should be displayed",
});
expect("div.bg-info").toHaveCount(0, {
message: "No column should be the current date since we move in the following week.",
});
await contains(".o_grid_navigation_buttons button span.oi-arrow-right").click();
expect("div.o_grid_row_title").toHaveCount(0, { message: "should not have any cell" });
});
test("basic grouped grid view", async () => {
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_section.o_grid_section_title").toHaveCount(1, {
message: "A section should be displayed (for the project P1)",
});
expect(".o_grid_section.o_grid_section_title").toHaveText("P1", {
message: "The title of the section should be the project name",
});
expect(".o_grid_section:not(.o_grid_section_title, .o_grid_row_total)").toHaveCount(7, {
message:
"7 cells for the section should be displayed to represent the total per day of the section",
});
expect(".o_grid_section.o_grid_row_total").toHaveCount(1, {
message:
"One cell should be displayed to display the total of the week for the whole section",
});
expect(".o_grid_section.o_grid_row_total").toHaveText("10:00", {
message: "The total of the section should be equal to 10 hours.",
});
expect(".o_grid_row.o_grid_row_title").toHaveCount(2, {
message: "2 rows should be displayed below that section (one per task)",
});
expect(queryAllTexts(".o_grid_row.o_grid_row_title")).toEqual(["None", "BS task"]);
expect(
".o_grid_row:not(.o_grid_row_title,.o_grid_row_total,.o_grid_column_total,.o_grid_add_line)"
).toHaveCount(14, {
message: "7 cells per row should be displayed to get value per day in the current week",
});
expect(
queryAllTexts(
".o_grid_row:not(.o_grid_row_title,.o_grid_row_total,.o_grid_column_total,.o_grid_add_line)"
)
).toEqual([
// row 1
"0:00",
"0:00",
"2:30",
"0:00",
"0:00",
"0:00",
"0:00",
// row 2
"0:00",
"0:00",
"0:00",
"7:30",
"0:00",
"0:00",
"0:00",
]);
expect(".o_grid_row.o_grid_row_total").toHaveCount(2, {
message: "One cell per row should be displayed to display the total of the week",
});
expect(queryAllTexts(".o_grid_row.o_grid_row_total")).toEqual(["2:30", "7:30"]);
expect(".o_grid_search_btn").toHaveCount(0, {
message: "No search button should be displayed in the grid cells.",
});
await hover(".o_grid_section.o_grid_highlightable:eq(1)");
await contains(".o_grid_cell button.o_grid_search_btn").click();
// Click on next period to have no data
await contains(".o_grid_navigation_buttons button span.oi-arrow-left").click();
expect(".o_grid_section").toHaveCount(0);
expect(
".o_grid_row.o_grid_highlightable:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveCount(0, {
message: "No cell should be found because no records is found next week",
});
expect(".o_view_nocontent").toHaveCount(1, {
message: "No content div should be displayed",
});
expect("div.bg-info").toHaveCount(0, {
message: "No column should be the current date since we move in the following week.",
});
});
test("clicking on the info icon on a cell triggers a do_action for section rows", async () => {
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_search_btn").toHaveCount(0, {
message: "No search button should be displayed in the grid cells.",
});
await hover(".o_grid_section.o_grid_highlightable:eq(1)");
await contains(".o_grid_cell button.o_grid_search_btn").click();
});
test("Add/remove groupbys in search view", async () => {
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
searchViewArch: `
`,
});
await toggleSearchBarMenu();
await toggleMenuItem("Task");
await toggleMenuItem("Project");
expect(".o_grid_section").toHaveCount(0);
expect(".o_grid_row_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_row_title")).toEqual(["None\n|\nP1", "BS task\n|\nP1"]);
await contains(".o_grid_navigation_buttons button span.oi-arrow-right").click();
expect(".o_grid_section").toHaveCount(0);
expect(".o_grid_row_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_row_title")).toEqual([
"BS task\n|\nP1",
"Another BS task\n|\nWebocalypse Now",
]);
// Remove the group and check the default groupbys defined in the view are correctly used.
await toggleSearchBarMenu();
await toggleMenuItem("Task");
await toggleMenuItem("Project");
expect(".o_grid_section_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_section_title")).toEqual(["P1", "Webocalypse Now"]);
expect(".o_grid_row_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_row_title")).toEqual(["BS task", "Another BS task"]);
});
test("groupBy with a field", async () => {
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
searchViewArch: `
`,
});
await toggleSearchBarMenu();
await contains("span.o_menu_item:not(.o_add_custom_filter)").click();
expect(".o_grid_section").toHaveCount(0);
expect(".o_grid_row_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_row_title")).toEqual(["None", "BS task"]);
await contains(".o_grid_navigation_buttons button span.oi-arrow-right").click();
expect(".o_grid_section").toHaveCount(0);
expect(".o_grid_row_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_row_title")).toEqual(["BS task", "Another BS task"]);
});
test("groupBy doesn't change the scale", async () => {
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
searchViewArch: `
`,
});
await contains(".scale_button_selection").click();
await contains(".o-dropdown--menu .o_scale_button_month").click();
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Month", {
message: "The active range should be Month",
});
await toggleSearchBarMenu();
await contains("div.o_group_by_menu > span.o_menu_item").click();
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Month", {
message: "The active range should still be Month",
});
});
test("groupBy with column field should not be supported", async () => {
expect.assertions(7);
mockDate("2017-01-25 00:00:00");
onRpc("web_read_group", ({ kwargs }) => {
expect(kwargs.groupby).toEqual(["date:day", "task_id", "project_id"]);
});
mockService("notification", {
add: (message, options) => {
expect(message).toBe(
"Grouping by the field used in the column of the grid view is not possible."
);
expect(options.type).toBe("warning");
},
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
searchViewArch: `
`,
});
await toggleSearchBarMenu();
await toggleMenuItem("Date");
const dateOptionNodes = queryAll(".o_item_option");
await contains(dateOptionNodes[0]).click();
await contains(dateOptionNodes[1]).click();
});
test("DOM keys are unique", async () => {
Line._records = [
{ id: 1, project_id: 31, date: "2017-01-24", unit_amount: 2.5 },
{ id: 3, project_id: 143, date: "2017-01-25", unit_amount: 5.5 },
{ id: 2, project_id: 33, date: "2017-01-25", unit_amount: 2 },
{ id: 4, project_id: 143, date: "2017-01-18", unit_amount: 0 },
{ id: 5, project_id: 142, date: "2017-01-18", unit_amount: 0 },
{ id: 10, project_id: 31, date: "2017-01-18", unit_amount: 0 },
{ id: 12, project_id: 142, date: "2017-01-17", unit_amount: 0 },
{ id: 22, project_id: 33, date: "2017-01-19", unit_amount: 0 },
{ id: 21, project_id: 99, date: "2017-01-19", unit_amount: 0 },
];
Project._records = [
{ id: 31, name: "Rem" },
{ id: 33, name: "Rer" },
{ id: 99, name: "Sar" },
{ id: 142, name: "Sas" },
{ id: 143, name: "Sassy" },
];
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(queryAllTexts(".o_grid_row_title")).toEqual(["Rem", "Rer", "Sassy"]);
await contains(".o_grid_navigation_buttons button span.oi-arrow-left").click();
expect(queryAllTexts(".o_grid_row_title")).toEqual(["Sas", "Rem", "Sassy", "Rer", "Sar"]);
});
test("Group By Selection field", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(queryAllTexts(".o_grid_row_title")).toEqual(["DEF", "GHI"]);
await contains(".o_grid_navigation_buttons button span.oi-arrow-left").click();
expect(queryAllTexts(".o_grid_row_title")).toEqual(["ABC", "DEF"]);
});
test("Create record with Add button in grid view", async () => {
mockDate("2017-02-25 00:00:00");
onRpc("create", (args) => {
expect(args.args[0][0].date).toBe("2017-02-25", {
message: "default date should be the current day",
});
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_row_title").toHaveCount(0);
expect(".modal").toHaveCount(0);
expect(".o_view_nocontent").toHaveCount(0);
await contains(".o_grid_button_add").click();
expect(".modal").toHaveCount(1);
await selectFieldDropdownItem("project_id", "P1");
await selectFieldDropdownItem("task_id", "BS task");
// input unit_amount
await contains(".modal .o_field_widget[name=unit_amount] input").edit("4");
// save
await contains(".modal .modal-footer button.o_form_button_save").click();
expect(".o_grid_row_title").toHaveCount(1, {
message: "the record should be created and a row should be added",
});
expect(".o_grid_row_title").toHaveText("P1\n|\nBS task");
});
test("Create record with Add button in grid view grouped", async () => {
mockDate("2017-02-25 00:00:00");
onRpc("create", (args) => {
expect(args.args[0][0].date).toBe("2017-02-25", {
message: "default date should be the current day",
});
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_row_title").toHaveCount(0);
expect(".modal").toHaveCount(0);
expect(".o_view_nocontent").toHaveCount(0);
await contains(".o_grid_button_add").click();
expect(".modal").toHaveCount(1);
await selectFieldDropdownItem("project_id", "P1");
await selectFieldDropdownItem("task_id", "BS task");
// input unit_amount
await contains(".modal .o_field_widget[name=unit_amount] input").edit("4");
// save
await contains(".modal .modal-footer button.o_form_button_save").click();
expect(".o_grid_section_title").toHaveCount(1, {
message: "the record should be created and a row should be added",
});
expect(".o_grid_section_title").toHaveText("P1");
expect(".o_grid_row_title").toHaveCount(1, {
message: "the record should be created and a row should be added",
});
expect(".o_grid_row_title").toHaveText("BS task");
});
test("switching active range", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Week", {
message: "The default active range should be the first one define in the grid view",
});
expect(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total").toHaveCount(
7,
{
message: "It should have 7 columns (one for each day)",
}
);
await contains(".scale_button_selection").click();
expect(".o-dropdown--menu .o_scale_button_week").toHaveCount(1, {
message: "The week scale should be in the dropdown menu",
});
expect(".o-dropdown--menu .o_scale_button_month").toHaveCount(1, {
message: "The month scale should be in the dropdown menu",
});
await contains(".o-dropdown--menu .o_scale_button_month").click();
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Month", {
message: "The active range should be Month",
});
expect(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)").toHaveCount(
31,
{
message: "It should have 31 columns (one for each day)",
}
);
});
test("clicking on the info icon on a cell triggers a do_action", async () => {
onRpc("get_views", (args) => {
if (args.kwargs.views.find((v) => v[1] === "list")) {
const context = args.kwargs.context;
const expectedContext = {
default_project_id: 31,
default_task_id: 1,
default_date: "2017-01-30",
};
for (const [key, value] of Object.entries(expectedContext)) {
expect(context[key]).toBe(value);
}
}
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_search_btn").toHaveCount(0, {
message: "No search button should be displayed in the grid cells.",
});
await hover(".o_grid_row .o_grid_cell_readonly:eq(1)");
await contains(".o_grid_cell button.o_grid_search_btn").click();
});
test("editing a value", async () => {
onRpc("grid_update_cell", (args) => {
expect(args.model).toBe("analytic.line", {
message: "The update cell should be called in the current model.",
});
const [domain, fieldName, value] = args.args;
const domainExpected = Domain.and([
[
["project_id", "=", 31],
["task_id", "=", 1],
],
[
["date", ">=", "2017-01-29"],
["date", "<", "2017-01-30"],
],
]).toList({});
expect(domain).toEqual(domainExpected);
expect(fieldName).toBe("unit_amount", {
message: "The value updated should be the measure field",
});
expect(value).toBe(2, {
message: "The value should be the one entered by the user, that is 2",
});
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
const cell = queryFirst(".o_grid_row .o_grid_cell_readonly");
const cellContainer = cell.closest(".o_grid_highlightable");
const columnTotal = queryOne(
`.o_grid_row.o_grid_column_total[data-grid-column="${cellContainer.dataset.gridColumn}"]`
);
const [columnTotalHours, columnTotalMinutes] = (
(columnTotal.textContent?.length && columnTotal.textContent.split(":")) || [0, 0]
).map((value) => Number(value));
const rowTotal = queryOne(
`.o_grid_row_total[data-grid-row="${cellContainer.dataset.gridRow}"]`
);
const [rowTotalHours, rowTotalMinutes] = (
(rowTotal.textContent?.length && rowTotal.textContent.split(":")) || [0, 0]
).map((value) => Number(value));
expect(cell).toHaveText("0:00");
await hover(cell);
await runAllTimers();
expect(".o_grid_cell").toHaveCount(1, {
message: "The GridCell component should be mounted on the grid cell hovered.",
});
const gridCellComponentEl = queryFirst(".o_grid_cell");
const gridCell = cell.closest(".o_grid_row");
expect(gridCellComponentEl.style["grid-row"]).toBe(gridCell.style["grid-row"], {
message:
"The GridCell component should be mounted in the same cell than the one hovered in the grid view.",
});
expect(gridCellComponentEl.style["grid-column"]).toBe(gridCell.style["grid-column"], {
message:
"The GridCell component should be mounted in the same cell than the one hovered in the grid view.",
});
expect(gridCellComponentEl.style["z-index"]).toBe("1", {
message:
"The GridCell component should be mounted in the same cell than the one hovered in the grid view.",
});
await contains(".o_grid_cell").click();
await animationFrame();
expect(".o_grid_cell input").toHaveCount(1);
await contains(".o_grid_cell input").edit("2");
await animationFrame();
expect(cell).toHaveText("2:00");
expect(columnTotal).toHaveText(
`${columnTotalHours + 2}:${String(columnTotalMinutes).padStart(2, "0")}`
);
expect(rowTotal).toHaveText(
`${rowTotalHours + 2}:${String(rowTotalMinutes).padStart(2, "0")}`
);
});
test("hide row total", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_row_title").toHaveCount(2);
expect(".o_grid_row_total").toHaveCount(0, { message: "No row total should be displayed" });
expect(".o_grid_column_total").toHaveCount(7, {
message: "Columns total should be displayed",
});
});
test("hide column total", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_row_title").toHaveCount(2);
expect(".o_grid_row_total").toHaveCount(3, { message: "Rows total should be displayed" });
expect(".o_grid_column_total").toHaveCount(0, {
message: " No column total should be displayed",
});
});
test("display bar chart total", async () => {
Line._records.push({
id: 8,
project_id: 142,
task_id: 54,
date: "2017-01-25",
unit_amount: 4,
});
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_row_title").toHaveCount(3);
expect(".o_grid_row_total").toHaveCount(5, { message: "Rows total should be displayed" });
expect(".o_grid_column_total:not(.o_grid_bar_chart_container)").toHaveCount(8, {
message: "8 cells should be visible to display the total per colunm",
});
expect(".o_grid_bar_chart_container").toHaveCount(7, {
message: "The bar chart total container should be displayed (one per column)",
});
expect(".o_grid_bar_chart_total_pill").toHaveCount(2, {
message:
"2 bar charts totals should be displayed because the 5 others columns as a total equals to 0.",
});
expect(queryAllTexts(".o_grid_bar_chart_total_pill")).toEqual(["", ""]);
const cell = queryFirst(".o_grid_row .o_grid_cell_readonly");
expect(cell).toHaveText("0:00");
await hover(cell);
await contains(".o_grid_cell").click();
await animationFrame();
expect(".o_grid_cell input").toHaveCount(1);
await contains(".o_grid_cell input").edit("2");
expect(".o_grid_bar_chart_total_pill").toHaveCount(3, {
message:
"3 bar chart totals should be now displayed because a new column as a total greater than 0.",
});
});
test("row and column are highlighted when hovering a cell", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_row.o_grid_highlightable.bg-700").toHaveCount(0, {
message: "No cell should be highlighted",
});
await hover(".o_grid_row .o_grid_cell_readonly");
await runAllTimers();
expect(
".o_grid_row.o_grid_highlightable.o_grid_highlighted.o_grid_row_highlighted"
).toHaveCount(8, {
message:
"8 cells should be highlighted (the cells in the same rows (title row included))",
});
expect(".o_grid_row_total.o_grid_highlighted.o_grid_row_highlighted").toHaveCount(1, {
message: "The row total should also be highlighted",
});
});
test("grid_anchor stays when navigating", async () => {
// create an action manager to test the interactions with the search view
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[false, "grid"]],
context: {
search_default_project_id: 31,
grid_anchor: "2017-01-31",
},
});
// check first column header
expect(queryAllTexts(".o_grid_column_title")).toEqual([
"Today\nWeek",
"Sun,\nJan 29",
"Mon,\nJan 30",
"Tue,\nJan 31",
"Wed,\nFeb 1",
"Thu,\nFeb 2",
"Fri,\nFeb 3",
"Sat,\nFeb 4",
"Unit Amount",
]);
// move to previous week, and check first column header
await contains(".oi-arrow-left").click();
// check first column header
expect(queryAllTexts(".o_grid_column_title")).toEqual([
"Today\nWeek",
"Sun,\nJan 22",
"Mon,\nJan 23",
"Tue,\nJan 24",
"Wed,\nJan 25",
"Thu,\nJan 26",
"Fri,\nJan 27",
"Sat,\nJan 28",
"Unit Amount",
]);
// remove the filter in the searchview
await contains(".o_facet_remove").click();
expect(queryAllTexts(".o_grid_column_title")).toEqual([
"Today\nWeek",
"Sun,\nJan 22",
"Mon,\nJan 23",
"Tue,\nJan 24",
"Wed,\nJan 25",
"Thu,\nJan 26",
"Fri,\nJan 27",
"Sat,\nJan 28",
"Unit Amount",
]);
});
test("dialog should not close when clicking the link to many2one field", async () => {
// create an action manager to test the interactions with the search view
onRpc("/web/dataset/call_kw/task/get_formview_id", (route, args) => {
return false;
});
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[false, "grid"]],
});
await contains(".o_grid_button_add").click();
await animationFrame();
expect(".modal[role='dialog']").toHaveCount(1);
await selectFieldDropdownItem("task_id", "BS task");
await contains('.modal .o_field_widget[name="task_id"] button.o_external_button').click();
// Clicking somewhere on the form dialog should not close it
expect(".modal[role='dialog']").toHaveCount(2);
await contains(".modal[role='dialog']").click();
expect(".modal[role='dialog']").toHaveCount(2);
});
test("grid with two tasks with same name, and widget", async () => {
Task._records = [
{ id: 1, name: "Awesome task", project_id: 31 },
{ id: 2, name: "Awesome task", project_id: 31 },
];
Line._records = [
{ id: 1, task_id: 1, date: "2017-01-30", unit_amount: 2 },
{ id: 2, task_id: 2, date: "2017-01-31", unit_amount: 5.5 },
];
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[false, "grid"]],
context: { search_default_groupby_task: 1 }, // to avoid creating a new grid view to remove project_id in rows
});
expect(".o_grid_row_title").toHaveCount(2);
expect(queryAllTexts(".o_grid_row_title")).toEqual(["Awesome task", "Awesome task"]);
});
test("test grid cell formatting with float_time widget", async () => {
mockDate("2017-01-24 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
groupBy: ["task_id", "project_id"],
arch: ``,
});
expect(
".o_grid_row.o_grid_highlightable:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveCount(1);
expect(
".o_grid_row.o_grid_highlightable:not(.o_grid_row_title,.o_grid_column_total,.o_grid_row_total)"
).toHaveText("2:30", { message: "Check if the cell is correctly formatted as float time" });
expect(
".o_grid_column_total:not(.o_grid_row_title,.o_grid_row_total,.o_grid_bar_chart_container)"
).toHaveCount(1);
expect(
".o_grid_column_total:not(.o_grid_row_title,.o_grid_row_total,.o_grid_bar_chart_container) span"
).toHaveText("2:30", { message: "check format time is used" });
expect(".o_grid_row.o_grid_highlightable.o_grid_row_total").toHaveCount(1);
expect(".o_grid_row.o_grid_highlightable.o_grid_row_total").toHaveText("2:30", {
message: "check format time is used",
});
});
test("The help content is not displayed instead of the grid with `display_empty` is true in the grid tag", async () => {
mockDate("2022-01-01 00:00:00"); // to be sure no data is found
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_view_nocontent").toHaveCount(0, {
message: "No content div should be displayed",
});
});
test("Test add a line in the grid view", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_button_add:visible").toHaveCount(1, {
message: "'Add a line' control panel button should be visible",
});
expect(queryAllTexts(".o_grid_renderer .o_grid_add_line a")).toEqual(["Add a line"], {
message: "A button `Add a line` should be displayed in the grid view",
});
await contains(".o_grid_renderer .o_grid_add_line a").click();
expect(".modal").toHaveCount(1);
await contains(".modal .modal-footer button.o_form_button_cancel").click();
await contains(".o_grid_navigation_buttons button span.oi-arrow-right").click();
expect(".o_grid_button_add:visible").toHaveCount(1, {
message: "'Add a line' control panel button should be visible",
});
});
test("create/edit disabled for readonly grid view", async () => {
Line._fields.validated = fields.Boolean({
string: "Validation",
aggregator: "bool_or",
});
Line._records.push({
id: 8,
project_id: 142,
task_id: 54,
date: "2017-01-25",
unit_amount: 4,
validated: true,
});
mockDate("2017-01-25 00:00:00");
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
await hover(".o_grid_row .o_grid_cell_readonly");
await runAllTimers();
expect(".o_grid_cell .o_grid_search_btn").toHaveCount(1);
expect(".o_grid_cell.o_field_cursor_disabled").toHaveCount(0, {
message: "The cell should not be in readonly",
});
await hover(".o_grid_row .o_grid_cell_readonly:eq(1)");
await runAllTimers();
expect(".o_grid_cell .o_grid_search_btn").toHaveCount(1);
expect(".o_grid_cell.o_field_cursor_disabled").toHaveCount(1, {
message:
"The cell should be in readonly since at least one timesheet is validated in that cell",
});
await contains("button.o_grid_search_btn").click();
});
test("display the empty grid without None line when there is no data", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
domain: Domain.FALSE.toList({}),
});
expect(".o_grid_section_title").toHaveCount(0, {
message: "No section should be displayed to display 'None'",
});
expect(".o_grid_row_title").toHaveCount(0, {
message: "No row should be added to display 'None'",
});
});
test('ensure the "None" is displayed in multi-level groupby', async () => {
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[1, "grid"]],
context: {
search_default_project_id: 31,
search_default_groupby_task: 1,
search_default_groupby_selection: 1,
grid_anchor: "2017-01-24",
},
});
expect(".o_grid_section").toHaveCount(0, {
message:
"No section should be displayed since the section field is not first in the groupby",
});
expect(".o_grid_row_title:eq(0)").toHaveText("None\n|\nABC", {
message: "'None' should be displayed",
});
});
test("Group By selection field without passing selection field in data", async () => {
Line._records.push({
id: 6,
project_id: 142,
task_id: 12,
date: "2017-01-24",
unit_amount: 7.0,
});
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[1, "grid"]],
context: {
search_default_groupby_selection: 1,
grid_anchor: "2017-01-24",
},
});
expect(".o_grid_row_title:contains(None)").toHaveCount(1, {
message: "'None' should be displayed.",
});
});
test("stop edition when the user clicks outside", async () => {
const arch = Line._views["grid,false"].replace("", '');
await mountView({
type: "grid",
resModel: "analytic.line",
arch,
});
await hover(".o_grid_row .o_grid_cell_readonly:eq(1)");
await contains(".o_grid_cell").click();
await animationFrame();
expect(".o_grid_cell input").toHaveCount(1, { message: "The cell should be in edit mode" });
await contains(".o_grid_view").click();
expect(".o_grid_cell input").toHaveCount(0, {
message: "The GridCell should no longer be visible and so no cell is in edit mode.",
});
});
test("display no content helper when no data and sample data is used (with display_empty='1')", async () => {
const arch = Line._views["grid,false"].replace(
"",
``
);
await mountView({
type: "grid",
resModel: "analytic.line",
arch,
domain: Domain.FALSE.toList({}),
});
expect(".o_view_sample_data").toHaveCount(1, {
message: "The sample data should be displayed since no records is found.",
});
expect(".o_view_nocontent").toHaveCount(1, {
message:
"The action helper should also be displayed since the sample data is displayed even if display_empty='1'.",
});
expect(".o_grid_buttons .o_grid_button_add:visible").toHaveCount(1, {
message:
"The `Add a Line` button should be displayed when no content data is displayed to be able to create a record.",
});
await contains(".o_grid_navigation_buttons span.oi-arrow-right").click();
expect(".o_view_sample_data").toHaveCount(0, {
message:
"The sample data should no longer be displayed since display_empty is true in the grid view",
});
expect(".o_view_nocontent").toHaveCount(0, {
message:
"The no content helper should no longer be displayed since display_empty is true in the grid view.",
});
expect(".o_grid_buttons .o_grid_button_add:visible").toHaveCount(1, {
message: "The `Add a Line` button should be displayed near the `Today` one",
});
expect(".o_grid_grid .o_grid_row.o_grid_add_line.position-md-sticky").toHaveCount(1, {
message:
"The `Add a Line` button should be displayed in the grid view since create_inline='1'",
});
});
test("Only relevant grid rows are rendered with larger recordsets", async () => {
// Setup: generates 100 new tasks and related analytic lines distributed
// in all available projects, deterministically based on their ID.
const { _fields: alFields, _records: analyticLines } = Line;
const { _records: tasks } = Task;
const { _records: projects } = Project;
const selectionValues = alFields.selection_field.selection;
const today = luxon.DateTime.local().toFormat("yyyy-MM-dd");
for (let id = 100; id < 200; id++) {
const projectId = projects[id % projects.length].id;
tasks.push({
id,
name: `BS task #${id}`,
project_id: projectId,
});
analyticLines.push({
id,
project_id: projectId,
task_id: id,
selection_field: selectionValues[id % selectionValues.length][0],
date: today,
unit_amount: (id % 10) + 1, // 1 to 10
});
}
await mountView({
type: "grid",
resModel: "analytic.line",
arch: /* xml */ `
`,
});
/**
* Returns unique "data-grid-row" attributes to check for rows equality
* @returns {string[]}
*/
const getCurrentRows = () => [
...new Set([...grid.children].map((el) => el.dataset.gridRow)),
];
const content = queryOne(".o_content");
const grid = queryOne(".o_grid_grid");
const firstRow = grid.querySelector(".o_grid_column_title");
content.style = "height: 600px; overflow: scroll;";
// This is to ensure that the virtual rows will not be impacted by
// sub-pixel calculations.
await scroll(content, { top: 0 });
await animationFrame();
const initialRows = getCurrentRows();
let currentRows = initialRows;
expect(content.scrollTop).toBe(0, { message: "content should be scrolled to the top" });
expect(content.offsetHeight).toBe(710, { message: "content should have its height fixed" });
// ! This next assertion is important: it ensures that the grid rows are
// ! hard-coded so that the virtual hook can work with it. Adapt this test
// ! accordingly should the row height change.
expect(
grid.clientHeight - firstRow.offsetHeight /* first row is "auto" so we don't count it */
).toBe((tasks.length - 1) /* ignore total row */ * 32 /* base grid row height */, {
message:
"grid content should be the height of its row height times the amount of records",
});
expect(currentRows.length).toBeLessThan(tasks.length, {
message: "not all rows should be displayed",
});
// Scroll to the middle of the grid
await scroll(content, { top: content.scrollHeight / 2 });
await animationFrame();
expect(currentRows).not.toEqual(getCurrentRows(), { message: "rows should be different" });
expect(getCurrentRows().length).toBeLessThan(tasks.length, {
message: "not all rows should be displayed",
});
currentRows = getCurrentRows();
// Scroll to the end of the grid
await scroll(content, { top: content.scrollHeight });
await animationFrame();
expect(currentRows).not.toEqual(getCurrentRows(), { message: "rows should be different" });
expect(getCurrentRows().length).toBeLessThan(tasks.length, {
message: "not all rows should be displayed",
});
// Scroll back to top
await scroll(content, { top: 0 });
await animationFrame();
// FIXME: virtual hook: rows are not exactly the same after scrolling once for some reason
// expect(getCurrentRows()).toEqual(initialRows, {
// message: "rows should be the same as initially",
// });
});
test("Edition navigate with tab/shift+tab and enter key", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
function checkGridCellInRightPlace(expectedGridRow, expectedGridColumn) {
const gridCell = queryOne(".o_grid_cell");
expect(gridCell.dataset.gridRow).toBe(expectedGridRow);
expect(gridCell.dataset.gridColumn).toBe(expectedGridColumn);
}
const firstCell = queryOne(".o_grid_row[data-row='1'][data-column='0']");
expect(firstCell.dataset.gridRow).toBe("2");
expect(firstCell.dataset.gridColumn).toBe("2");
await hover(firstCell, ".o_grid_cell_readonly");
await runAllTimers();
expect(".o_grid_cell").toHaveCount(1, {
message: "The GridCell component should be mounted on the grid cell hovered.",
});
checkGridCellInRightPlace(firstCell.dataset.gridRow, firstCell.dataset.gridColumn);
await contains(".o_grid_cell").click();
await animationFrame();
// Go to the next cell
await press("tab");
await animationFrame();
checkGridCellInRightPlace("2", "3");
// Go to the previous cell
await press("shift+tab");
await animationFrame();
checkGridCellInRightPlace("2", "2");
// Go the cell below
await press("enter");
await animationFrame();
checkGridCellInRightPlace("3", "2");
// Go up since it is the cell in the row
await press("enter");
await animationFrame();
checkGridCellInRightPlace("2", "3");
await press("shift+tab");
await animationFrame();
checkGridCellInRightPlace("2", "2");
// Go to the last editable cell in the grid view since it is the first cell.
await press("shift+tab");
await animationFrame();
checkGridCellInRightPlace("3", "8");
// Go back to the first cell since it is the last cell in grid view.
await press("tab");
await animationFrame();
checkGridCellInRightPlace("2", "2");
// Go to the last editable cell in the grid view since it is the first cell.
await press("shift+tab");
await animationFrame();
checkGridCellInRightPlace("3", "8");
// Go back to the first cell since it is the last cell in grid view.
await press("enter");
await animationFrame();
checkGridCellInRightPlace("2", "2");
});
test("Add custom buttons in grid view", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect("button[name='action_test']").toHaveCount(1, {
message: "The custom button should be visible",
});
expect("button[name='action_test_invisible']").toHaveCount(0);
});
test("date should be grouped by month in year range", async () => {
expect.assertions(1);
onRpc("web_read_group", (args) => {
expect(args.kwargs.groupby).toEqual(["date:month", "project_id", "task_id"]);
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: `
`,
});
});
test("display notification when the update of the grid cell cannot be done", async () => {
onRpc("grid_update_cell", () => {
expect.step("grid_update_cell");
return {
type: "ir.actions.client",
tag: "display_notification",
params: {
message: "test display a notification",
type: "danger",
sticky: false,
},
};
});
mockService("action", {
doAction: (data) => {
if (data.tag === "display_notification") {
expect.step(`notification_${data.params.type}`);
}
},
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
await hover(".o_grid_row .o_grid_cell_readonly");
await runAllTimers();
await contains(".o_grid_cell").click();
await animationFrame();
expect(".o_grid_cell input").toHaveCount(1);
await contains(".o_grid_cell input").edit("2");
expect.verifySteps(["grid_update_cell", "notification_danger"]);
});
test("today should be focused", async () => {
expect.assertions(7);
await mountView({
type: "grid",
resModel: "analytic.line",
arch: `
`,
});
const content = queryOne(".o_content");
content.style.overflow = "scroll";
function expectScrollLeft(shouldBeScrolled) {
if (shouldBeScrolled) {
expect(content.scrollLeft).toBeGreaterThan(0);
content.scrollLeft = 0;
} else {
expect(content.scrollLeft).toBe(0);
}
}
await contains("button.btn-secondary[data-hotkey='v']").click();
await animationFrame();
await contains("span.dropdown-item[data-hotkey='m']").click();
expectScrollLeft(true);
await contains("button.btn-secondary[data-hotkey='n']").click();
expectScrollLeft(false);
await contains("button.btn-secondary[data-hotkey='p']").click();
expectScrollLeft(true);
await contains("button.btn-secondary[data-hotkey='p']").click();
expectScrollLeft(false);
await contains("button.btn-secondary[data-hotkey='t']").click();
expectScrollLeft(true);
await hover(".o_grid_highlightable:not(.o_grid_column_title):not(.o_grid_row_title)");
await contains(".o_grid_cell").click();
await animationFrame();
await contains(".o_grid_cell input").edit("2");
expectScrollLeft(false);
await contains("button.btn-secondary[data-hotkey='v']").click();
await animationFrame();
await contains("span.dropdown-item[data-hotkey='w']").click();
expectScrollLeft(false);
});
test("grid: show/hide weekend will show/hide grid values", async () => {
mockDate("2017-01-25 00:00:00");
patchWithCleanup(browser, {
localStorage: {
setItem(key, value) {
expect.step(`${key}-${value}`);
},
getItem(key) {
if (key === "grid.isWeekendVisible") {
return true;
}
},
},
});
Line._records.push({
id: 6,
project_id: 31,
task_id: 1,
selection_field: "def",
date: "2017-01-22",
unit_amount: 10,
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_grid_section.o_grid_row_total").toHaveText("20:00", {
message: "should show 20h in timesheet",
});
expect(queryAllTexts(".o_grid_row.o_grid_row_total")).toEqual(["17:30", "2:30"]);
expect(".scale_button_selection").toHaveCount(1);
await contains(".scale_button_selection").click();
expect(".o-dropdown--menu span.dropdown-item.active").toHaveCount(2);
await contains(".o-dropdown--menu span.dropdown-item.active:eq(1)").click();
expect(".o_grid_section.o_grid_row_total").toHaveText("10:00", {
message: "should show 10 hours as weekend is hidden",
});
expect(queryAllTexts(".o_grid_row.o_grid_row_total")).toEqual(["7:30", "2:30"]);
expect.verifySteps(["grid.isWeekendVisible-false"]);
});
test("grid: use the context in the action when a record will be created", async () => {
mockDate("2017-02-25 00:00:00");
onRpc("create", (args) => {
expect(args.args[0][0].date).toBe("2017-02-25", {
message: "default date should be the current day",
});
});
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
context: {
default_project_id: 31,
},
});
expect(".o_grid_row_title").toHaveCount(0);
expect(".modal").toHaveCount(0);
expect(".o_view_nocontent").toHaveCount(0);
await contains(".o_grid_button_add").click();
expect(".modal").toHaveCount(1);
expect(".modal div[name=project_id]").toHaveCount(1);
expect(".modal div[name=project_id] input").toHaveValue("P1");
});
test("restore navigationInfo from previous state", async () => {
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[false, "grid"]],
});
await contains(".oi-arrow-left").click();
expect(
queryAllTexts(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)")
).toEqual([
"Sun,\nJan 22",
"Mon,\nJan 23",
"Tue,\nJan 24",
"Wed,\nJan 25",
"Thu,\nJan 26",
"Fri,\nJan 27",
"Sat,\nJan 28",
]);
await hover(".o_grid_row .o_grid_cell_readonly:eq(0)");
await runAllTimers();
await contains(".o_grid_cell button.o_grid_search_btn").click();
await contains(".breadcrumb-item.o_back_button").click();
expect(
queryAllTexts(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)")
).toEqual([
"Sun,\nJan 22",
"Mon,\nJan 23",
"Tue,\nJan 24",
"Wed,\nJan 25",
"Thu,\nJan 26",
"Fri,\nJan 27",
"Sat,\nJan 28",
]);
});
test("export navigationInfo when col is not a range", async () => {
Line._views["grid,false"] = ``;
await mountWithCleanup(WebClient);
await getService("action").doAction({
res_model: "analytic.line",
type: "ir.actions.act_window",
views: [[false, "grid"]],
});
await hover(".o_grid_row .o_grid_cell_readonly:eq(0)");
await contains(".o_grid_cell button.o_grid_search_btn").click();
expect(".o_list_view").toHaveCount(1);
});
test("Scale: scale default is fetched from localStorage", async () => {
patchWithCleanup(browser.localStorage, {
getItem(key) {
if (String(key).startsWith("scaleOf-viewId")) {
return "week";
}
},
setItem(key, value) {
if (key === `scaleOf-viewId-${view.env.config.viewId}`) {
expect.step(`scale_${value}`);
}
},
});
const view = await mountView({
type: "grid",
resModel: "analytic.line",
arch: /* xml */ `
`,
});
expect(".scale_button_selection").toHaveText("Week");
await contains(".o_view_scale_selector .dropdown-toggle").click();
await contains(`.o_scale_button_year`).click();
expect(".scale_button_selection").toHaveText("Year");
expect.verifySteps(["scale_year"]);
});
});
describe.tags("mobile");
describe("grid_view_mobile", () => {
test("basic empty grid view in mobile", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
domain: Domain.FALSE.toList({}),
});
expect(".o_grid_view").toHaveCount(1);
expect(".o_grid_renderer").toHaveCount(1);
expect(".o_control_panel_main_buttons .o_grid_buttons").toHaveCount(1);
expect(".o_grid_custom_buttons").toHaveCount(0);
expect(".o_grid_navigation_buttons").toHaveCount(1);
expect(".o_grid_navigation_buttons button:eq(0)").toHaveText("Today", {
message: "The first navigation button should be the Today one.",
});
expect(".o_grid_navigation_buttons > div > button > span.oi-arrow-left").toHaveCount(1, {
message: "The previous button should be there",
});
expect(".o_grid_navigation_buttons > div > button > span.oi-arrow-right").toHaveCount(1, {
message: "The previous button should be there",
});
expect(".o_view_scale_selector").toHaveCount(1);
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Day", {
message: "The default active range should be the first one define in the grid view",
});
await contains(".scale_button_selection").click();
expect(".o-dropdown--menu .o_scale_button_day").toHaveCount(1, {
message: "The Day scale should be in the dropdown menu",
});
expect(".o-dropdown--menu .o_scale_button_week").toHaveCount(1, {
message: "The week scale should be in the dropdown menu",
});
expect(".o-dropdown--menu .o_scale_button_month").toHaveCount(1, {
message: "The month scale should be in the dropdown menu",
});
expect(".o_grid_column_title.fw-bolder").toHaveCount(1, {
message: "The column title containing the date should be the current date",
});
expect(".o_grid_column_title.fw-bolder").toHaveText("Mon,\nJan 30", {
message: "The current date should be Monday on 30 January 2023",
});
expect(".o_grid_column_title:not(.o_grid_navigation_wrap, .o_grid_row_total)").toHaveCount(
1,
{ message: "It should have 1 column" }
);
expect(".o_grid_column_title.o_grid_row_total").toHaveCount(1, {
message: "It should have 1 column for the total",
});
expect(".o_grid_column_title.o_grid_row_total").toHaveCount(1);
expect(".o_grid_column_title.o_grid_row_total").toHaveText("Unit Amount", {
message: "The column title of row totals should be the string of the measure field",
});
expect(".o_grid_add_line a").toHaveCount(0, {
message:
"No Add a line button should be displayed when create_inline is false (default behavior)",
});
});
test("grid view should open in day range for mobile", async () => {
await mountView({
type: "grid",
resModel: "analytic.line",
arch: ``,
});
expect(".o_view_scale_selector").toHaveCount(1);
expect(".o_view_scale_selector button.scale_button_selection").toHaveText("Day", {
message: "The default active range should be the first one define in the grid view",
});
});
});