import { expect, test } from "@odoo/hoot"; import { queryAllTexts } from "@odoo/hoot-dom"; import { runAllTimers } from "@odoo/hoot-mock"; import { clickSave, contains, defineModels, fields, models, mountView, onRpc, } from "@web/../tests/web_test_helpers"; class Partner extends models.Model { int_field = fields.Integer({ sortable: true }); timmy = fields.Many2many({ string: "pokemon", relation: "partner.type" }); p = fields.One2many({ string: "one2many field", relation: "partner", relation_field: "trululu", }); trululu = fields.Many2one({ relation: "partner" }); _records = [{ id: 1, int_field: 10, p: [1] }]; } class PartnerType extends models.Model { name = fields.Char(); _records = [ { id: 12, name: "gold" }, { id: 14, name: "silver" }, ]; } defineModels([Partner, PartnerType]); test("Many2ManyCheckBoxesField", async () => { Partner._records[0].timmy = [12]; const commands = [[[4, 14]], [[3, 12]]]; onRpc("web_save", (args) => { expect.step("web_save"); expect(args.args[1].timmy).toEqual(commands.shift()); }); await mountView({ type: "form", resModel: "partner", resId: 1, arch: `
`, }); expect("div.o_field_widget div.form-check").toHaveCount(2); expect("div.o_field_widget div.form-check input:eq(0)").toBeChecked(); expect("div.o_field_widget div.form-check input:eq(1)").not.toBeChecked(); expect("div.o_field_widget div.form-check input:disabled").toHaveCount(0); // add a m2m value by clicking on input await contains("div.o_field_widget div.form-check input:eq(1)").click(); await runAllTimers(); await clickSave(); expect("div.o_field_widget div.form-check input:checked").toHaveCount(2); // remove a m2m value by clinking on label await contains("div.o_field_widget div.form-check > label").click(); await runAllTimers(); await clickSave(); expect("div.o_field_widget div.form-check input:eq(0)").not.toBeChecked(); expect("div.o_field_widget div.form-check input:eq(1)").toBeChecked(); expect.verifySteps(["web_save", "web_save"]); }); test("Many2ManyCheckBoxesField (readonly)", async () => { Partner._records[0].timmy = [12]; await mountView({ type: "form", resModel: "partner", resId: 1, arch: ` `, }); expect("div.o_field_widget div.form-check").toHaveCount(2, { message: "should have fetched and displayed the 2 values of the many2many", }); expect("div.o_field_widget div.form-check input:disabled").toHaveCount(2, { message: "the checkboxes should be disabled", }); await contains("div.o_field_widget div.form-check > label:eq(1)").click(); expect("div.o_field_widget div.form-check input:eq(0)").toBeChecked(); expect("div.o_field_widget div.form-check input:eq(1)").not.toBeChecked(); }); test("Many2ManyCheckBoxesField does not read added record", async () => { Partner._records[0].timmy = []; onRpc((args) => { expect.step(args.method); }); await mountView({ type: "form", resModel: "partner", resId: 1, arch: ` `, }); expect("div.o_field_widget div.form-check").toHaveCount(2); expect(queryAllTexts(".o_field_widget .form-check-label")).toEqual(["gold", "silver"]); expect("div.o_field_widget div.form-check input:checked").toHaveCount(0); await contains("div.o_field_widget div.form-check input").click(); await runAllTimers(); expect("div.o_field_widget div.form-check").toHaveCount(2); expect(queryAllTexts(".o_field_widget .form-check-label")).toEqual(["gold", "silver"]); expect("div.o_field_widget div.form-check input:checked").toHaveCount(1); await clickSave(); expect("div.o_field_widget div.form-check").toHaveCount(2); expect(queryAllTexts(".o_field_widget .form-check-label")).toEqual(["gold", "silver"]); expect("div.o_field_widget div.form-check input:checked").toHaveCount(1); expect.verifySteps(["get_views", "web_read", "name_search", "web_save"]); }); test("Many2ManyCheckBoxesField: start non empty, then remove twice", async () => { Partner._records[0].timmy = [12, 14]; await mountView({ type: "form", resModel: "partner", resId: 1, arch: ` `, }); await contains("div.o_field_widget div.form-check input:eq(0)").click(); await contains("div.o_field_widget div.form-check input:eq(1)").click(); await runAllTimers(); await clickSave(); expect("div.o_field_widget div.form-check input:eq(0)").not.toBeChecked(); expect("div.o_field_widget div.form-check input:eq(1)").not.toBeChecked(); }); test("Many2ManyCheckBoxesField: values are updated when domain changes", async () => { await mountView({ type: "form", resModel: "partner", resId: 1, arch: ` `, }); expect(".o_field_widget[name='int_field'] input").toHaveValue("10"); expect(".o_field_widget[name='timmy'] .form-check").toHaveCount(2); expect(".o_field_widget[name='timmy']").toHaveText("gold\nsilver"); await contains(".o_field_widget[name='int_field'] input").edit(13); expect(".o_field_widget[name='timmy'] .form-check").toHaveCount(1); expect(".o_field_widget[name='timmy']").toHaveText("silver"); }); test("Many2ManyCheckBoxesField with 40+ values", async () => { // 40 is the default limit for x2many fields. However, the many2many_checkboxes is a // special field that fetches its data through the fetchSpecialData mechanism, and it // uses the name_search server-side limit of 100. This test comes with a fix for a bug // that occurred when the user (un)selected a checkbox that wasn't in the 40 first checkboxes, // because the piece of data corresponding to that checkbox hadn't been processed by the // BasicModel, whereas the code handling the change assumed it had. expect.assertions(3); const records = []; for (let id = 1; id <= 90; id++) { records.push({ id, name: `type ${id}`, }); } PartnerType._records = records; Partner._records[0].timmy = records.map((r) => r.id); onRpc("web_save", ({ args }) => { expect(args[1].timmy).toEqual([[3, records[records.length - 1].id]]); }); await mountView({ type: "form", resModel: "partner", resId: 1, arch: ` `, }); expect(".o_field_widget[name='timmy'] input[type='checkbox']:checked").toHaveCount(90); // toggle the last value await contains(".o_field_widget[name='timmy'] input[type='checkbox']:last").click(); await runAllTimers(); await clickSave(); expect(".o_field_widget[name='timmy'] input[type='checkbox']:last").not.toBeChecked(); }); test("Many2ManyCheckBoxesField with 100+ values", async () => { // The many2many_checkboxes widget limits the displayed values to 100 (this is the // server-side name_search limit). This test encodes a scenario where there are more than // 100 records in the co-model, and all values in the many2many relationship aren't // displayed in the widget (due to the limit). If the user (un)selects a checkbox, we don't // want to remove all values that aren't displayed from the relation. expect.assertions(5); const records = []; for (let id = 1; id < 150; id++) { records.push({ id, name: `type ${id}`, }); } PartnerType._records = records; Partner._records[0].timmy = records.map((r) => r.id); onRpc("web_save", ({ args }) => { expect(args[1].timmy).toEqual([[3, records[0].id]]); expect.step("web_save"); }); onRpc("name_search", () => { expect.step("name_search"); }); await mountView({ type: "form", resModel: "partner", resId: 1, arch: ` `, }); expect(".o_field_widget[name='timmy'] input[type='checkbox']").toHaveCount(100); expect(".o_field_widget[name='timmy'] input[type='checkbox']").toBeChecked(); // toggle the first value await contains(".o_field_widget[name='timmy'] input[type='checkbox']").click(); await runAllTimers(); await clickSave(); expect(".o_field_widget[name='timmy'] input[type='checkbox']").not.toBeChecked(); expect.verifySteps(["name_search", "web_save"]); }); test("Many2ManyCheckBoxesField in a one2many", async () => { expect.assertions(3); PartnerType._records.push({ id: 15, name: "bronze" }); Partner._records[0].timmy = [14, 15]; onRpc("web_save", ({ args }) => { expect(args[1]).toEqual({ p: [ [ 1, 1, { timmy: [ [4, 12], [3, 14], ], }, ], ], }); }); await mountView({ type: "form", resModel: "partner", arch: ` `, resId: 1, }); await contains(".o_data_cell").click(); // edit the timmy field by (un)checking boxes on the widget await contains(".modal .form-check-input:eq(0)").click(); expect(".modal .form-check-input:eq(0)").toBeChecked(); await contains(".modal .form-check-input:eq(1)").click(); expect(".modal .form-check-input:eq(1)").not.toBeChecked(); await contains(".modal .o_form_button_save").click(); await clickSave(); }); test("Many2ManyCheckBoxesField with default values", async () => { expect.assertions(7); Partner._fields.timmy = fields.Many2many({ string: "pokemon", relation: "partner.type", default: [[4, 3]], }); PartnerType._records.push({ id: 3, name: "bronze" }); onRpc("web_save", ({ args }) => { expect(args[1].timmy).toEqual([[4, 12]], { message: "correct values should have been sent to create", }); }); await mountView({ type: "form", resModel: "partner", arch: `