import { clickSave, contains, defineModels, fields, models, mountView, onRpc, serverState, } from "@web/../tests/web_test_helpers"; import { expect, test } from "@odoo/hoot"; import { animationFrame } from "@odoo/hoot-mock"; import { click, edit, queryAll, queryFirst } from "@odoo/hoot-dom"; import { pointerDown } from "../../../lib/hoot-dom/hoot-dom"; const RED_TEXT = /* html */ `
some text
`; const GREEN_TEXT = /* html */ `
hello
`; const BLUE_TEXT = /* html */ `
hello world
`; class Partner extends models.Model { txt = fields.Html({ string: "txt", trim: true }); _records = [{ id: 1, txt: RED_TEXT }]; } defineModels([Partner]); test("html fields are correctly rendered in form view (readonly)", async () => { await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); expect("div.kek").toHaveCount(1); expect(".o_field_html .kek").toHaveStyle({ color: "rgb(255, 0, 0)" }); expect(".o_field_html").toHaveText("some text"); }); test("html field with required attribute", async () => { await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); expect(".o_field_html textarea").toHaveCount(1, { message: "should have a text area" }); await click(".o_field_html textarea"); await edit(""); await animationFrame(); expect(".o_field_html textarea").toHaveValue(""); await clickSave(); expect(".o_notification_title").toHaveText("Invalid fields:"); expect(queryFirst(".o_notification_content")).toHaveInnerHTML("
  • txt
"); }); test("html fields are correctly rendered (edit)", async () => { onRpc("has_group", () => true); await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); expect(".o_field_html textarea").toHaveCount(1, { message: "should have a text area" }); expect(".o_field_html textarea").toHaveValue(RED_TEXT); await click(".o_field_html textarea"); await edit(GREEN_TEXT); await animationFrame(); expect(".o_field_html textarea").toHaveValue(GREEN_TEXT); expect(".o_field_html .kek").toHaveCount(0); await edit(BLUE_TEXT); await animationFrame(); expect(".o_field_html textarea").toHaveValue(BLUE_TEXT); }); test("html fields are correctly rendered in list view", async () => { onRpc("has_group", () => true); await mountView({ type: "list", resModel: "partner", arch: /* xml */ ``, }); expect(".o_data_row [name='txt']").toHaveText("some text"); expect(".o_data_row [name='txt'] .kek").toHaveStyle({ color: "rgb(255, 0, 0)" }); await click(".o_data_row [name='txt']"); await animationFrame(); expect(".o_data_row [name='txt'] textarea").toHaveValue( '
some text
' ); }); test("html field displays an empty string for the value false in list view", async () => { Partner._records[0].txt = false; onRpc("has_group", () => true); await mountView({ type: "list", resModel: "partner", arch: /* xml */ ``, }); expect(".o_data_row [name='txt']").toHaveText(""); await click(".o_data_row [name='txt']"); await animationFrame(); expect(".o_data_row [name='txt'] textarea").toHaveValue(""); }); test("html fields are correctly rendered in kanban view", async () => { await mountView({ type: "kanban", resModel: "partner", arch: /* xml */ ` `, }); expect(".kek").toHaveText("some text"); expect(".kek").toHaveStyle({ color: "rgb(255, 0, 0)" }); }); test("field html translatable", async () => { expect.assertions(10); Partner._fields.txt = fields.Html({ string: "txt", trim: true, translate: true }); serverState.lang = "en_US"; serverState.multiLang = true; onRpc("has_group", () => true); onRpc("get_field_translations", function ({ args }) { expect(args).toEqual([[1], "txt"], { message: "should translate the txt field of the record", }); return [ [ { lang: "en_US", source: "first paragraph", value: "first paragraph" }, { lang: "en_US", source: "second paragraph", value: "second paragraph", }, { lang: "fr_BE", source: "first paragraph", value: "", }, { lang: "fr_BE", source: "second paragraph", value: "deuxième paragraphe", }, ], { translation_type: "char", translation_show_source: true }, ]; }); onRpc("get_installed", () => { return [ ["en_US", "English"], ["fr_BE", "French (Belgium)"], ]; }); onRpc("update_field_translations", ({ args }) => { expect(args).toEqual( [ [1], "txt", { en_US: { "first paragraph": "first paragraph modified" }, fr_BE: { "first paragraph": "premier paragraphe modifié", "second paragraph": "deuxième paragraphe modifié", }, }, ], { message: "the new translation value should be written" } ); return []; }); await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); expect("[name=txt] textarea").toHaveClass("o_field_translate"); await contains("[name=txt] textarea").click(); expect(".o_field_html .btn.o_field_translate").toHaveCount(1, { message: "should have a translate button", }); expect(".o_field_html .btn.o_field_translate").toHaveText("EN", { message: "the button should have as test the current language", }); await click(".o_field_html .btn.o_field_translate"); await animationFrame(); expect(".modal").toHaveCount(1, { message: "a translate modal should be visible" }); expect(".translation").toHaveCount(4, { message: "four rows should be visible" }); const translations = queryAll(".modal .o_translation_dialog .translation input"); const enField1 = translations[0]; expect(enField1).toHaveValue("first paragraph", { message: "first part of english translation should be filled", }); await click(enField1); await edit("first paragraph modified"); const frField1 = translations[2]; expect(frField1).toHaveValue("", { message: "first part of french translation should not be filled", }); await click(frField1); await edit("premier paragraphe modifié"); const frField2 = translations[3]; expect(frField2).toHaveValue("deuxième paragraphe", { message: "second part of french translation should be filled", }); await click(frField2); await edit("deuxième paragraphe modifié"); await click(".modal button.btn-primary"); // save await animationFrame(); }); test("html fields: spellcheck is disabled on blur", async () => { await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); const textarea = queryFirst(".o_field_html textarea"); expect(textarea).toHaveProperty("spellcheck", true, { message: "by default, spellcheck is enabled", }); await click(textarea); await edit("nev walue"); await pointerDown(document.body); await animationFrame(); expect(textarea).toHaveProperty("spellcheck", false, { message: "spellcheck is disabled once the field has lost its focus", }); await pointerDown(textarea); expect(textarea).toHaveProperty("spellcheck", true, { message: "spellcheck is re-enabled once the field is focused", }); }); test("Setting an html field to empty string is saved as a false value", async () => { expect.assertions(1); onRpc("web_save", ({ args }) => { expect(args[1].txt).toBe(false, { message: "the txt value should be false" }); }); await mountView({ type: "form", resModel: "partner", arch: /* xml */ `
`, resId: 1, }); await click(".o_field_widget[name=txt] textarea"); await edit(""); await clickSave(); }); test("html field: correct value is used to evaluate the modifiers", async () => { Partner._fields.foo = fields.Char({ string: "foo", onChange: (obj) => { if (obj.foo === "a") { obj.txt = false; } else if (obj.foo === "b") { obj.txt = ""; } }, }); Partner._records[0].foo = false; Partner._records[0].txt = false; await mountView({ type: "form", resModel: "partner", resId: 1, arch: /* xml */ `
`, }); expect("[name='txt'] textarea").toHaveCount(1); await click("[name='foo'] input"); await edit("a", { confirm: "enter" }); await animationFrame(); expect("[name='txt'] textarea").toHaveCount(1); await edit("b", { confirm: "enter" }); await animationFrame(); expect("[name='txt'] textarea").toHaveCount(0); });