import { expect, test } from "@odoo/hoot";
import { click, press, queryAll, queryAllTexts, queryFirst } from "@odoo/hoot-dom";
import { animationFrame } from "@odoo/hoot-mock";
import { defineModels, fields, models, mountView, onRpc } from "@web/../tests/web_test_helpers";
class Partner extends models.Model {
foo = fields.Char({ string: "Foo" });
sequence = fields.Integer({ string: "Sequence", searchable: true });
selection = fields.Selection({
string: "Selection",
selection: [
["normal", "Normal"],
["blocked", "Blocked"],
["done", "Done"],
],
});
_records = [
{ id: 1, foo: "yop", selection: "blocked" },
{ id: 2, foo: "blip", selection: "normal" },
{ id: 4, foo: "abc", selection: "done" },
{ id: 3, foo: "gnap" },
{ id: 5, foo: "blop" },
];
}
defineModels([Partner]);
test("StateSelectionField in form view", async () => {
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_red").toHaveCount(1, {
message: "should have one red status since selection is the second, blocked state",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_green").toHaveCount(0, {
message: "should not have one green status since selection is the second, blocked state",
});
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should not be a dropdown" });
// Click on the status button to make the dropdown appear
await click(".o_field_widget.o_field_state_selection .o_status");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(1, { message: "there should be a dropdown" });
expect(".o-dropdown--menu .dropdown-item").toHaveCount(3, {
message: "there should be three options in the dropdown",
});
expect(".o-dropdown--menu .dropdown-item:nth-child(2)").toHaveClass("active", {
message: "current value has a checkmark",
});
// Click on the first option, "Normal"
await click(".o-dropdown--menu .dropdown-item");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(0, {
message: "there should not be a dropdown anymore",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_red").toHaveCount(0, {
message: "should not have one red status since selection is the first, normal state",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_green").toHaveCount(0, {
message: "should not have one green status since selection is the first, normal state",
});
expect(".o_field_widget.o_field_state_selection span.o_status").toHaveCount(1, {
message: "should have one grey status since selection is the first, normal state",
});
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should still not be a dropdown" });
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_red").toHaveCount(0, {
message: "should still not have one red status since selection is the first, normal state",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_green").toHaveCount(0, {
message:
"should still not have one green status since selection is the first, normal state",
});
expect(".o_field_widget.o_field_state_selection span.o_status").toHaveCount(1, {
message: "should still have one grey status since selection is the first, normal state",
});
// Click on the status button to make the dropdown appear
await click(".o_field_widget.o_field_state_selection .o_status");
await animationFrame();
expect(".o-dropdown--menu .dropdown-item").toHaveCount(3, {
message: "there should be three options in the dropdown",
});
// Click on the last option, "Done"
await click(".o-dropdown--menu .dropdown-item:last-child");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(0, {
message: "there should not be a dropdown anymore",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_red").toHaveCount(0, {
message: "should not have one red status since selection is the third, done state",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_green").toHaveCount(1, {
message: "should have one green status since selection is the third, done state",
});
// save
await click(".o_form_button_save");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(0, {
message: "there should still not be a dropdown anymore",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_red").toHaveCount(0, {
message: "should still not have one red status since selection is the third, done state",
});
expect(".o_field_widget.o_field_state_selection span.o_status.o_status_green").toHaveCount(1, {
message: "should still have one green status since selection is the third, done state",
});
});
test("StateSelectionField with readonly modifier", async () => {
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ ``,
resId: 1,
});
expect(".o_field_state_selection").toHaveClass("o_readonly_modifier");
expect(".dropdown-menu").not.toBeVisible();
await click(".o_field_state_selection span.o_status");
await animationFrame();
expect(".dropdown-menu").not.toBeVisible();
});
test("StateSelectionField for form view with hide_label option", async () => {
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
expect(".o_status_label").toHaveCount(1);
});
test("StateSelectionField for list view with hide_label option", async () => {
onRpc("has_group", () => true);
Partner._fields.graph_type = fields.Selection({
type: "selection",
selection: [
["line", "Line"],
["bar", "Bar"],
],
});
Partner._records[0].graph_type = "bar";
Partner._records[1].graph_type = "line";
await mountView({
type: "list",
resModel: "partner",
arch: /* xml */ `
`,
});
expect(".o_state_selection_cell .o_field_state_selection span.o_status").toHaveCount(10, {
message: "should have ten status selection widgets",
});
const selector =
".o_state_selection_cell .o_field_state_selection[name=selection] span.o_status_label";
expect(selector).toHaveCount(5, { message: "should have five label on selection widgets" });
expect(`${selector}:contains("Done")`).toHaveCount(1, {
message: "should have one Done status label",
});
expect(`${selector}:contains("Normal")`).toHaveCount(3, {
message: "should have three Normal status label",
});
expect(
".o_state_selection_cell .o_field_state_selection[name=graph_type] span.o_status"
).toHaveCount(5, { message: "should have five status selection widgets" });
expect(
".o_state_selection_cell .o_field_state_selection[name=graph_type] span.o_status_label"
).toHaveCount(0, { message: "should not have status label in selection widgets" });
});
test("StateSelectionField in editable list view", async () => {
onRpc("has_group", () => true);
await mountView({
type: "list",
resModel: "partner",
arch: /* xml */ `
`,
});
expect(".o_state_selection_cell .o_field_state_selection span.o_status").toHaveCount(5, {
message: "should have five status selection widgets",
});
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_red"
).toHaveCount(1, { message: "should have one red status" });
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_green"
).toHaveCount(1, { message: "should have one green status" });
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should not be a dropdown" });
// Click on the status button to make the dropdown appear
let cell = queryFirst("tbody td.o_state_selection_cell");
await click(".o_state_selection_cell .o_field_state_selection span.o_status");
await animationFrame();
expect(cell.parentElement).not.toHaveClass("o_selected_row", {
message: "should not be in edit mode since we clicked on the state selection widget",
});
expect(".o-dropdown--menu").toHaveCount(1, { message: "there should be a dropdown" });
expect(".o-dropdown--menu .dropdown-item").toHaveCount(3, {
message: "there should be three options in the dropdown",
});
// Click on the first option, "Normal"
await click(".o-dropdown--menu .dropdown-item");
await animationFrame();
expect(".o_state_selection_cell .o_field_state_selection span.o_status").toHaveCount(5, {
message: "should still have five status selection widgets",
});
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_red"
).toHaveCount(0, { message: "should now have no red status" });
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_green"
).toHaveCount(1, { message: "should still have one green status" });
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should not be a dropdown" });
expect("tr.o_selected_row").toHaveCount(0, { message: "should not be in edit mode" });
// switch to edit mode and check the result
cell = queryFirst("tbody td.o_state_selection_cell");
await click(cell);
await animationFrame();
expect(cell.parentElement).toHaveClass("o_selected_row", {
message: "should now be in edit mode",
});
expect(".o_state_selection_cell .o_field_state_selection span.o_status").toHaveCount(5, {
message: "should still have five status selection widgets",
});
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_red"
).toHaveCount(0, { message: "should now have no red status" });
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_green"
).toHaveCount(1, { message: "should still have one green status" });
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should not be a dropdown" });
// Click on the status button to make the dropdown appear
await click(".o_state_selection_cell .o_field_state_selection span.o_status");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(1, { message: "there should be a dropdown" });
expect(".o-dropdown--menu .dropdown-item").toHaveCount(3, {
message: "there should be three options in the dropdown",
});
// Click on another row
const lastCell = queryAll("tbody td.o_state_selection_cell")[4];
await click(lastCell);
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(0, {
message: "there should not be a dropdown anymore",
});
const firstCell = queryFirst("tbody td.o_state_selection_cell");
expect(firstCell.parentElement).not.toHaveClass("o_selected_row", {
message: "first row should not be in edit mode anymore",
});
expect(lastCell.parentElement).toHaveClass("o_selected_row", {
message: "last row should be in edit mode",
});
// Click on the third status button to make the dropdown appear
await click(".o_state_selection_cell .o_field_state_selection span.o_status:eq(2)");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(1, "there should be a dropdown".msg);
expect(".o-dropdown--menu .dropdown-item").toHaveCount(3, {
message: "there should be three options in the dropdown",
});
// Click on the last option, "Done"
await click(".o-dropdown--menu .dropdown-item:last-child");
await animationFrame();
expect(".o-dropdown--menu").toHaveCount(0, {
message: "there should not be a dropdown anymore",
});
expect(".o_state_selection_cell .o_field_state_selection span.o_status").toHaveCount(5, {
message: "should still have five status selection widgets",
});
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_red"
).toHaveCount(0, { message: "should still have no red status" });
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_green"
).toHaveCount(2, { message: "should now have two green status" });
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should not be a dropdown" });
// save
await click(".o_control_panel_main_buttons .o_list_button_save");
await animationFrame();
expect(".o_state_selection_cell .o_field_state_selection span.o_status").toHaveCount(5, {
message: "should have five status selection widgets",
});
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_red"
).toHaveCount(0, { message: "should have no red status" });
expect(
".o_state_selection_cell .o_field_state_selection span.o_status.o_status_green"
).toHaveCount(2, { message: "should have two green status" });
expect(".o-dropdown--menu").toHaveCount(0, { message: "there should not be a dropdown" });
});
test('StateSelectionField edited by the smart actions "Set kanban state as "', async () => {
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
expect(".o_status_red").toHaveCount(1);
await press(["control", "k"]);
await animationFrame();
expect(`.o_command:contains("Set kanban state as Normal\nALT + D")`).toHaveCount(1);
const doneItem = `.o_command:contains("Set kanban state as Done\nALT + G")`;
expect(doneItem).toHaveCount(1);
await click(doneItem);
await animationFrame();
expect(".o_status_green").toHaveCount(1);
await press(["control", "k"]);
await animationFrame();
expect(`.o_command:contains("Set kanban state as Normal\nALT + D")`).toHaveCount(1);
expect(`.o_command:contains("Set kanban state as Blocked\nALT + F")`).toHaveCount(1);
expect(`.o_command:contains("Set kanban state as Done\nALT + G")`).toHaveCount(0);
});
test("StateSelectionField uses legend_* fields", async () => {
Partner._fields.legend_normal = fields.Char();
Partner._fields.legend_blocked = fields.Char();
Partner._fields.legend_done = fields.Char();
Partner._records[0].legend_normal = "Custom normal";
Partner._records[0].legend_blocked = "Custom blocked";
Partner._records[0].legend_done = "Custom done";
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
await click(".o_status");
await animationFrame();
expect(queryAllTexts(".o-dropdown--menu .dropdown-item")).toEqual([
"Custom normal",
"Custom blocked",
"Custom done",
]);
await click(".dropdown-item .o_status");
await animationFrame();
await click(".o_status");
await animationFrame();
expect(queryAllTexts(".o-dropdown--menu .dropdown-item")).toEqual([
"Custom normal",
"Custom blocked",
"Custom done",
]);
});
test("works when required in a readonly view", async () => {
Partner._records[0].selection = "normal";
Partner._records = [Partner._records[0]];
onRpc("web_save", ({ method }) => expect.step(method));
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
`,
});
expect(".o_status_label").toHaveCount(0);
await click(".o_field_state_selection button");
await animationFrame();
await click(".dropdown-item:eq(2)");
await animationFrame();
expect.verifySteps(["web_save"]);
expect(".o_field_state_selection span").toHaveClass("o_status_green");
});
test("StateSelectionField - auto save record when field toggled", async () => {
onRpc("web_save", ({ method }) => expect.step(method));
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
await click(".o_field_widget.o_field_state_selection .o_status");
await animationFrame();
await click(".dropdown-menu .dropdown-item:last-child");
await animationFrame();
expect.verifySteps(["web_save"]);
});
test("StateSelectionField - prevent auto save with autosave option", async () => {
onRpc("write", ({ method }) => expect.step(method));
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
await click(".o_field_widget.o_field_state_selection .o_status");
await animationFrame();
await click(".dropdown-menu .dropdown-item:last-child");
await animationFrame();
expect.verifySteps([]);
});
test("StateSelectionField - hotkey handling when there are more than 3 options available", async () => {
Partner._fields.selection = fields.Selection({
string: "Selection",
selection: [
["normal", "Normal"],
["blocked", "Blocked"],
["done", "Done"],
["martin", "Martin"],
["martine", "Martine"],
],
});
Partner._records[0].selection = null;
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `
`,
resId: 1,
});
await click(".o_field_widget.o_field_state_selection .o_status");
await animationFrame();
expect(".dropdown-menu .dropdown-item").toHaveCount(5, {
message: "Five choices are displayed",
});
await press(["control", "k"]);
await animationFrame();
expect(".o_command#o_command_2").toHaveText("Set kanban state as Done\nALT + G", {
message: "hotkey and command are present",
});
expect(".o_command#o_command_4").toHaveText("Set kanban state as Martine", {
message: "no hotkey is present, but the command exists",
});
await click(".o_command#o_command_2");
await animationFrame();
expect(".o_field_state_selection .o_status").toHaveClass("o_status_green", {
message: "green color and Done state have been set",
});
});