Odoo18-Base/addons/web/static/tests/views/fields/selection_field.test.js
2025-01-06 10:57:38 +07:00

468 lines
14 KiB
JavaScript

import { expect, test } from "@odoo/hoot";
import {
click,
pointerDown,
queryAll,
queryAllValues,
queryFirst,
queryOne,
select,
} from "@odoo/hoot-dom";
import { animationFrame } from "@odoo/hoot-mock";
import {
clickSave,
defineModels,
fields,
models,
mountView,
onRpc,
} from "@web/../tests/web_test_helpers";
class Partner extends models.Model {
display_name = fields.Char({ string: "Displayed name" });
int_field = fields.Integer({ string: "int_field" });
trululu = fields.Many2one({ string: "Trululu", relation: "partner" });
product_id = fields.Many2one({ string: "Product", relation: "product" });
color = fields.Selection({
selection: [
["red", "Red"],
["black", "Black"],
],
default: "red",
string: "Color",
});
_records = [
{
id: 1,
display_name: "first record",
int_field: 10,
trululu: 4,
},
{
id: 2,
display_name: "second record",
int_field: 9,
trululu: 1,
product_id: 37,
},
{
id: 4,
display_name: "aaa",
},
];
}
class Product extends models.Model {
name = fields.Char({ string: "Product Name" });
_records = [
{
id: 37,
display_name: "xphone",
},
{
id: 41,
display_name: "xpad",
},
];
}
defineModels([Partner, Product]);
test("SelectionField in a list view", async () => {
Partner._records.forEach((r) => (r.color = "red"));
onRpc("has_group", () => true);
await mountView({
type: "list",
resModel: "partner",
arch: '<list string="Colors" editable="top"><field name="color"/></list>',
});
expect(".o_data_row").toHaveCount(3);
await click(".o_data_cell");
await animationFrame();
const td = queryFirst("tbody tr.o_selected_row td:not(.o_list_record_selector)");
expect(queryOne("select", { root: td })).toHaveCount(1, {
message: "td should have a child 'select'",
});
expect(td.children).toHaveCount(1, { message: "select tag should be only child of td" });
});
test("SelectionField, edition and on many2one field", async () => {
Partner._onChanges.product_id = () => {};
Partner._records[0].product_id = 37;
Partner._records[0].trululu = false;
onRpc(({ method }) => expect.step(method));
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ `
<form>
<field name="product_id" widget="selection" />
<field name="trululu" widget="selection" />
<field name="color" widget="selection" />
</form>`,
});
expect("select").toHaveCount(3);
expect(".o_field_widget[name='product_id'] select option[value='37']").toHaveCount(1, {
message: "should have fetched xphone option",
});
expect(".o_field_widget[name='product_id'] select option[value='41']").toHaveCount(1, {
message: "should have fetched xpad option",
});
expect(".o_field_widget[name='product_id'] select").toHaveValue("37", {
message: "should have correct product_id value",
});
expect(".o_field_widget[name='trululu'] select").toHaveValue("false", {
message: "should not have any value in trululu field",
});
await click(".o_field_widget[name='product_id'] select");
await select("41");
await animationFrame();
expect(".o_field_widget[name='product_id'] select").toHaveValue("41", {
message: "should have a value of xphone",
});
expect(".o_field_widget[name='color'] select").toHaveValue('"red"', {
message: "should have correct value in color field",
});
expect.verifySteps(["get_views", "web_read", "name_search", "name_search", "onchange"]);
});
test("unset selection field with 0 as key", async () => {
// The server doesn't make a distinction between false value (the field
// is unset), and selection 0, as in that case the value it returns is
// false. So the client must convert false to value 0 if it exists.
Partner._fields.selection = fields.Selection({
selection: [
[0, "Value O"],
[1, "Value 1"],
],
});
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ '<form edit="0"><field name="selection" /></form>',
});
expect(".o_field_widget").toHaveText("Value O", {
message: "the displayed value should be 'Value O'",
});
expect(".o_field_widget").not.toHaveClass("o_field_empty", {
message: "should not have class o_field_empty",
});
});
test("unset selection field with string keys", async () => {
// The server doesn't make a distinction between false value (the field
// is unset), and selection 0, as in that case the value it returns is
// false. So the client must convert false to value 0 if it exists. In
// this test, it doesn't exist as keys are strings.
Partner._fields.selection = fields.Selection({
selection: [
["0", "Value O"],
["1", "Value 1"],
],
});
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ '<form edit="0"><field name="selection" /></form>',
});
expect(".o_field_widget").toHaveText("", { message: "there should be no displayed value" });
expect(".o_field_widget").toHaveClass("o_field_empty", {
message: "should have class o_field_empty",
});
});
test("unset selection on a many2one field", async () => {
expect.assertions(1);
onRpc("web_save", ({ args }) => {
expect(args[1].trululu).toBe(false, {
message: "should send 'false' as trululu value",
});
});
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ '<form><field name="trululu" widget="selection" /></form>',
});
await click(".o_form_view select");
await select("false");
await animationFrame();
await clickSave();
await animationFrame();
});
test("field selection with many2ones and special characters", async () => {
// edit the partner with id=4
Partner._records[2].display_name = "<span>hey</span>";
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ '<form><field name="trululu" widget="selection" /></form>',
});
expect("select option[value='4']").toHaveText("<span>hey</span>");
});
test("required selection widget should not have blank option", async () => {
Partner._fields.feedback_value = fields.Selection({
required: true,
selection: [
["good", "Good"],
["bad", "Bad"],
],
default: "good",
string: "Good",
});
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ `
<form>
<field name="feedback_value" />
<field name="color" required="feedback_value == 'bad'" />
</form>`,
});
expect(queryAll(".o_field_widget[name='color'] option").map((n) => n.style.display)).toEqual([
"",
"",
"",
]);
expect(
queryAll(".o_field_widget[name='feedback_value'] option").map((n) => n.style.display)
).toEqual(["none", "", ""]);
// change value to update widget modifier values
await click(".o_field_widget[name='feedback_value'] select");
await select('"bad"');
await animationFrame();
expect(queryAll(".o_field_widget[name='color'] option").map((n) => n.style.display)).toEqual([
"none",
"",
"",
]);
});
test("required selection widget should have only one blank option", async () => {
Partner._fields.feedback_value = fields.Selection({
required: true,
selection: [
["good", "Good"],
["bad", "Bad"],
],
default: "good",
string: "Good",
});
Partner._fields.color = fields.Selection({
selection: [
[false, ""],
["red", "Red"],
["black", "Black"],
],
default: "red",
string: "Color",
});
await mountView({
type: "form",
resModel: "partner",
resId: 1,
arch: /* xml */ `
<form>
<field name="feedback_value" />
<field name="color" required="feedback_value == 'bad'" />
</form>`,
});
expect(".o_field_widget[name='color'] option").toHaveCount(3, {
message: "Three options in non required field (one blank option)",
});
// change value to update widget modifier values
await click(".o_field_widget[name='feedback_value'] select");
await select('"bad"');
await animationFrame();
expect(queryAll(".o_field_widget[name='color'] option").map((n) => n.style.display)).toEqual([
"none",
"",
"",
]);
});
test("selection field with placeholder", async () => {
await mountView({
type: "form",
resModel: "partner",
arch: /* xml */ `<form><field name="trululu" widget="selection" placeholder="Placeholder"/></form>`,
});
expect(".o_field_widget[name='trululu'] select option:first").toHaveText("Placeholder");
expect(".o_field_widget[name='trululu'] select option:first").toHaveValue("false");
});
test("SelectionField in kanban view", async () => {
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
<kanban>
<templates>
<t t-name="card">
<field name="color" widget="selection" />
</t>
</templates>
</kanban>`,
domain: [["id", "=", 1]],
});
expect(".o_field_widget[name='color'] select").toHaveCount(1, {
message: "SelectionKanbanField widget applied to selection field",
});
expect(".o_field_widget[name='color'] option").toHaveCount(3, {
message: "Three options are displayed (one blank option)",
});
expect(queryAllValues(".o_field_widget[name='color'] option")).toEqual([
"false",
'"red"',
'"black"',
]);
});
test("SelectionField - auto save record in kanban view", async () => {
onRpc("web_save", ({ method }) => expect.step(method));
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
<kanban>
<templates>
<t t-name="card">
<field name="color" widget="selection" />
</t>
</templates>
</kanban>`,
domain: [["id", "=", 1]],
});
await click(".o_field_widget[name='color'] select");
await select('"black"');
await animationFrame();
expect.verifySteps(["web_save"]);
});
test("SelectionField don't open form view on click in kanban view", async function (assert) {
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
<kanban>
<templates>
<t t-name="card">
<field name="color" widget="selection" />
</t>
</templates>
</kanban>`,
domain: [["id", "=", 1]],
selectRecord: () => {
expect.step("selectRecord");
},
});
await click(".o_field_widget[name='color'] select");
await animationFrame();
expect.verifySteps([]);
});
test("SelectionField is disabled if field readonly", async () => {
Partner._fields.color = fields.Selection({
selection: [
["red", "Red"],
["black", "Black"],
],
default: "red",
string: "Color",
readonly: true,
});
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
<kanban>
<templates>
<t t-name="card">
<field name="color" widget="selection" />
</t>
</templates>
</kanban>
`,
domain: [["id", "=", 1]],
});
expect(".o_field_widget[name='color'] span").toHaveCount(1, {
message: "field should be readonly",
});
});
test("SelectionField is disabled with a readonly attribute", async () => {
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
<kanban>
<templates>
<t t-name="card">
<field name="color" widget="selection" readonly="1" />
</t>
</templates>
</kanban>
`,
domain: [["id", "=", 1]],
});
expect(".o_field_widget[name='color'] span").toHaveCount(1, {
message: "field should be readonly",
});
});
test("SelectionField in kanban view with handle widget", async () => {
// When records are draggable, most pointerdown events are default prevented. This test
// comes with a fix that blacklists "select" elements, i.e. pointerdown events on such
// elements aren't default prevented, because if they were, the select element can't be
// opened. The test is a bit artificial but there's no other way to test the scenario, as
// using editSelect simply triggers a "change" event, which obviously always works.
await mountView({
type: "kanban",
resModel: "partner",
arch: /* xml */ `
<kanban>
<templates>
<t t-name="card">
<field name="color" widget="selection"/>
</t>
</templates>
</kanban>`,
});
const events = await pointerDown(".o_kanban_record .o_field_widget[name=color] select");
await animationFrame();
expect(events.get("pointerdown").defaultPrevented).toBe(false);
});