diff --git a/addons/account/static/src/js/tours/account.js b/addons/account/static/src/js/tours/account.js index 010a156432a..b2b2f1ca97d 100644 --- a/addons/account/static/src/js/tours/account.js +++ b/addons/account/static/src/js/tours/account.js @@ -86,7 +86,7 @@ tour.register('account_tour', { ...tour.stepUtils.saveForm(), { trigger: "button[name=action_post]", - extra_trigger: "button.o_form_button_edit", + extra_trigger: "button.o_form_button_create", content: _t("Once your invoice is ready, press CONFIRM."), }, { trigger: "button[name=action_invoice_sent]", diff --git a/addons/account/static/tests/tours/tax_group_tests.js b/addons/account/static/tests/tours/tax_group_tests.js index 7d7d0b33329..4133825b607 100644 --- a/addons/account/static/tests/tours/tax_group_tests.js +++ b/addons/account/static/tests/tours/tax_group_tests.js @@ -63,11 +63,6 @@ tour.register('account_tax_group', { content: "Save the account move", trigger: '.o_form_button_save', }, - // Edit account.move - { - content: "Edit the account move", - trigger: '.o_form_button_edit', - }, // Edit tax group amount { content: "Edit tax group amount", diff --git a/addons/analytic/static/tests/analytic_distribution_tests.js b/addons/analytic/static/tests/analytic_distribution_tests.js index 1ef871906e8..8924c1f42b2 100644 --- a/addons/analytic/static/tests/analytic_distribution_tests.js +++ b/addons/analytic/static/tests/analytic_distribution_tests.js @@ -2,7 +2,6 @@ import { click, - clickEdit, getFixture, } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; @@ -139,8 +138,6 @@ QUnit.module("Analytic", (hooks) => { "should have rendered 'FI 69.7%'" ); - await clickEdit(target); - assert.containsN( target, ".o_delete", diff --git a/addons/base_iban/static/src/tests/iban_widget_test.js b/addons/base_iban/static/src/tests/iban_widget_test.js index b3848d2d27f..769fae6159c 100644 --- a/addons/base_iban/static/src/tests/iban_widget_test.js +++ b/addons/base_iban/static/src/tests/iban_widget_test.js @@ -1,6 +1,6 @@ /** @odoo-module **/ import { start, startServer } from '@mail/../tests/helpers/test_utils'; -import { click, clickEdit, clickSave, editInput, getFixture } from "@web/../tests/helpers/utils"; +import { click, clickSave, editInput, getFixture } from "@web/../tests/helpers/utils"; import { DELAY } from "@base_iban/components/iban_widget/iban_widget"; @@ -55,8 +55,6 @@ QUnit.module('Fields', {}, function () { QUnit.test('Iban Widget full flow', async assert => { const { target, advanceTime, afterNextRender } = await openPreparedView(assert, [validIban], ""); - assert.containsNone(target, ".o_iban", "Shouldn't display any validation icon while not editing"); - await clickEdit(target); assert.containsNone(target, ".o_iban", "Shouldn't display any validation icon while not editing a specific line"); @@ -73,10 +71,6 @@ QUnit.module('Fields', {}, function () { await clickSave(target); assert.containsNone(target, ".o_iban", "Shouldn't display any validation while not editing"); - await clickEdit(target); - assert.containsNone(target, ".o_iban", - "Shouldn't display any validation icon while not editing a specific line"); - await click(target, "td.o_iban_cell"); await afterNextRender(() => advanceTime(DELAY)); assert.containsOnce(target, "i.fa.fa-times.o_iban_fail", "The validation icon should be present while clicking on an already filled IBAN"); diff --git a/addons/crm/static/tests/crm_rainbowman_tests.js b/addons/crm/static/tests/crm_rainbowman_tests.js index 15d6836ed3d..82723d2223d 100644 --- a/addons/crm/static/tests/crm_rainbowman_tests.js +++ b/addons/crm/static/tests/crm_rainbowman_tests.js @@ -4,7 +4,6 @@ import "@crm/../tests/mock_server"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { click, - clickEdit, clickSave, dragAndDrop, getFixture, @@ -125,6 +124,7 @@ QUnit.module('Crm Rainbowman Triggers', { ...this.testFormView, resId: 6, mockRPC: getMockRpc(assert), + mode: "readonly", }); await click(target.querySelector(".o_statusbar_status button[data-value='3']")); @@ -139,7 +139,6 @@ QUnit.module('Crm Rainbowman Triggers', { resId: 6, mockRPC: getMockRpc(assert), }); - await clickEdit(target); await click(target.querySelector(".o_statusbar_status button[data-value='3']")); assert.verifySteps([]); // no message displayed yet @@ -155,6 +154,7 @@ QUnit.module('Crm Rainbowman Triggers', { ...this.testFormView, resId: 2, mockRPC: getMockRpc(assert), + mode: "readonly", }); await click(target.querySelector(".o_statusbar_status button[data-value='3']")); @@ -168,6 +168,7 @@ QUnit.module('Crm Rainbowman Triggers', { ...this.testFormView, resId: 1, mockRPC: getMockRpc(assert), + mode: "readonly", }); await click(target.querySelector(".o_statusbar_status button[data-value='3']")); @@ -181,6 +182,7 @@ QUnit.module('Crm Rainbowman Triggers', { ...this.testFormView, resId: 8, mockRPC: getMockRpc(assert), + mode: "readonly", }); await click(target.querySelector(".o_statusbar_status button[data-value='3']")); @@ -194,6 +196,7 @@ QUnit.module('Crm Rainbowman Triggers', { ...this.testFormView, resId: 10, mockRPC: getMockRpc(assert), + mode: "readonly", }); await click(target.querySelector(".o_statusbar_status button[data-value='3']")); @@ -207,6 +210,7 @@ QUnit.module('Crm Rainbowman Triggers', { ...this.testFormView, resId: 1, mockRPC: getMockRpc(assert), + mode: "readonly", }); await click(target.querySelector(".o_statusbar_status button[data-value='2']")); diff --git a/addons/crm/static/tests/tours/crm_email_and_phone_propagation.js b/addons/crm/static/tests/tours/crm_email_and_phone_propagation.js index 3ccb30554e7..0e132f105c4 100644 --- a/addons/crm/static/tests/tours/crm_email_and_phone_propagation.js +++ b/addons/crm/static/tests/tours/crm_email_and_phone_propagation.js @@ -15,18 +15,11 @@ odoo.define('crm.crm_email_and_phone_propagation', function (require) { trigger: '.o_kanban_record .o_kanban_record_title span:contains(Test Lead Propagation)', content: 'Open the first lead', run: 'click', - }, { - trigger: '.o_form_button_edit', - extra_trigger: '.o_lead_opportunity_form.o_form_readonly', - content: 'Edit the lead', - run: 'click', }, { trigger: '.o_form_button_save', extra_trigger: '.o_form_editable .o_field_widget[name=email_from] input', content: 'Save the lead', run: 'click', - }, { - trigger: '.o_form_readonly', }, ]); @@ -42,10 +35,6 @@ odoo.define('crm.crm_email_and_phone_propagation', function (require) { trigger: '.o_kanban_record .o_kanban_record_title span:contains(Test Lead Propagation)', content: 'Open the first lead', run: 'click', - }, { - trigger: '.o_form_button_edit', - content: 'Edit the lead', - run: 'click', }, { trigger: '.o_form_editable .o_field_widget[name=email_from] input', extra_trigger: '.o_form_editable .o_field_widget[name=phone] input', @@ -55,14 +44,13 @@ odoo.define('crm.crm_email_and_phone_propagation', function (require) { action.remove_text("", ".o_form_editable .o_field_widget[name=phone] input"); }, }, { - trigger: '.o_form_button_save', + trigger: '.o_back_button', // wait the the warning message to be visible extra_trigger: '.o_form_sheet_bg .fa-exclamation-triangle:not(.o_invisible_modifier)', - content: 'Save the lead', + content: 'Save the lead and exit to kanban', run: 'click', - }, { - trigger: '.o_form_readonly', - }, + },{ + trigger: '.o_kanban_renderer', + } ]); - }); diff --git a/addons/crm/static/tests/tours/crm_forecast_tour.js b/addons/crm/static/tests/tours/crm_forecast_tour.js index 742b2a54204..e5d99938e1f 100644 --- a/addons/crm/static/tests/tours/crm_forecast_tour.js +++ b/addons/crm/static/tests/tours/crm_forecast_tour.js @@ -64,10 +64,6 @@ tour.register('crm_forecast', { trigger: ".o_kanban_record .o_kanban_record_title:contains('Test Opportunity 1')", content: "edit lead", run: "click" - }, { - trigger: ".o_form_button_edit", - content: "edit datetime", - run: "click" }, { trigger: ".o_field_widget[name=date_deadline] input", content: "complete expected closing", diff --git a/addons/crm/static/tests/tours/crm_rainbowman.js b/addons/crm/static/tests/tours/crm_rainbowman.js index f56e3404220..3189d0d810c 100644 --- a/addons/crm/static/tests/tours/crm_rainbowman.js +++ b/addons/crm/static/tests/tours/crm_rainbowman.js @@ -60,14 +60,18 @@ }, { trigger: ".o_statusbar_status button[data-value='4']", content: "move lead to won stage", - }, { + }, + ...tour.stepUtils.saveForm(), + { trigger: ".o_statusbar_status button[data-value='1']", extra_trigger: ".o_reward_rainbow", content: "move lead to previous stage & rainbowman appears", }, { trigger: "button[name=action_set_won_rainbowman]", content: "click button mark won", - }, { + }, + ...tour.stepUtils.saveForm(), + { trigger: ".o_menu_brand", extra_trigger: ".o_reward_rainbow", content: "last rainbowman appears", diff --git a/addons/hr/static/tests/m2x_avatar_employee_tests.js b/addons/hr/static/tests/m2x_avatar_employee_tests.js index 03c7c049346..17d576427b6 100644 --- a/addons/hr/static/tests/m2x_avatar_employee_tests.js +++ b/addons/hr/static/tests/m2x_avatar_employee_tests.js @@ -183,7 +183,7 @@ QUnit.module('hr', {}, function () { res_id: m2xHrAvatarUserId1, views: [[false, 'form']], }); - assert.strictEqual(document.querySelector('.o_field_widget[name=employee_id]').innerText.trim(), 'Mario'); + assert.strictEqual(document.querySelector('.o_field_widget[name=employee_id] input').value.trim(), 'Mario'); await dom.click(document.querySelector('.o_m2o_avatar > img')); diff --git a/addons/hr_holidays/static/tests/qunit_suite_tests/radio_image_field_tests.js b/addons/hr_holidays/static/tests/qunit_suite_tests/radio_image_field_tests.js index 5669d0edc3a..a9be64ae707 100644 --- a/addons/hr_holidays/static/tests/qunit_suite_tests/radio_image_field_tests.js +++ b/addons/hr_holidays/static/tests/qunit_suite_tests/radio_image_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, clickEdit, clickSave, getFixture } from "@web/../tests/helpers/utils"; +import { click, clickSave, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -42,11 +42,6 @@ QUnit.module("Fields", (hooks) => { serverData, arch: '
', }); - assert.containsOnce(target, ".o_field_widget.o_field_hr_holidays_radio_image"); - assert.containsNone(target, ".o_radio_input"); - assert.containsNone(target, "img"); - - await clickEdit(target); assert.containsOnce(target, ".o_field_widget.o_field_hr_holidays_radio_image"); assert.containsN(target, ".o_radio_input", 3); @@ -58,7 +53,7 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); assert.containsOnce(target, ".o_field_widget.o_field_hr_holidays_radio_image"); - assert.containsNone(target, ".o_radio_input"); - assert.containsOnce(target, "img", 1); + assert.containsN(target, ".o_radio_input", 3); + assert.containsN(target, "img", 3); }); }); diff --git a/addons/hr_skills/static/tests/tours/skills_tour.js b/addons/hr_skills/static/tests/tours/skills_tour.js index 85e26f1cdfd..b630ad37d4c 100644 --- a/addons/hr_skills/static/tests/tours/skills_tour.js +++ b/addons/hr_skills/static/tests/tours/skills_tour.js @@ -24,10 +24,6 @@ tour.register('hr_skills_tour', { content: "Save", trigger: ".o_form_button_save", }, - { - content: "Switch to edit", - trigger: ".o_form_button_edit", - }, { content: "Add a new Resume experience", trigger: ".o_field_resume_one2many tr.o_resume_group_header button.btn-secondary", @@ -50,6 +46,7 @@ tour.register('hr_skills_tour', { { content: "Save it", trigger: ".o_form_button_save", + in_modal: true, run: "click", }, { diff --git a/addons/loyalty/static/tests/views/filterable_selection_field_tests.js b/addons/loyalty/static/tests/views/filterable_selection_field_tests.js index 11a90ed71f5..300b035819d 100644 --- a/addons/loyalty/static/tests/views/filterable_selection_field_tests.js +++ b/addons/loyalty/static/tests/views/filterable_selection_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, editSelect, getFixture } from "@web/../tests/helpers/utils"; +import { editSelect, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -47,7 +47,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); assert.containsN(target, "select option", 3); assert.containsOnce( @@ -71,7 +70,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); assert.containsN(target, "select option", 3); assert.containsOnce( @@ -96,7 +94,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); assert.containsN(target, "select option", 4); assert.containsOnce( diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py index 2bbb0b657a5..ce912d8d49d 100644 --- a/addons/mail/__manifest__.py +++ b/addons/mail/__manifest__.py @@ -193,6 +193,7 @@ For more specific needs, you may also assign custom-defined actions 'mail/static/src/components/*/*.js', 'mail/static/src/components/*/*.scss', 'mail/static/src/components/*/*.xml', + 'mail/static/src/views/*/*.xml', ('include', 'mail.assets_messaging'), 'mail/static/src/services/*.js', 'mail/static/src/views/**/*.js', diff --git a/addons/mail/static/src/views/form/form_compiler.js b/addons/mail/static/src/views/form/form_compiler.js index 74feeed4915..d6503c8741d 100644 --- a/addons/mail/static/src/views/form/form_compiler.js +++ b/addons/mail/static/src/views/form/form_compiler.js @@ -5,9 +5,9 @@ import { registry } from "@web/core/registry"; import { SIZES } from "@web/core/ui/ui_service"; import { patch } from "@web/core/utils/patch"; import { append, createElement, setAttributes } from "@web/core/utils/xml"; +import { ViewCompiler, getModifier } from "@web/views/view_compiler"; import { FormCompiler } from "@web/views/form/form_compiler"; - function compileChatter(node, params) { let hasActivities = false; let hasFollowers = false; @@ -44,9 +44,9 @@ function compileChatter(node, params) { "hasParentReloadOnFollowersUpdate": hasParentReloadOnFollowersUpdate, "hasParentReloadOnMessagePosted": hasParentReloadOnMessagePosted, "isAttachmentBoxVisibleInitially": isAttachmentBoxVisibleInitially, - "threadId": "props.record.resId or undefined", - "threadModel": "props.record.resModel", - "webRecord": "props.record", + "threadId": params.threadId, + "threadModel": params.threadModel, + "webRecord": params.webRecord, }); const chatterContainerHookXml = createElement("div"); chatterContainerHookXml.classList.add("o_FormRenderer_chatterContainer"); @@ -59,27 +59,94 @@ function compileAttachmentPreview(node, params) { webClientViewAttachmentViewContainerHookXml.classList.add('o_attachment_preview'); const webClientViewAttachmentViewContainerXml = createElement("WebClientViewAttachmentViewContainer"); setAttributes(webClientViewAttachmentViewContainerXml, { - "threadId": "props.record.resId or undefined", - "threadModel": "props.record.resModel", + "threadId": params.threadId, + "threadModel": params.threadModel, }); append(webClientViewAttachmentViewContainerHookXml, webClientViewAttachmentViewContainerXml); return webClientViewAttachmentViewContainerHookXml; } +export class MailFormCompiler extends ViewCompiler { + setup() { + this.compilers.push({ selector: "t", fn: this.compileT }); + this.compilers.push({ selector: "div.oe_chatter", fn: this.compileChatter }); + this.compilers.push({ + selector: "div.o_attachment_preview", + fn: this.compileAttachmentPreview, + }); + } + + compile(node, params) { + const res = super.compile(node, params).children[0]; + const chatterContainerHookXml = res.querySelector(".o_FormRenderer_chatterContainer"); + if (chatterContainerHookXml) { + setAttributes(chatterContainerHookXml, { + "t-if": `!hasAttachmentViewer() and uiService.size >= ${SIZES.XXL}`, + "t-attf-class": `{{ uiService.size >= ${SIZES.XXL} ? "o-aside" : "" }}`, + }); + } + const attachmentViewHookXml = res.querySelector(".o_attachment_preview"); + if (attachmentViewHookXml) { + setAttributes(attachmentViewHookXml, { + "t-if": `hasAttachmentViewer() and uiService.size >= ${SIZES.XXL}`, + }); + } + return res; + } + + compileT(node, params) { + const compiledRoot = createElement("t"); + for (const child of node.childNodes) { + const invisible = getModifier(child, "invisible"); + let compiledChild = this.compileNode(child, params, false); + compiledChild = this.applyInvisible(invisible, compiledChild, { + ...params, + recordExpr: "model.root", + }); + append(compiledRoot, compiledChild); + } + return compiledRoot; + } + + compileChatter(node) { + return compileChatter(node, { + threadId: "model.root.resId or undefined", + threadModel: "model.root.resModel", + webRecord: "model.root", + }); + } + + compileAttachmentPreview(node) { + return compileAttachmentPreview(node, { + threadId: "model.root.resId or undefined", + threadModel: "model.root.resModel", + }); + } +} + registry.category("form_compilers").add("chatter_compiler", { selector: "div.oe_chatter", - fn: compileChatter, + fn: (node) => + compileChatter(node, { + threadId: "props.record.resId or undefined", + threadModel: "props.record.resModel", + webRecord: "props.record", + }), }); registry.category("form_compilers").add("attachment_preview_compiler", { selector: "div.o_attachment_preview", - fn: compileAttachmentPreview, + fn: (node) => + compileAttachmentPreview(node, { + threadId: "props.record.resId or undefined", + threadModel: "props.record.resModel", + }), }); patch(FormCompiler.prototype, 'mail', { - compile() { + compile(node, params) { // TODO no chatter if in dialog? - const res = this._super(...arguments); + const res = this._super(node, params); const chatterContainerHookXml = res.querySelector('.o_FormRenderer_chatterContainer'); if (!chatterContainerHookXml) { return res; // no chatter, keep the result as it is @@ -97,17 +164,12 @@ patch(FormCompiler.prototype, 'mail', { if (!parentXml) { return res; // miss-config: a sheet-bg is required for the rest } - const webClientViewAttachmentViewHookXml = res.querySelector('.o_attachment_preview'); - // TODO hasAttachmentViewer should also depend on the groups= and/or invisible modifier on o_attachment_preview (see invoice form) - if (webClientViewAttachmentViewHookXml) { + if (params.hasAttachmentViewer) { // in sheet bg (attachment viewer present) - setAttributes(webClientViewAttachmentViewHookXml, { - 't-if': `hasAttachmentViewer() and uiService.size >= ${SIZES.XXL}`, - }); - const sheetBgChatterContainerHookXml = chatterContainerHookXml.cloneNode(true); + const sheetBgChatterContainerHookXml = chatterContainerHookXml; sheetBgChatterContainerHookXml.classList.add('o-isInFormSheetBg'); setAttributes(sheetBgChatterContainerHookXml, { - 't-if': `hasAttachmentViewer() and uiService.size >= ${SIZES.XXL}`, + 't-if': `uiService.size < ${SIZES.XXL} or hasAttachmentViewer()`, }); append(formSheetBgXml, sheetBgChatterContainerHookXml); const sheetBgChatterContainerXml = sheetBgChatterContainerHookXml.querySelector('ChatterContainer'); @@ -116,29 +178,19 @@ patch(FormCompiler.prototype, 'mail', { "hasExternalBorder": "true", "hasMessageListScrollAdjust": "false", }); - } - // after sheet bg (standard position, either aside or below) - if (webClientViewAttachmentViewHookXml) { - setAttributes(chatterContainerHookXml, { - 't-if': `!(hasAttachmentViewer() and uiService.size >= ${SIZES.XXL})`, - 't-attf-class': `{{ uiService.size >= ${SIZES.XXL} and !(hasAttachmentViewer() and uiService.size >= ${SIZES.XXL}) ? "o-aside" : "" }}`, - }); - setAttributes(chatterContainerXml, { - "isInFormSheetBg": "hasAttachmentViewer()", - "hasExternalBorder": `!(uiService.size >= ${SIZES.XXL} and !(hasAttachmentViewer() and uiService.size >= ${SIZES.XXL}))`, - "hasMessageListScrollAdjust": `uiService.size >= ${SIZES.XXL} and !(hasAttachmentViewer() and uiService.size >= ${SIZES.XXL})`, - }); } else { + // after sheet bg (standard position, either aside or below) setAttributes(chatterContainerXml, { "isInFormSheetBg": "false", "hasExternalBorder": `uiService.size < ${SIZES.XXL}`, "hasMessageListScrollAdjust": `uiService.size >= ${SIZES.XXL}`, }); setAttributes(chatterContainerHookXml, { + 't-if': `uiService.size < ${SIZES.XXL}`, 't-attf-class': `{{ uiService.size >= ${SIZES.XXL} ? "o-aside" : "" }}`, }); + append(parentXml, chatterContainerHookXml); } - append(parentXml, chatterContainerHookXml); return res; }, }); diff --git a/addons/mail/static/src/views/form/form_controller.js b/addons/mail/static/src/views/form/form_controller.js new file mode 100644 index 00000000000..0920be0f0af --- /dev/null +++ b/addons/mail/static/src/views/form/form_controller.js @@ -0,0 +1,79 @@ +/** @odoo-module */ + +import { useModels } from "@mail/component_hooks/use_models"; +import { ChatterContainer } from "@mail/components/chatter_container/chatter_container"; +import { WebClientViewAttachmentViewContainer } from "@mail/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container"; + +import { browser } from "@web/core/browser/browser"; +import { useService } from "@web/core/utils/hooks"; +import { patch } from "@web/core/utils/patch"; +import { useDebounced } from "@web/core/utils/timing"; +import { FormController } from "@web/views/form/form_controller"; +import { useViewCompiler } from "@web/views/view_compiler"; +import { evalDomain } from "@web/views/utils"; + +import { MailFormCompiler } from "./form_compiler"; + +const { onMounted, onWillUnmount } = owl; + +patch(FormController.prototype, "mail", { + setup() { + this._super(); + this.uiService = useService("ui"); + this.hasAttachmentViewerInArch = false; + + if (this.env.services.messaging) { + useModels(); + } + + const { archInfo } = this.props; + const { arch, xmlDoc } = archInfo; + + const template = document.createElement("t"); + const xmlDocAttachmentPreview = xmlDoc.querySelector("div.o_attachment_preview"); + if (xmlDocAttachmentPreview && xmlDocAttachmentPreview.parentNode.nodeName === "form") { + // TODO hasAttachmentViewer should also depend on the groups= and/or invisible modifier on o_attachment_preview (see invoice form) + template.appendChild(xmlDocAttachmentPreview); + this.hasAttachmentViewerInArch = true; + } + + const xmlDocChatter = xmlDoc.querySelector("div.oe_chatter"); + if (xmlDocChatter && xmlDocChatter.parentNode.nodeName === "form") { + template.appendChild(xmlDocChatter.cloneNode(true)); + } + + const mailTemplates = useViewCompiler(MailFormCompiler, arch, { Mail: template }, {}); + this.mailTemplate = mailTemplates.Mail; + + this.onResize = useDebounced(this.render, 200); + onMounted(() => browser.addEventListener("resize", this.onResize)); + onWillUnmount(() => browser.removeEventListener("resize", this.onResize)); + }, + /** + * @returns {Messaging|undefined} + */ + getMessaging() { + return this.env.services.messaging && this.env.services.messaging.modelManager.messaging; + }, + /** + * @returns {boolean} + */ + hasAttachmentViewer() { + if (!this.getMessaging() || !this.model.root.resId) { + return false; + } + const thread = this.getMessaging().models['Thread'].insert({ + id: this.model.root.resId, + model: this.model.root.resModel, + }); + return thread.attachmentsInWebClientView.length > 0; + }, + evalDomainFromRecord(record, expr) { + return evalDomain(expr, record.evalContext); + }, +}); + +Object.assign(FormController.components, { + ChatterContainer, + WebClientViewAttachmentViewContainer, +}); diff --git a/addons/mail/static/src/views/form/form_controller.xml b/addons/mail/static/src/views/form/form_controller.xml new file mode 100644 index 00000000000..b15f4d7e4fd --- /dev/null +++ b/addons/mail/static/src/views/form/form_controller.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + hasAttachmentViewerInArch + + + + diff --git a/addons/mail/static/src/views/form/form_renderer.js b/addons/mail/static/src/views/form/form_renderer.js index 40237e82aa6..82d84e74721 100644 --- a/addons/mail/static/src/views/form/form_renderer.js +++ b/addons/mail/static/src/views/form/form_renderer.js @@ -15,6 +15,13 @@ patch(FormRenderer.prototype, 'mail', { } }, + get compileParams() { + return { + ...this._super(), + hasAttachmentViewer: this.props.hasAttachmentViewer, + }; + }, + //-------------------------------------------------------------------------- // Mail Methods //-------------------------------------------------------------------------- diff --git a/addons/mail/static/src/widgets/form_renderer/form_renderer.scss b/addons/mail/static/src/widgets/form_renderer/form_renderer.scss index 7ff19adfab6..aef5bcd2c61 100644 --- a/addons/mail/static/src/widgets/form_renderer/form_renderer.scss +++ b/addons/mail/static/src/widgets/form_renderer/form_renderer.scss @@ -34,7 +34,6 @@ .o_FormRenderer_chatterContainer { display: flex; - flex: 1 1 auto; margin: 0 auto; max-width: $o-form-view-sheet-max-width; padding: map-get($spacers, 3) map-get($spacers, 3) map-get($spacers, 5); diff --git a/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js b/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js index d7fdebec780..9cd0bff5026 100644 --- a/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js @@ -4,7 +4,7 @@ import { start, startServer } from '@mail/../tests/helpers/test_utils'; import { ROUTES_TO_IGNORE } from '@mail/../tests/helpers/webclient_setup'; import testUtils from 'web.test_utils'; -import { clickEdit, patchWithCleanup, selectDropdownItem } from '@web/../tests/helpers/utils'; +import { patchWithCleanup, selectDropdownItem } from '@web/../tests/helpers/utils'; import { ListController } from "@web/views/list/list_controller"; QUnit.module('mail', {}, function () { @@ -489,8 +489,6 @@ QUnit.test('many2many_tags_email widget can load more than 40 records', async fu assert.strictEqual(document.querySelectorAll('.o_field_widget[name="partner_ids"] .badge').length, 100); - await clickEdit(document.body); - assert.containsOnce(document.body, '.o_form_editable'); // add a record to the relation diff --git a/addons/mail/static/tests/qunit_suite_tests/components/field_text_emojis_tests.js b/addons/mail/static/tests/qunit_suite_tests/components/field_text_emojis_tests.js index b48a21bf78f..ed9f43f7a63 100644 --- a/addons/mail/static/tests/qunit_suite_tests/components/field_text_emojis_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/components/field_text_emojis_tests.js @@ -1,6 +1,6 @@ /**@odoo-module **/ -import { clickEdit, clickSave, getFixture } from "@web/../tests/helpers/utils"; +import { getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; QUnit.module("Field text emojis", (hooks) => { @@ -34,13 +34,7 @@ QUnit.module("Field text emojis", (hooks) => { assert.containsOnce(target, ".o_field_text_emojis"); assert.containsOnce(target, ".o_field_text_emojis button"); - assert.isNotVisible(target.querySelector(".o_field_text_emojis button")); - - await clickEdit(target); + assert.isVisible(target.querySelector(".o_field_text_emojis button")); assert.isVisible(target, ".o_field_text_emojis button .fa-smile"); - - await clickSave(target); - assert.containsOnce(target, ".o_field_text_emojis button"); - assert.isNotVisible(target.querySelector(".o_field_text_emojis button")); }); }); diff --git a/addons/mail/static/tests/qunit_suite_tests/m2x_avatar_user_tests.js b/addons/mail/static/tests/qunit_suite_tests/m2x_avatar_user_tests.js index 5826d05e1cd..a72ae912db8 100644 --- a/addons/mail/static/tests/qunit_suite_tests/m2x_avatar_user_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/m2x_avatar_user_tests.js @@ -143,7 +143,7 @@ QUnit.module('mail', {}, function () { 'view_mode': 'form', 'views': [[false, 'form']], }); - assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user > div > a").textContent, "Mario") + assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user input").value, "Mario") triggerHotkey("control+k") await nextTick(); @@ -161,7 +161,7 @@ QUnit.module('mail', {}, function () { ]) await click(target, "#o_command_3") await nextTick(); - assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user > div > a").textContent, "Luigi") + assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user input").value, "Luigi") }); QUnit.test('many2one_avatar_user widget edited by the smart action "Assign to me"', async function (assert) { @@ -184,7 +184,7 @@ QUnit.module('mail', {}, function () { 'view_mode': 'form', 'views': [[false, 'form']], }); - assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user > div > a").textContent, "Mario") + assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user input").value, "Mario") triggerHotkey("control+k") await nextTick(); const idx = [...target.querySelectorAll(".o_command")].map(el => el.textContent).indexOf("Assign/Unassign to meALT + SHIFT + I") @@ -193,14 +193,14 @@ QUnit.module('mail', {}, function () { // Assign me (Luigi) triggerHotkey("alt+shift+i") await nextTick(); - assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user > div > a").textContent, "Luigi") + assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user input").value, "Luigi") // Unassign me triggerHotkey("control+k"); await nextTick(); await click([...target.querySelectorAll(".o_command")][idx]) await nextTick(); - assert.containsNone(target, ".o_field_many2one_avatar_user > div > a"); + assert.strictEqual(target.querySelector(".o_field_many2one_avatar_user input").value, "") }); QUnit.test('many2many_avatar_user widget edited by the smart action "Assign to..."', async function (assert) { diff --git a/addons/mail/static/tests/qunit_suite_tests/widgets/form_renderer_tests.js b/addons/mail/static/tests/qunit_suite_tests/widgets/form_renderer_tests.js index 4263e194f71..4d63990a93c 100644 --- a/addons/mail/static/tests/qunit_suite_tests/widgets/form_renderer_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/widgets/form_renderer_tests.js @@ -560,7 +560,7 @@ QUnit.test('chatter should become enabled when creation done', async function (a }); QUnit.test('read more/less links are not duplicated when switching from read to edit mode', async function (assert) { - assert.expect(5); + assert.expect(3); const pyEnv = await startServer(); const resPartnerId1 = pyEnv['res.partner'].create({}); @@ -622,33 +622,10 @@ QUnit.test('read more/less links are not duplicated when switching from read to '.o_Message_readMoreLess', "there should be only one read more" ); - await afterNextRender(() => afterEvent({ - eventName: 'o-component-message-read-more-less-inserted', - func: () => document.querySelector('.o_form_button_edit').click(), - message: "should wait until read more/less is inserted after clicking on edit", - predicate: ({ message }) => message.id === mailMessageId1, - })); - assert.containsOnce( - document.body, - '.o_Message_readMoreLess', - "there should still be only one read more after switching to edit mode" - ); - - await afterNextRender(() => afterEvent({ - eventName: 'o-component-message-read-more-less-inserted', - func: () => document.querySelector('.o_form_button_cancel').click(), - message: "should wait until read more/less is inserted after canceling edit", - predicate: ({ message }) => message.id === mailMessageId1, - })); - assert.containsOnce( - document.body, - '.o_Message_readMoreLess', - "there should still be only one read more after switching back to read mode" - ); }); QUnit.test('read more links becomes read less after being clicked', async function (assert) { - assert.expect(6); + assert.expect(5); const pyEnv = await startServer(); const resPartnerId1 = pyEnv['res.partner'].create({}); @@ -716,18 +693,6 @@ QUnit.test('read more links becomes read less after being clicked', async functi "Read More/Less link should contain 'Read More' as text" ); - await afterNextRender(() => afterEvent({ - eventName: 'o-component-message-read-more-less-inserted', - func: () => document.querySelector('.o_form_button_edit').click(), - message: "should wait until read more/less is inserted after clicking on edit", - predicate: ({ message }) => message.id === mailMessageId1, - })); - assert.strictEqual( - document.querySelector('.o_Message_readMoreLess').textContent, - 'Read More', - "Read More/Less link should contain 'Read More' as text" - ); - document.querySelector('.o_Message_readMoreLess').click(); assert.strictEqual( document.querySelector('.o_Message_readMoreLess').textContent, diff --git a/addons/mass_mailing/static/src/js/tours/mass_mailing_editor_tour.js b/addons/mass_mailing/static/src/js/tours/mass_mailing_editor_tour.js index ef194c00ad0..95ce87fbec8 100644 --- a/addons/mass_mailing/static/src/js/tours/mass_mailing_editor_tour.js +++ b/addons/mass_mailing/static/src/js/tours/mass_mailing_editor_tour.js @@ -47,9 +47,11 @@ odoo.define('mass_mailing.mass_mailing_editor_tour', function (require) { trigger: 'input[name="subject"]', run: 'text Test', }, { - trigger: 'button.o_form_button_save', - }, { - trigger: 'iframe.o_readonly', + trigger: '.o_form_view', // blur previous input + }, + ...tour.stepUtils.saveForm(), + { + trigger: 'iframe .o_editable', run: () => {}, }]); }); diff --git a/addons/mass_mailing/static/tests/mass_mailing_html_tests.js b/addons/mass_mailing/static/tests/mass_mailing_html_tests.js index 6a7c37b1d02..920beb418eb 100644 --- a/addons/mass_mailing/static/tests/mass_mailing_html_tests.js +++ b/addons/mass_mailing/static/tests/mass_mailing_html_tests.js @@ -69,7 +69,7 @@ QUnit.module('field html', (hooks) => { }); QUnit.test('save arch and html', async function (assert) { - assert.expect(4); + assert.expect(2); await makeView({ type: "form", @@ -95,15 +95,6 @@ QUnit.module('field html', (hooks) => { let fieldReadonly = fixture.querySelector('.o_field_widget[name="body_html"]'); let fieldEdit = fixture.querySelector('.o_field_widget[name="body_arch"]'); - assert.strictEqual($(fieldReadonly).css('display'), 'block', "should display the readonly mode"); - assert.strictEqual($(fieldEdit).css('display'), 'none', "should hide the edit mode"); - - await testUtils.clickEdit(fixture); - await testUtils.nextTick(); - - fieldReadonly = fixture.querySelector('.o_field_widget[name="body_html"]'); - fieldEdit = fixture.querySelector('.o_field_widget[name="body_arch"]'); - assert.strictEqual($(fieldReadonly).css('display'), 'none', "should hide the readonly mode"); assert.strictEqual($(fieldEdit).css('display'), 'block', "should display the edit mode"); }); diff --git a/addons/project/static/src/js/tours/project.js b/addons/project/static/src/js/tours/project.js index ab7289dd9a4..9f836dcc083 100644 --- a/addons/project/static/src/js/tours/project.js +++ b/addons/project/static/src/js/tours/project.js @@ -108,7 +108,7 @@ tour.register('project_tour', { run: "click", }, { trigger: ".breadcrumb-item:not(.active):last", - extra_trigger: '.o_form_project_tasks.o_form_readonly', + extra_trigger: '.o_form_project_tasks.o_form_saved', content: Markup(_t("Let's go back to the kanban view to have an overview of your next tasks.")), position: "right", run: 'click', diff --git a/addons/purchase_product_matrix/static/tests/tours/purchase_product_matrix_tour.js b/addons/purchase_product_matrix/static/tests/tours/purchase_product_matrix_tour.js index 9366face458..43dbd58d957 100644 --- a/addons/purchase_product_matrix/static/tests/tours/purchase_product_matrix_tour.js +++ b/addons/purchase_product_matrix/static/tests/tours/purchase_product_matrix_tour.js @@ -101,7 +101,7 @@ tour.register('purchase_matrix_tour', { }, { trigger: 'span:contains("Confirm")', run: 'click' // apply the matrix -}, ...tour.stepUtils.saveForm('.o_field_cell.o_data_cell.o_list_number:contains("8.20")') +}, ...tour.stepUtils.saveForm({ extra_trigger: '.o_field_cell.o_data_cell.o_list_number:contains("8.20")' }) ]); diff --git a/addons/resource/static/tests/components/section_one2many_field_tests.js b/addons/resource/static/tests/components/section_one2many_field_tests.js index 94167d4f121..56cb49e773b 100644 --- a/addons/resource/static/tests/components/section_one2many_field_tests.js +++ b/addons/resource/static/tests/components/section_one2many_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { clickEdit, getFixture } from "@web/../tests/helpers/utils"; +import { getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; QUnit.module("SectionOneToManyField", (hooks) => { @@ -73,13 +73,8 @@ QUnit.module("SectionOneToManyField", (hooks) => { assert.doesNotHaveClass(rows[1], "o_is_line_section fw-bold"); assert.strictEqual(rows[0].textContent, "firstSectionTitle"); assert.strictEqual(rows[1].textContent, "recordTitle5"); - assert.strictEqual(rows[0].querySelector("td[name=title]").getAttribute("colspan"), "2"); + assert.strictEqual(rows[0].querySelector("td[name=title]").getAttribute("colspan"), "3"); assert.strictEqual(rows[1].querySelector("td[name=title]").getAttribute("colspan"), null); - - assert.containsNone(target, ".o_list_record_remove"); - - await clickEdit(target); - assert.containsOnce(target, ".o_list_record_remove"); assert.containsNone(target, ".o_is_line_section .o_list_record_remove"); }); diff --git a/addons/sale_expense/static/tests/sale_order_many2one_tests.js b/addons/sale_expense/static/tests/sale_order_many2one_tests.js index 2e33a52b6ef..740fb2ae062 100644 --- a/addons/sale_expense/static/tests/sale_order_many2one_tests.js +++ b/addons/sale_expense/static/tests/sale_order_many2one_tests.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; -import { clickDropdown, clickEdit, getFixture } from "@web/../tests/helpers/utils"; +import { clickDropdown, getFixture } from "@web/../tests/helpers/utils"; QUnit.module('sale_expense', { @@ -67,7 +67,6 @@ QUnit.module('sale_expense', { '
' }); - await clickEdit(this.target); await clickDropdown(this.target, "sale_order_id"); assert.containsN(this.target, 'li.o-autocomplete--dropdown-item', 9); diff --git a/addons/survey/static/tests/components/description_page_field_tests.js b/addons/survey/static/tests/components/description_page_field_tests.js index 9e8687be88d..dbc5cb6e4cb 100644 --- a/addons/survey/static/tests/components/description_page_field_tests.js +++ b/addons/survey/static/tests/components/description_page_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { click, clickEdit, getFixture } from "@web/../tests/helpers/utils"; +import { click, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; QUnit.module("DescriptionPageField", (hooks) => { @@ -78,7 +78,6 @@ QUnit.module("DescriptionPageField", (hooks) => { assert.containsN(target, "td.o_survey_description_page_cell", 2); assert.containsNone(target, "button.o_icon_button"); - await clickEdit(target); await click(target.querySelector(".o_data_cell")); assert.containsOnce(target.querySelector(".o_data_row"), "button.o_icon_button"); assert.containsNone(target, ".modal .o_form_view"); diff --git a/addons/survey/static/tests/components/question_page_one2many_field_tests.js b/addons/survey/static/tests/components/question_page_one2many_field_tests.js index d7c70bc3447..a1abe443f02 100644 --- a/addons/survey/static/tests/components/question_page_one2many_field_tests.js +++ b/addons/survey/static/tests/components/question_page_one2many_field_tests.js @@ -1,13 +1,6 @@ /** @odoo-module */ -import { - click, - clickEdit, - editInput, - getFixture, - nextTick, - triggerHotkey, -} from "@web/../tests/helpers/utils"; +import { click, editInput, getFixture, nextTick, triggerHotkey } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; QUnit.module("QuestionPageOneToManyField", (hooks) => { @@ -108,7 +101,9 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => {
`, + mode: "readonly", }); + await click(target.querySelector(".o_data_cell")); assert.containsNone(target, ".o_selected_row"); assert.containsOnce(target, ".modal .o_form_view"); @@ -131,7 +126,6 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => {
`, }); - await clickEdit(target); await click(target.querySelector(".o_data_cell")); assert.hasClass(target.querySelector(".o_is_section"), "o_selected_row"); assert.containsNone(target, ".modal .o_form_view"); @@ -155,7 +149,6 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => { `, }); - await clickEdit(target); await click(target.querySelector(".o_data_row:nth-child(2) .o_data_cell")); assert.containsNone(target, ".o_selected_row"); assert.containsOnce(target, ".modal .o_form_view"); @@ -184,7 +177,6 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => { `, }); - await clickEdit(target); assert.containsNone(target, ".o_selected_row"); await click(target.querySelectorAll(".o_field_x2many_list_row_add a")[1]); @@ -215,7 +207,6 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => { `, }); - await clickEdit(target); await click(target.querySelector(".o_field_x2many_list_row_add a")); assert.containsNone(target, ".o_selected_row"); assert.containsOnce(target, ".modal .o_form_view"); @@ -241,7 +232,6 @@ QUnit.module("QuestionPageOneToManyField", (hooks) => { `, }); - await clickEdit(target); await click(target.querySelector(".o_data_row .o_data_cell")); assert.containsOnce(target, ".o_selected_row.o_is_section"); diff --git a/addons/test_sale_product_configurators/static/tests/tours/event_sale_with_product_configurator_ui.js b/addons/test_sale_product_configurators/static/tests/tours/event_sale_with_product_configurator_ui.js index 97ab0359514..90d257762ad 100644 --- a/addons/test_sale_product_configurators/static/tests/tours/event_sale_with_product_configurator_ui.js +++ b/addons/test_sale_product_configurators/static/tests/tours/event_sale_with_product_configurator_ui.js @@ -113,5 +113,5 @@ tour.register('event_sale_with_product_configurator_tour', { in_modal: false, }, { trigger: '.o_event_sale_js_event_configurator_ok', -}, ...tour.stepUtils.saveForm('.o_field_cell.o_data_cell.o_list_number:contains("60.00")'), +}, ...tour.stepUtils.saveForm({ extra_trigger: '.o_field_cell.o_data_cell.o_list_number:contains("60.00")' }), ]); diff --git a/addons/web/static/src/core/datepicker/datepicker.js b/addons/web/static/src/core/datepicker/datepicker.js index 81ba4fdbfa0..599558b3e74 100644 --- a/addons/web/static/src/core/datepicker/datepicker.js +++ b/addons/web/static/src/core/datepicker/datepicker.js @@ -171,7 +171,7 @@ export class DatePicker extends Component { */ updateInput({ useStatic } = {}) { const [formattedDate] = this.formatValue(this.date, this.getOptions(useStatic)); - if (formattedDate) { + if (formattedDate !== null) { this.inputRef.el.value = formattedDate; } } @@ -230,7 +230,7 @@ export class DatePicker extends Component { // Reset to default (= given) date. this.updateInput(); } - if (!areDateEquals(this.date, parsedDate)) { + if (parsedDate !== null && !areDateEquals(this.date, parsedDate)) { this.props.onDateTimeChanged(parsedDate); } } diff --git a/addons/web/static/src/search/action_menus/action_menus.js b/addons/web/static/src/search/action_menus/action_menus.js index fdecb049424..224abd8f176 100644 --- a/addons/web/static/src/search/action_menus/action_menus.js +++ b/addons/web/static/src/search/action_menus/action_menus.js @@ -111,7 +111,8 @@ export class ActionMenus extends Component { * @private * @param {Object} item */ - onItemSelected(item) { + async onItemSelected(item) { + await this.props.onBeforeAction(item); if (item.callback) { item.callback([item]); } else if (item.action) { @@ -142,8 +143,10 @@ ActionMenus.props = { }, }, onActionExecuted: { type: Function, optional: true }, + onBeforeAction: { type: Function, optional: true }, }; ActionMenus.defaultProps = { onActionExecuted: () => {}, + onBeforeAction: () => {}, }; ActionMenus.template = "web.ActionMenus"; diff --git a/addons/web/static/src/views/basic_relational_model.js b/addons/web/static/src/views/basic_relational_model.js index c7b0b01b4a4..719209b13f2 100644 --- a/addons/web/static/src/views/basic_relational_model.js +++ b/addons/web/static/src/views/basic_relational_model.js @@ -326,6 +326,10 @@ export class Record extends DataPoint { return !this.resId; } + get isValid() { + return !this._invalidFields.size; + } + get resId() { if (this.__bm_handle__) { const resId = this.model.__bm__.localData[this.__bm_handle__].res_id; @@ -349,6 +353,10 @@ export class Record extends DataPoint { return Promise.all([...proms, this._updatePromise]); } + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + async checkValidity(urgent) { if (!urgent) { await this.askChanges(); @@ -685,7 +693,7 @@ export class Record extends DataPoint { * reloading after changes are applied, typically used to defer the load. * @returns {Promise} */ - async save(options = { stayInEdition: false, noReload: false, savePoint: false }) { + async save(options = { stayInEdition: true, noReload: false, savePoint: false }) { const shouldSwitchToReadonly = !options.stayInEdition && this.isInEdition; let resolveSavePromise; this._savePromise = new Promise((r) => { @@ -810,9 +818,6 @@ export class Record extends DataPoint { this.model.__bm__.discardChanges(this.__bm_handle__); this._invalidFields = new Set(); this.__syncData(); - if (this.resId) { - this.switchMode("readonly"); - } this.model.notify(); } diff --git a/addons/web/static/src/views/fields/fields.scss b/addons/web/static/src/views/fields/fields.scss index d877330e9e6..579411fe1e0 100644 --- a/addons/web/static/src/views/fields/fields.scss +++ b/addons/web/static/src/views/fields/fields.scss @@ -1,3 +1,45 @@ .o_field_cursor_disabled { cursor: not-allowed; } + +.o_field_widget:not(.o_legacy_field_widget) { + .o_input { + border: $input-border-width solid transparent; + padding: $o-input-padding-y $o-input-padding-x; + + &:hover, &:focus { + border-color: var(--o-input-border-color); + } + } + + .o_field_many2one_selection, .o_field_many2many_selection, .o_datepicker { + .o_dropdown_button, .o_datepicker_button { + display: none; + } + + &:hover, &:focus-within { + .o_dropdown_button, .o_datepicker_button { + display: unset; + } + } + } + + .o_field_many2one_selection { + display: flex; + width: 100%; + + .o_external_button { + visibility: hidden; + } + + &:hover, &:focus-within { + .o_external_button { + visibility: visible; + } + } + } +} + +.o_kanban_quick_create .o_field_widget .o_input { + border-color: var(--o-input-border-color); +} diff --git a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.scss b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.scss index 8ba997bd7bf..4be3b641cf8 100644 --- a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.scss +++ b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.scss @@ -12,6 +12,10 @@ .o_tags_input { padding: 1px 0; + &:hover, &:focus-within { + border-color: var(--o-input-border-color); + } + .o_tag { padding-left: 0.6em; padding-right: 0.6em; diff --git a/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss b/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss index 490a1cd8307..21d169e6cfb 100644 --- a/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss +++ b/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss @@ -3,6 +3,10 @@ .o_tags_input { padding: 1px 0; + + &:hover, &:focus-within { + border-color: var(--o-input-border-color); + } } .o_field_many2many_selection { diff --git a/addons/web/static/src/views/fields/many2one/many2one_field.js b/addons/web/static/src/views/fields/many2one/many2one_field.js index eec6ccd950b..4a7a6e97d10 100644 --- a/addons/web/static/src/views/fields/many2one/many2one_field.js +++ b/addons/web/static/src/views/fields/many2one/many2one_field.js @@ -188,7 +188,11 @@ export class Many2OneField extends Component { } } onExternalBtnClick() { - this.openDialog(this.resId); + if (this.props.openTarget === "current") { + this.openAction(); + } else { + this.openDialog(this.resId); + } } async onBarcodeBtnClick() { const barcode = await BarcodeScanner.scanBarcode(); @@ -259,6 +263,7 @@ Many2OneField.props = { relation: { type: String, optional: true }, string: { type: String, optional: true }, canScanBarcode: { type: Boolean, optional: true }, + openTarget: { type: String, validate: (v) => ["current", "new"].includes(v), optional: true }, }; Many2OneField.defaultProps = { canOpen: true, @@ -270,6 +275,7 @@ Many2OneField.defaultProps = { searchLimit: 7, string: "", canScanBarcode: false, + openTarget: "current", }; Many2OneField.displayName = _lt("Many2one"); @@ -295,6 +301,7 @@ Many2OneField.extractProps = ({ attrs, field }) => { string: attrs.string || field.string, createNameField: attrs.options.create_name_field, canScanBarcode: canScanBarcode, + openTarget: attrs.open_target, }; }; diff --git a/addons/web/static/src/views/fields/many2one/many2one_field.xml b/addons/web/static/src/views/fields/many2one/many2one_field.xml index bb596bf9e01..beb27606cdb 100644 --- a/addons/web/static/src/views/fields/many2one/many2one_field.xml +++ b/addons/web/static/src/views/fields/many2one/many2one_field.xml @@ -45,7 +45,8 @@ + + + + + @@ -53,11 +53,8 @@ Discard -
- -
diff --git a/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.js b/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.js new file mode 100644 index 00000000000..c93447a2b30 --- /dev/null +++ b/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.js @@ -0,0 +1,66 @@ +/** @odoo-module **/ + +import { Dialog } from "@web/core/dialog/dialog"; +import { RPCError } from "@web/core/network/rpc_service"; +import { registry } from "@web/core/registry"; + +const { Component, onWillDestroy } = owl; + +export class FormErrorDialog extends Component { + setup() { + const { data, message } = this.props; + if (data && data.arguments && data.arguments.length > 0) { + this.message = data.arguments[0]; + } else { + this.message = message; + } + } + + async discard() { + await this.props.onDiscard(); + this.props.close(); + } +} +FormErrorDialog.template = "web.FormErrorDialog"; +FormErrorDialog.components = { Dialog }; + +function makeFormErrorHandler(onDiscard) { + return (env, error, originalError) => { + if ( + originalError && + originalError.legacy && + originalError.message && + originalError.message instanceof RPCError + ) { + const event = originalError.event; + originalError = originalError.message; + error.unhandledRejectionEvent.preventDefault(); + if (event.isDefaultPrevented()) { + // in theory, here, event was already handled + return true; + } + event.preventDefault(); + + env.services.dialog.add(FormErrorDialog, { + message: originalError.message, + data: originalError.data, + onDiscard, + }); + + return true; + } + return false; + }; +} + +let formId = 0; + +export function useFormErrorDialog(onDiscard) { + const errorHandlerKey = `form_error_handler_${++formId}`; + registry + .category("error_handlers") + .add(errorHandlerKey, makeFormErrorHandler(onDiscard), { sequence: 0 }); + onWillDestroy(() => { + registry.category("error_handlers").remove(errorHandlerKey); + }); +} diff --git a/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.scss b/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.scss new file mode 100644 index 00000000000..dac7ad8b96d --- /dev/null +++ b/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.scss @@ -0,0 +1,10 @@ +.o_form_error_dialog { + border: none; + padding: 2rem; + top: 50%; + transform: translateY(-50%); + + footer { + border: none; + } +} diff --git a/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.xml b/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.xml new file mode 100644 index 00000000000..d3cbbc549a5 --- /dev/null +++ b/addons/web/static/src/views/form/form_error_dialog/form_error_dialog.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/addons/web/static/src/views/form/form_label.js b/addons/web/static/src/views/form/form_label.js index 3c50689dabf..c4f386b2727 100644 --- a/addons/web/static/src/views/form/form_label.js +++ b/addons/web/static/src/views/form/form_label.js @@ -8,7 +8,7 @@ const { Component, xml } = owl; export class FormLabel extends Component { get className() { - const { invalid, empty } = fieldVisualFeedback( + const { invalid, empty, readonly } = fieldVisualFeedback( this.props.fieldInfo.FieldComponent, this.props.record, this.props.fieldName, @@ -21,6 +21,9 @@ export class FormLabel extends Component { if (empty) { classes.push("o_form_label_empty"); } + if (readonly) { + classes.push("o_form_label_readonly"); + } return classes.join(" "); } diff --git a/addons/web/static/src/views/form/form_renderer.js b/addons/web/static/src/views/form/form_renderer.js index 9034eac12dc..eccf98f060f 100644 --- a/addons/web/static/src/views/form/form_renderer.js +++ b/addons/web/static/src/views/form/form_renderer.js @@ -43,21 +43,21 @@ export class FormRenderer extends Component { if (!disableAutofocus) { const rootRef = useRef("compiled_view_root"); useEffect( - (isInEdition, rootEl) => { + (isVirtual, rootEl) => { if (!rootEl) { return; } let elementToFocus; - if (isInEdition) { + if (isVirtual) { elementToFocus = (autofocusFieldId && rootEl.querySelector(`#${autofocusFieldId}`)) || - rootEl.querySelector(`.o_content .o_field_widget input`); + rootEl.querySelector(`.o_content .o_field_widget input[type="text"]`); } if (elementToFocus) { elementToFocus.focus(); } }, - () => [this.props.record.isInEdition, rootRef.el] + () => [this.props.record.isVirtual, rootRef.el] ); } } @@ -65,6 +65,10 @@ export class FormRenderer extends Component { evalDomainFromRecord(record, expr) { return evalDomain(expr, record.evalContext); } + + get compileParams() { + return {}; + } } FormRenderer.template = xml``; diff --git a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js new file mode 100644 index 00000000000..a3263ccc40e --- /dev/null +++ b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js @@ -0,0 +1,35 @@ +/** @odoo-module **/ + +const { Component } = owl; + +export class FormStatusIndicator extends Component { + get displayButtons() { + return this.indicatorMode !== "saved"; + } + + get indicatorMode() { + if (this.props.model.root.isVirtual) { + return this.props.model.root.isValid ? "dirty" : "invalid"; + } else if (!this.props.model.root.isValid) { + return "invalid"; + } else if (this.props.model.root.isDirty) { + return "dirty"; + } else { + return "saved"; + } + } + + async discard() { + await this.props.discard(); + } + async save() { + await this.props.save(); + } +} +FormStatusIndicator.template = "web.FormStatusIndicator"; +FormStatusIndicator.props = { + model: Object, + save: Function, + discard: Function, + isDisabled: Boolean, +}; diff --git a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.scss b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.scss new file mode 100644 index 00000000000..c1ccb2e97ae --- /dev/null +++ b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.scss @@ -0,0 +1,15 @@ +.o_form_status_indicator:not(.o_form_status_indicator_new_record) { + .o_form_status_indicator_buttons { + visibility: hidden; + } + + &:hover { + span { + visibility: hidden; + } + + .o_form_status_indicator_buttons:not(.o_form_status_indicator_buttons_hidden) { + visibility: visible; + } + } +} diff --git a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml new file mode 100644 index 00000000000..ecfa52083b7 --- /dev/null +++ b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml @@ -0,0 +1,21 @@ + + + + +
+ + + Unable to save + + + Unsaved changes + + +
+ + +
+
+
+ +
diff --git a/addons/web/static/src/views/form/form_view.js b/addons/web/static/src/views/form/form_view.js index 8d24b6b19df..271add520b6 100644 --- a/addons/web/static/src/views/form/form_view.js +++ b/addons/web/static/src/views/form/form_view.js @@ -6,13 +6,14 @@ import { RelationalModel } from "../basic_relational_model"; import { FormArchParser } from "./form_arch_parser"; import { FormController } from "./form_controller"; import { FormCompiler } from "./form_compiler"; +import { FormControlPanel } from "./control_panel/form_control_panel"; export const formView = { type: "form", display_name: "Form", multiRecord: false, - display: { controlPanel: { ["top-right"]: false } }, searchMenuTypes: [], + ControlPanel: FormControlPanel, Controller: FormController, Renderer: FormRenderer, ArchParser: FormArchParser, diff --git a/addons/web/static/src/views/form/status_bar_buttons/status_bar_buttons.js b/addons/web/static/src/views/form/status_bar_buttons/status_bar_buttons.js index d525216b306..7ac55d22731 100644 --- a/addons/web/static/src/views/form/status_bar_buttons/status_bar_buttons.js +++ b/addons/web/static/src/views/form/status_bar_buttons/status_bar_buttons.js @@ -11,10 +11,7 @@ export class StatusBarButtons extends Component { return []; } return Object.entries(this.props.slots) - .filter( - (entry) => - entry[1].isVisible && (entry[1].displayInReadOnly ? this.props.readonly : true) - ) + .filter((entry) => entry[1].isVisible) .map((entry) => entry[0]); } } diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 8233fc80f6a..113c995dfff 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -572,13 +572,6 @@ export class ListRenderer extends Component { if (column.type === "button_group") { classNames.push("o_list_button"); } - // note: remove this oe_read/edit_only logic when form view - // will always be in edit mode - if (/\boe_edit_only\b/.test(column.className)) { - classNames.push("oe_edit_only"); - } else if (/\boe_read_only\b/.test(column.className)) { - classNames.push("oe_read_only"); - } if (column.widget) { classNames.push(`o_${column.widget}_cell`); } diff --git a/addons/web/static/src/views/view_button/view_button.js b/addons/web/static/src/views/view_button/view_button.js index afbd9b4cee6..e9c7222a764 100644 --- a/addons/web/static/src/views/view_button/view_button.js +++ b/addons/web/static/src/views/view_button/view_button.js @@ -92,6 +92,8 @@ export class ViewButton extends Component { this.env[DROPDOWN].close(); } }, + disableAction: this.props.disable, + enableAction: this.props.enable, }); } @@ -133,7 +135,9 @@ ViewButton.props = [ "hotkey?", "icon?", "defaultRank?", + "disable?", "disabled?", + "enable?", "size?", "tabindex?", "title?", @@ -145,4 +149,6 @@ ViewButton.defaultProps = { tag: "button", className: "", clickParams: {}, + disable: () => {}, + enable: () => {}, }; diff --git a/addons/web/static/src/views/view_button/view_button_hook.js b/addons/web/static/src/views/view_button/view_button_hook.js index c237325e7c7..955b34e5ec3 100644 --- a/addons/web/static/src/views/view_button/view_button_hook.js +++ b/addons/web/static/src/views/view_button/view_button_hook.js @@ -15,12 +15,15 @@ function disableButtons(el) { return btns; } -function enableButtons(el, manuallyDisabledButtons) { +function enableButtons(el, manuallyDisabledButtons, enableAction) { if (el) { for (const btn of manuallyDisabledButtons) { btn.removeAttribute("disabled"); } } + if (enableAction) { + enableAction(); + } } function undefinedAsTrue(val) { @@ -37,8 +40,17 @@ export function useViewButtons(model, ref, options = {}) { return true; }); useSubEnv({ - async onClickViewButton({ clickParams, getResParams, beforeExecute }) { + async onClickViewButton({ + clickParams, + getResParams, + beforeExecute, + disableAction, + enableAction, + }) { const manuallyDisabledButtons = disableButtons(getEl()); + if (disableAction) { + disableAction(); + } async function execute() { let _continue = true; @@ -48,7 +60,7 @@ export function useViewButtons(model, ref, options = {}) { _continue = _continue && undefinedAsTrue(await beforeExecuteAction(clickParams)); if (!_continue) { - enableButtons(getEl(), manuallyDisabledButtons); + enableButtons(getEl(), manuallyDisabledButtons, enableAction); return; } const params = getResParams(); @@ -85,7 +97,7 @@ export function useViewButtons(model, ref, options = {}) { error = _e; await doActionParams.onClose(); } - enableButtons(getEl(), manuallyDisabledButtons); + enableButtons(getEl(), manuallyDisabledButtons, enableAction); if (error) { return Promise.reject(error); } @@ -100,7 +112,7 @@ export function useViewButtons(model, ref, options = {}) { }; dialog.add(ConfirmationDialog, dialogProps, { onClose: resolve }); }); - enableButtons(getEl(), manuallyDisabledButtons); + enableButtons(getEl(), manuallyDisabledButtons, enableAction); } else { return execute(); } diff --git a/addons/web/static/src/views/view_compiler.js b/addons/web/static/src/views/view_compiler.js index c34815ac0d8..0cc5e426e39 100644 --- a/addons/web/static/src/views/view_compiler.js +++ b/addons/web/static/src/views/view_compiler.js @@ -254,7 +254,8 @@ export class ViewCompiler { return; } if (!params.enableInvisible) { - let isVisileExpr = `!evalDomainFromRecord(props.record,${JSON.stringify(invisible)})`; + const recordExpr = params.recordExpr || "props.record"; + let isVisileExpr = `!evalDomainFromRecord(${recordExpr},${JSON.stringify(invisible)})`; if (compiled.hasAttribute("t-if")) { const formerTif = compiled.getAttribute("t-if"); isVisileExpr = `( ${formerTif} ) and ${isVisileExpr}`; diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js b/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js index 30552a9d0e6..9a7c4f19ed5 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js @@ -20,13 +20,17 @@ export class SettingsFormRenderer extends FormRenderer { if (!labels[this.props.archInfo.arch]) { labels[this.props.archInfo.arch] = []; } - this.compileParams = { + super.setup(); + this.searchState = useState(this.env.searchState); + } + + get compileParams() { + return { + ...super.compileParams, labels: labels[this.props.archInfo.arch], getFieldExpr: this.getFieldExpr, record: this.props.record, }; - super.setup(); - this.searchState = useState(this.env.searchState); } getFieldExpr(fieldName, fieldWidget) { diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_view.js b/addons/web/static/src/webclient/settings_form_view/settings_form_view.js index 46bad63fee1..795138af49a 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_view.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_view.js @@ -1,6 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; +import { ControlPanel } from "@web/search/control_panel/control_panel"; import { formView } from "@web/views/form/form_view"; import { SettingsFormController } from "./settings_form_controller"; import { SettingsFormRenderer } from "./settings_form_renderer"; @@ -10,6 +11,7 @@ export const settingsFormView = { ...formView, display: {}, buttonTemplate: "web.SettingsFormView.Buttons", + ControlPanel: ControlPanel, Controller: SettingsFormController, Compiler: SettingsFormCompiler, Renderer: SettingsFormRenderer, diff --git a/addons/web/static/src/webclient/webclient_layout.scss b/addons/web/static/src/webclient/webclient_layout.scss index 4d35d4f298e..c225285ff2f 100644 --- a/addons/web/static/src/webclient/webclient_layout.scss +++ b/addons/web/static/src/webclient/webclient_layout.scss @@ -34,7 +34,7 @@ html { > .o_control_panel { flex: 0 0 auto; } - > .o_content { + .o_content { flex: 1 1 auto; position: relative; // Allow to redistribute the 100% height to its child overflow: auto; @@ -47,7 +47,7 @@ html { &:not(.o_action_delegate_scroll) { overflow: auto; - > .o_content { + .o_content { overflow: initial; } } diff --git a/addons/web/static/tests/helpers/utils.js b/addons/web/static/tests/helpers/utils.js index 5d32f1e4450..e48dc2f256e 100644 --- a/addons/web/static/tests/helpers/utils.js +++ b/addons/web/static/tests/helpers/utils.js @@ -385,16 +385,17 @@ export function clickCreate(htmlElement) { } export function clickEdit(htmlElement) { - if (htmlElement.querySelectorAll(".o_form_button_edit").length) { - return click(htmlElement, ".o_form_button_edit"); - } else if (htmlElement.querySelectorAll(".o_list_button_edit").length) { + if (htmlElement.querySelectorAll(".o_list_button_edit").length) { return click(htmlElement, ".o_list_button_edit"); } else { throw new Error("No edit button found to be clicked."); } } -export function clickSave(htmlElement) { +export async function clickSave(htmlElement) { + if (htmlElement.querySelectorAll(".o_form_status_indicator").length) { + await mouseEnter(htmlElement, ".o_form_status_indicator"); + } if (htmlElement.querySelectorAll(".o_form_button_save").length) { return click(htmlElement, ".o_form_button_save"); } else if (htmlElement.querySelectorAll(".o_list_button_save").length) { @@ -404,7 +405,10 @@ export function clickSave(htmlElement) { } } -export function clickDiscard(htmlElement) { +export async function clickDiscard(htmlElement) { + if (htmlElement.querySelectorAll(".o_form_status_indicator").length) { + await mouseEnter(htmlElement, ".o_form_status_indicator"); + } if (htmlElement.querySelectorAll(".o_form_button_cancel").length) { return click(htmlElement, ".o_form_button_cancel"); } else if (htmlElement.querySelectorAll(".o_list_button_discard").length) { diff --git a/addons/web/static/tests/mobile/mobile_form_view_tests.js b/addons/web/static/tests/mobile/mobile_form_view_tests.js index c21fcac3bd5..bb29f0f4452 100644 --- a/addons/web/static/tests/mobile/mobile_form_view_tests.js +++ b/addons/web/static/tests/mobile/mobile_form_view_tests.js @@ -3,7 +3,6 @@ import { registry } from "@web/core/registry"; import { click, - clickEdit, clickSave, editInput, getFixture, @@ -124,7 +123,6 @@ QUnit.module("Mobile Views", ({ beforeEach }) => { ); // change display_name to update buttons modifiers and make it visible - await clickEdit(fixture); await editInput(fixture, ".o_field_widget[name=display_name] input", "test"); await clickSave(fixture); assert.containsOnce( @@ -135,43 +133,6 @@ QUnit.module("Mobile Views", ({ beforeEach }) => { } ); - QUnit.test( - `statusbar "Action" button not displayed in edit mode with .oe_read_only button`, - async (assert) => { - await makeView({ - type: "form", - resModel: "partner", - serverData, - arch: ` -
-
-
- - - - - -
- `, - }); - - assert.containsNone( - fixture, - ".o_statusbar_buttons .dropdown", - "dropdown should not be there" - ); - - await clickSave(fixture); - assert.containsOnce( - fixture, - ".o_statusbar_buttons .dropdown", - "dropdown should not be there" - ); - } - ); - QUnit.test( `statusbar "Action" button shouldn't be displayed for only one visible button`, async (assert) => { @@ -195,8 +156,6 @@ QUnit.module("Mobile Views", ({ beforeEach }) => { `, }); - await clickEdit(fixture); - // There should be a simple statusbar button and no action dropdown assert.containsNone( fixture, @@ -248,7 +207,6 @@ QUnit.module("Mobile Views", ({ beforeEach }) => { `, }); - await clickEdit(fixture); // Now there should an action dropdown, because there are two visible buttons assert.containsOnce( fixture, @@ -449,8 +407,6 @@ QUnit.module("Mobile Views", ({ beforeEach }) => { }, }); - await clickEdit(fixture); - window.scrollTo({ top: 265, left: 0 }); assert.strictEqual(window.scrollY, 265, "Should have scrolled 265 px vertically"); assert.strictEqual(window.screenLeft, 0, "Should be 0 px from left as it is"); diff --git a/addons/web/static/tests/mobile/mobile_statusbar_field_tests.js b/addons/web/static/tests/mobile/mobile_statusbar_field_tests.js index 03254bb08e9..91f50eaa4c6 100644 --- a/addons/web/static/tests/mobile/mobile_statusbar_field_tests.js +++ b/addons/web/static/tests/mobile/mobile_statusbar_field_tests.js @@ -102,10 +102,10 @@ QUnit.module("Mobile Fields", ({ beforeEach }) => { `, }); - assert.hasClass( + assert.doesNotHaveClass( fixture.querySelector(".o_field_statusbar"), "o_field_empty", - "statusbar widget should have class o_field_empty" + "statusbar widget should have class o_field_empty in edit" ); assert.containsOnce( fixture, diff --git a/addons/web/static/tests/views/fields/ace_editor_field_tests.js b/addons/web/static/tests/views/fields/ace_editor_field_tests.js index 7e45300da30..d028d922f38 100644 --- a/addons/web/static/tests/views/fields/ace_editor_field_tests.js +++ b/addons/web/static/tests/views/fields/ace_editor_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { clickEdit, getFixture, triggerEvents } from "@web/../tests/helpers/utils"; +import { getFixture, triggerEvents } from "@web/../tests/helpers/utils"; import { pagerNext } from "@web/../tests/search/helpers"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; @@ -70,7 +70,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await clickEdit(target); await triggerEvents(target, ".ace-view-editor textarea", ["focus", "click"]); assert.hasClass(target.querySelector(".ace-view-editor"), "ace_focus"); }); diff --git a/addons/web/static/tests/views/fields/binary_field_tests.js b/addons/web/static/tests/views/fields/binary_field_tests.js index e680e657b46..edd77e93518 100644 --- a/addons/web/static/tests/views/fields/binary_field_tests.js +++ b/addons/web/static/tests/views/fields/binary_field_tests.js @@ -4,6 +4,7 @@ import { registerCleanup } from "@web/../tests/helpers/cleanup"; import { makeMockXHR } from "@web/../tests/helpers/mock_services"; import { click, + clickSave, editInput, getFixture, makeDeferred, @@ -66,8 +67,81 @@ QUnit.module("Fields", (hooks) => { QUnit.module("BinaryField"); + QUnit.test("BinaryField is correctly rendered (readonly)", async function (assert) { + assert.expect(6); + + async function send(data) { + assert.ok(data instanceof FormData); + assert.strictEqual( + data.get("field"), + "document", + "we should download the field document" + ); + assert.strictEqual( + data.get("data"), + "coucou==\n", + "we should download the correct data" + ); + + this.status = 200; + this.response = new Blob([data.get("data")], { type: "text/plain" }); + } + const MockXHR = makeMockXHR("", send); + + patchWithCleanup( + browser, + { + XMLHttpRequest: MockXHR, + }, + { pure: true } + ); + + await makeView({ + serverData, + type: "form", + resModel: "partner", + arch: ` +
+ + + `, + resId: 1, + }); + assert.containsOnce( + target, + '.o_field_widget[name="document"] a > .fa-download', + "the binary field should be rendered as a downloadable link in readonly" + ); + assert.strictEqual( + target.querySelector('.o_field_widget[name="document"]').textContent, + "coucou.txt", + "the binary field should display the name of the file in the link" + ); + assert.strictEqual( + target.querySelector(".o_field_char").textContent, + "coucou.txt", + "the filename field should have the file name as value" + ); + + // Testing the download button in the field + // We must avoid the browser to download the file effectively + const prom = makeDeferred(); + const downloadOnClick = (ev) => { + const target = ev.target; + if (target.tagName === "A" && "download" in target.attributes) { + ev.preventDefault(); + document.removeEventListener("click", downloadOnClick); + prom.resolve(); + } + }; + document.addEventListener("click", downloadOnClick); + registerCleanup(() => document.removeEventListener("click", downloadOnClick)); + await click(target.querySelector('.o_field_widget[name="document"] a')); + await prom; + }); + QUnit.test("BinaryField is correctly rendered", async function (assert) { - assert.expect(15); + assert.expect(9); async function send(data) { assert.ok(data instanceof FormData); @@ -106,39 +180,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - assert.containsOnce( - target, - '.o_field_widget[name="document"] a > .fa-download', - "the binary field should be rendered as a downloadable link in readonly" - ); - assert.strictEqual( - target.querySelector('.o_field_widget[name="document"]').textContent, - "coucou.txt", - "the binary field should display the name of the file in the link" - ); - assert.strictEqual( - target.querySelector(".o_field_char").textContent, - "coucou.txt", - "the filename field should have the file name as value" - ); - - // Testing the download button in the field - // We must avoid the browser to download the file effectively - const prom = makeDeferred(); - const downloadOnClick = (ev) => { - const target = ev.target; - if (target.tagName === "A" && "download" in target.attributes) { - ev.preventDefault(); - document.removeEventListener("click", downloadOnClick); - prom.resolve(); - } - }; - document.addEventListener("click", downloadOnClick); - registerCleanup(() => document.removeEventListener("click", downloadOnClick)); - await click(target.querySelector('.o_field_widget[name="document"] a')); - await prom; - - await click(target, ".o_form_button_edit"); assert.containsNone( target, @@ -178,7 +219,7 @@ QUnit.module("Fields", (hooks) => { "the filename field should be empty since we removed the file" ); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsNone( target, '.o_field_widget[name="document"] a > .fa-download', @@ -230,8 +271,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target, ".o_form_button_edit"); - const file = new File(["test"], "fake_file.txt", { type: "text/plain" }); await editInput(target, ".o_field_binary .o_input_file", file); diff --git a/addons/web/static/tests/views/fields/boolean_favorite_field_tests.js b/addons/web/static/tests/views/fields/boolean_favorite_field_tests.js index 9a780983108..6881f4254f8 100644 --- a/addons/web/static/tests/views/fields/boolean_favorite_field_tests.js +++ b/addons/web/static/tests/views/fields/boolean_favorite_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, getFixture } from "@web/../tests/helpers/utils"; +import { click, clickSave, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -114,8 +114,6 @@ QUnit.module("Fields", (hooks) => { 'the label should say "Add to Favorites"' ); - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.containsOnce( target, ".o_field_widget .o_favorite > a i.fa.fa-star-o", @@ -141,7 +139,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsOnce( target, ".o_field_widget .o_favorite > a i.fa.fa-star", @@ -188,7 +186,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_list_button_save"); + await clickSave(target); assert.containsOnce( target, ".o_data_row:first .o_field_widget .o_favorite > a i.fa.fa-star-o", diff --git a/addons/web/static/tests/views/fields/boolean_field_tests.js b/addons/web/static/tests/views/fields/boolean_field_tests.js index 86c698d885d..5278b69d38c 100644 --- a/addons/web/static/tests/views/fields/boolean_field_tests.js +++ b/addons/web/static/tests/views/fields/boolean_field_tests.js @@ -2,6 +2,7 @@ import { click, + clickSave, getFixture, nextTick, triggerEvent, @@ -50,15 +51,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.containsOnce(target, ".o_field_boolean input:checked", "checkbox should be checked"); - assert.containsOnce( - target, - ".o_field_boolean input:disabled", - "checkbox should be disabled" - ); - - // switch to edit mode and check the result - await click(target, ".o_form_button_edit"); assert.containsOnce( target, ".o_field_boolean input:checked", @@ -79,15 +71,13 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsNone( target, ".o_field_boolean input:checked", "checkbox should still no longer be checked" ); - // switch to edit mode and test the opposite change - await click(target, ".o_form_button_edit"); assert.containsNone( target, ".o_field_boolean input:checked", @@ -155,7 +145,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsOnce( target, ".o_field_boolean input:checked", @@ -211,7 +201,7 @@ QUnit.module("Fields", (hooks) => { await click(cell, ".o-checkbox"); // save - await click(target.querySelector(".o_list_button_save")); + await clickSave(target); cell = target.querySelector("tr.o_data_row td:not(.o_list_record_selector)"); assert.ok( cell.querySelector(".o-checkbox input:not(:checked)").disabled, @@ -236,7 +226,7 @@ QUnit.module("Fields", (hooks) => { await click(cell, ".o-checkbox"); // Save - await click(target.querySelector(".o_list_button_save")); + await clickSave(target); assert.containsN( target, "tbody td:not(.o_list_record_selector) .o-checkbox", @@ -282,14 +272,7 @@ QUnit.module("Fields", (hooks) => { serverData, arch: `
`, }); - assert.containsOnce(target, ".o_field_boolean input:checked", "checkbox should be checked"); - assert.containsOnce( - target, - ".o_field_boolean input:disabled", - "checkbox should be disabled" - ); - await click(target, ".o_form_button_edit"); assert.containsOnce( target, ".o_field_boolean input:checked", diff --git a/addons/web/static/tests/views/fields/boolean_toggle_field_tests.js b/addons/web/static/tests/views/fields/boolean_toggle_field_tests.js index 1f28dab830b..558154e33b4 100644 --- a/addons/web/static/tests/views/fields/boolean_toggle_field_tests.js +++ b/addons/web/static/tests/views/fields/boolean_toggle_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, clickEdit, getFixture } from "@web/../tests/helpers/utils"; +import { click, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -79,7 +79,7 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - assert.containsOnce(target, ".o_form_readonly"); + assert.containsOnce(target, ".o_form_editable"); assert.containsOnce(target, ".form-check.o_boolean_toggle"); assert.notOk(target.querySelector(".o_boolean_toggle input").disabled); assert.notOk(target.querySelector(".o_boolean_toggle input").checked); @@ -98,8 +98,6 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".form-check.o_boolean_toggle"); assert.ok(target.querySelector(".o_boolean_toggle input").disabled); - await click(target.querySelector(".o_form_button_edit")); - assert.ok(target.querySelector(".o_boolean_toggle input").disabled); }); QUnit.test("BooleanToggleField is enabled in edit mode", async function (assert) { @@ -112,7 +110,6 @@ QUnit.module("Fields", (hooks) => { }); assert.containsOnce(target, ".form-check.o_boolean_toggle"); - await click(target.querySelector(".o_form_button_edit")); assert.notOk(target.querySelector(".o_boolean_toggle input").disabled); assert.notOk(target.querySelector(".o_boolean_toggle input").checked); @@ -165,8 +162,6 @@ QUnit.module("Fields", (hooks) => { ".form-check.o_boolean_toggle", "Boolean toggle widget applied to boolean field" ); - - await clickEdit(target); assert.containsNone(target, ".o_boolean_toggle input:checked"); await click(target, ".o_boolean_toggle"); @@ -193,8 +188,6 @@ QUnit.module("Fields", (hooks) => { ".form-check.o_boolean_toggle", "Boolean toggle widget applied to boolean field" ); - - await clickEdit(target); assert.containsNone(target, ".o_boolean_toggle input:checked"); await click(target, ".o_boolean_toggle"); diff --git a/addons/web/static/tests/views/fields/char_field_tests.js b/addons/web/static/tests/views/fields/char_field_tests.js index 05bd6634daf..95748c10fdf 100644 --- a/addons/web/static/tests/views/fields/char_field_tests.js +++ b/addons/web/static/tests/views/fields/char_field_tests.js @@ -3,6 +3,7 @@ import { registry } from "@web/core/registry"; import { click, + clickSave, editInput, getFixture, makeDeferred, @@ -147,14 +148,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "yop", - "the value should be displayed properly" - ); - - // switch to edit mode and check the result - await click(target, ".o_form_button_edit"); assert.containsOnce( target, ".o_field_widget input[type='text']", @@ -170,9 +163,9 @@ QUnit.module("Fields", (hooks) => { await editInput(target, ".o_field_widget input[type='text']", "limbo"); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input[type='text']").value, "limbo", "the new value should be displayed" ); @@ -203,12 +196,8 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - await editInput(target, ".o_field_widget input[type='text']", ""); - - // save - await click(target, ".o_form_button_save"); + await clickSave(target); } ); @@ -229,9 +218,6 @@ QUnit.module("Fields", (hooks) => { `, }); - - await click(target, ".o_form_button_edit"); - assert.hasAttrValue( target.querySelector("input"), "maxlength", @@ -271,7 +257,7 @@ QUnit.module("Fields", (hooks) => { await editInput(cell, "input", "brolo"); // save - await click(target, ".o_list_button_save"); + await clickSave(target); cell = target.querySelector("tbody td:not(.o_list_record_selector)"); assert.doesNotHaveClass( cell.parentElement, @@ -336,8 +322,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - assert.hasClass(target.querySelector("[name=foo] input"), "o_field_translate"); assert.containsOnce( @@ -465,7 +449,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await click(target, ".o_form_button_edit"); // this will not affect the translate_fields effect until the record is // saved but is set for consistency of the test @@ -546,12 +529,10 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); await editInput(target, "[name='foo'] input", ""); - - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "", "the value should have been properly escaped" ); @@ -576,16 +557,9 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); - await editInput(target, ".o_field_widget[name='foo'] input", " abc "); - await editInput(target, ".o_field_widget[name='foo2'] input", " def "); - - await click(target, ".o_form_button_save"); - - // edit mode - await click(target, ".o_form_button_edit"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_widget[name='foo'] input").value, "abc", @@ -629,7 +603,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); await click(target, ".o_field_x2many_list_row_add a"); assert.strictEqual( target.querySelector(".o_field_widget[name='foo'] input").value, @@ -763,8 +736,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_field_widget[name='foo'] input").value, "yop", @@ -802,7 +773,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target.querySelector(".o_form_button_edit")); assert.hasAttrValue( target.querySelector('.o_field_widget[name="display_name"] input'), "autocomplete", @@ -820,7 +790,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target.querySelector(".o_form_button_edit")); assert.hasAttrValue( target.querySelector('.o_field_widget[name="display_name"] input'), "autocomplete", @@ -838,12 +807,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - assert.strictEqual( - target.querySelector('.o_field_widget[name="foo"]').innerText, - "***", - "password should be displayed with stars" - ); - await click(target.querySelector(".o_form_button_edit")); assert.strictEqual( target.querySelector('.o_field_widget[name="foo"] input').value, "yop", @@ -856,6 +819,30 @@ QUnit.module("Fields", (hooks) => { ); }); + QUnit.test("input field: readonly password", async function (assert) { + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: ` +
+ + `, + }); + + assert.notEqual( + target.querySelector(".o_field_char").textContent, + "yop", + "password field value should not be visible in read mode" + ); + assert.strictEqual( + target.querySelector(".o_field_char").textContent, + "***", + "password field value should be hidden with '*' in read mode" + ); + }); + QUnit.test("input field: change password value", async function (assert) { await makeView({ type: "form", @@ -868,19 +855,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.notEqual( - target.querySelector(".o_field_char").textContent, - "yop", - "password field value should not be visible in read mode" - ); - assert.strictEqual( - target.querySelector(".o_field_char").textContent, - "***", - "password field value should be hidden with '*' in read mode" - ); - - await click(target, ".o_form_button_edit"); - assert.hasAttrValue( target.querySelector(".o_field_char input"), "type", @@ -908,14 +882,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual( - target.querySelector(".o_field_char").textContent, - "", - "password field value should be empty in read mode" - ); - - await click(target, ".o_form_button_edit"); - assert.hasAttrValue( target.querySelector(".o_field_char input"), "type", diff --git a/addons/web/static/tests/views/fields/color_field_tests.js b/addons/web/static/tests/views/fields/color_field_tests.js index abf8a7beafa..abab67061d0 100644 --- a/addons/web/static/tests/views/fields/color_field_tests.js +++ b/addons/web/static/tests/views/fields/color_field_tests.js @@ -1,5 +1,5 @@ /** @odoo-module **/ -import { click, editInput, getFixture, clickEdit } from "@web/../tests/helpers/utils"; +import { click, editInput, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -129,7 +129,6 @@ QUnit.module("Fields", (hooks) => { "field has the default color set as background if no value has been selected" ); assert.strictEqual(target.querySelector(".o_field_color input").value, "#000000"); - await clickEdit(target); await editInput(target, ".o_field_char[name='foo'] input", "someValue"); assert.verifySteps([ 'onchange [[1],{"id":1,"foo":"someValue","hex_color":false},"foo",{"foo":"1","hex_color":""}]', diff --git a/addons/web/static/tests/views/fields/color_picker_field_tests.js b/addons/web/static/tests/views/fields/color_picker_field_tests.js index b8d5d78d2a3..f5050684848 100644 --- a/addons/web/static/tests/views/fields/color_picker_field_tests.js +++ b/addons/web/static/tests/views/fields/color_picker_field_tests.js @@ -59,9 +59,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // switch to edit mode - await click(target, ".o_form_button_edit"); - assert.hasClass( target.querySelectorAll(".o_field_color_picker button"), "o_colorlist_item_color_0", @@ -102,9 +99,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // switch to edit mode - await click(target, ".o_form_button_edit"); - await click(target, ".o_field_color_picker button"); assert.strictEqual( diff --git a/addons/web/static/tests/views/fields/date_field_tests.js b/addons/web/static/tests/views/fields/date_field_tests.js index 5a17d19374f..a26b6cc2179 100644 --- a/addons/web/static/tests/views/fields/date_field_tests.js +++ b/addons/web/static/tests/views/fields/date_field_tests.js @@ -9,6 +9,8 @@ import { patchTimeZone, triggerEvent, triggerEvents, + clickSave, + clickCreate, } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services"; @@ -120,7 +122,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); assert.containsNone( document.body, ".bootstrap-datetimepicker-widget", @@ -155,11 +156,11 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce( target, - ".o_field_widget > span", - "should have one span in the form view" + ".o_field_widget input", + "should have one input in the form view" ); assert.strictEqual( - target.querySelector(".o_field_widget > span").textContent, + target.querySelector(".o_field_widget input").value, "", "and it should be empty" ); @@ -175,7 +176,6 @@ QUnit.module("Fields", (hooks) => { serverData, arch: '
', }); - await click(target, ".o_form_button_edit"); const input = target.querySelector(".o_field_widget[name='date'] input"); assert.strictEqual(input.value, "02/03/2017"); @@ -196,7 +196,6 @@ QUnit.module("Fields", (hooks) => { serverData, arch: '
', }); - await click(target, ".o_form_button_edit"); const input = target.querySelector(".o_field_widget[name='date'] input"); assert.strictEqual(input.value, ""); @@ -215,7 +214,6 @@ QUnit.module("Fields", (hooks) => { serverData, arch: '
', }); - await click(target, ".o_form_button_edit"); await click(target, ".o_datepicker input"); // open datepicker and select a date @@ -236,7 +234,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("DateField in form view (with positive time zone offset)", async function (assert) { - assert.expect(8); + assert.expect(7); patchTimeZone(120); // Should be ignored by date fields @@ -257,14 +255,6 @@ QUnit.module("Fields", (hooks) => { }, }); - assert.strictEqual( - target.querySelector(".o_field_date").textContent, - "02/03/2017", - "the date should be correctly displayed in readonly" - ); - - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_datepicker_input").value, "02/03/2017", @@ -304,9 +294,9 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_date").textContent, + target.querySelector(".o_field_date input").value, "02/22/2017", "the selected date should be displayed after saving" ); @@ -323,14 +313,6 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); - assert.strictEqual( - target.querySelector(".o_field_date").textContent, - "02/03/2017", - "the date should be correctly displayed in readonly" - ); - - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_datepicker_input").value, "02/03/2017", @@ -351,7 +333,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); await click(target, ".o_datepicker input"); assert.containsOnce( @@ -379,8 +360,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // switch to edit mode - await click(target, ".o_form_button_edit"); // open datepicker and select another value await click(target, ".o_datepicker input"); @@ -430,8 +409,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_field_widget[name='date'] input").value, @@ -439,11 +416,8 @@ QUnit.module("Fields", (hooks) => { "The existing record should have a value for the date field" ); - // save with no changes - await click(target, ".o_form_button_save"); - //Create a new record - await click(target, ".o_form_button_create"); + await clickCreate(target); assert.notOk( target.querySelector(".o_field_widget[name='date'] input").value, "The new record should not have a value that the framework would have set" @@ -513,7 +487,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target.querySelector(".o_list_button_save")); + await clickSave(target); assert.strictEqual( target.querySelector("tr.o_data_row td:not(.o_list_record_selector)").textContent, "02/22/2017", @@ -534,8 +508,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_datepicker_input").value, @@ -553,7 +525,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_date").textContent, "", @@ -574,7 +546,6 @@ QUnit.module("Fields", (hooks) => { assert.step(method); }, }); - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_datepicker_input").value, @@ -585,8 +556,8 @@ QUnit.module("Fields", (hooks) => { const input = target.querySelector(".o_field_widget[name='datetime'] input"); input.value = "02/08/2017"; await triggerEvents(input, null, ["input", "change", "focusout"]); - await click(target, ".o_form_button_save"); + assert.containsOnce(target, ".o_form_saved"); assert.verifySteps(["get_views", "read"]); // should not have save as nothing changed } ); @@ -604,7 +575,6 @@ QUnit.module("Fields", (hooks) => { serverData, arch: '
', }); - await click(target, ".o_form_button_edit"); $(target).on("show.datetimepicker", () => { assert.containsOnce( @@ -660,8 +630,7 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - const dateViewForm = target.querySelector(".o_field_date").textContent; - await click(target.querySelector(".o_form_button_edit")); + const dateViewForm = target.querySelector(".o_field_date input").value; await click(target, ".o_datepicker input"); assert.strictEqual( @@ -671,9 +640,9 @@ QUnit.module("Fields", (hooks) => { ); await click(document.body.querySelector(".day[data-day*='/22/']")); const dateEditForm = target.querySelector(".o_datepicker_input").value; - await click(target.querySelector(".o_form_button_save")); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_date").textContent, + target.querySelector(".o_field_date input").value, dateEditForm, "date field should be the same as the one selected in the view form" ); @@ -692,7 +661,6 @@ QUnit.module("Fields", (hooks) => { serverData, arch: '
', }); - await click(target, ".o_form_button_edit"); const year = new Date().getFullYear(); const input = target.querySelector(".o_field_widget[name='date'] input"); diff --git a/addons/web/static/tests/views/fields/daterange_field_tests.js b/addons/web/static/tests/views/fields/daterange_field_tests.js index 58c6bbc5028..3cdf6dc4f3d 100644 --- a/addons/web/static/tests/views/fields/daterange_field_tests.js +++ b/addons/web/static/tests/views/fields/daterange_field_tests.js @@ -2,6 +2,7 @@ import { click, + clickSave, editInput, getFixture, patchDate, @@ -69,22 +70,6 @@ QUnit.module("Fields", (hooks) => { `, }); - let fields = target.querySelectorAll(".o_field_daterange"); - // Check date display correctly in readonly - assert.strictEqual( - fields[0].textContent, - "02/08/2017 15:30:00", - "the start date should be correctly displayed in readonly" - ); - assert.strictEqual( - fields[fields.length - 1].textContent, - "03/13/2017 05:30:00", - "the end date should be correctly displayed in readonly" - ); - - // Edit - await click(target, ".o_form_button_edit"); - // Check date range picker initialization assert.containsN( document.body, @@ -103,7 +88,7 @@ QUnit.module("Fields", (hooks) => { ); // open the first one - fields = target.querySelectorAll(".o_field_daterange"); + let fields = target.querySelectorAll(".o_field_daterange"); await click(fields[0].querySelector("input")); let datepicker = document.querySelector( @@ -206,12 +191,12 @@ QUnit.module("Fields", (hooks) => { ); // Save - await click(target, ".o_form_button_save"); - fields = target.querySelectorAll(".o_field_daterange"); + await clickSave(target); + fields = target.querySelectorAll(".o_field_daterange input"); // Check date after save - assert.strictEqual(fields[0].textContent, "02/08/2017 15:30:00"); - assert.strictEqual(fields[fields.length - 1].textContent, "02/09/2017 05:30:00"); + assert.strictEqual(fields[0].value, "02/08/2017 15:30:00"); + assert.strictEqual(fields[fields.length - 1].value, "02/09/2017 05:30:00"); } ); @@ -234,23 +219,6 @@ QUnit.module("Fields", (hooks) => { }); let fields = target.querySelectorAll(".o_field_daterange"); - - // Check date display correctly in readonly - assert.strictEqual( - fields[0].textContent, - "02/03/2017", - "the start date should be correctly displayed in readonly" - ); - assert.strictEqual( - fields[fields.length - 1].textContent, - "02/08/2017", - "the end date should be correctly displayed in readonly" - ); - - // Edit - await click(target, ".o_form_button_edit"); - - fields = target.querySelectorAll(".o_field_daterange"); const datepickers = document.querySelectorAll(`.daterangepicker`); // Check date range picker initialization @@ -361,17 +329,17 @@ QUnit.module("Fields", (hooks) => { ); // Save - await click(target, ".o_form_button_save"); + await clickSave(target); fields = target.querySelectorAll(".o_field_daterange"); // Check date after save assert.strictEqual( - fields[0].textContent, + fields[0].querySelector("input").value, "02/13/2017", "the start date should be '02/13/2017' after save" ); assert.strictEqual( - fields[fields.length - 1].textContent, + fields[1].querySelector("input").value, "03/18/2017", "the end date should be '03/18/2017' after save" ); @@ -399,9 +367,7 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); await click(target.querySelector(".o_field_daterange[name='datetime'] input")); - assert.isVisible( document.querySelector(".daterangepicker[data-name='datetime']"), "date range picker should be opened" @@ -445,18 +411,16 @@ QUnit.module("Fields", (hooks) => { // check date display correctly in readonly assert.strictEqual( - target.querySelector(".o_field_daterange").textContent, + target.querySelector(".o_field_daterange input").value, "02/08/2017 15:30:00", "the start date should be correctly displayed in readonly" ); assert.strictEqual( - target.querySelectorAll(".o_field_daterange")[1].textContent, + target.querySelectorAll(".o_field_daterange input")[1].value, "03/13/2017 05:30:00", "the end date should be correctly displayed in readonly" ); - // edit form - await click(target.querySelector(".o_form_button_edit")); // update input for Datetime await editInput( target, @@ -464,12 +428,12 @@ QUnit.module("Fields", (hooks) => { "02/08/2017 11:30:00" ); // save form - await click(target.querySelector(".o_form_button_save")); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_daterange").textContent, + target.querySelector(".o_field_daterange input").value, "02/08/2017 11:30:00", - "the start date should be correctly displayed in readonly after manual update" + "the start date should be correctly displayed after manual update" ); } ); @@ -492,7 +456,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_daterange[name='date'] input", "blabla"); // click outside daterange field await click(target); @@ -511,7 +474,7 @@ QUnit.module("Fields", (hooks) => { // again enter wrong value and try to save should raise invalid fields value await editInput(target, ".o_field_daterange[name='date'] input", "blabla"); - await click(target.querySelector(".o_form_button_save")); + await clickSave(target); assert.strictEqual( target.querySelector(".o_notification_title").textContent, "Invalid fields: " @@ -591,12 +554,12 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_field_daterange[name='datetime']").textContent, + target.querySelector(".o_field_daterange[name='datetime'] input").value, "02/08/2017", "the start date should only show date when option formatType is Date" ); assert.strictEqual( - target.querySelector(".o_field_daterange[name='datetime_end']").textContent, + target.querySelector(".o_field_daterange[name='datetime_end'] input").value, "03/13/2017", "the end date should only show date when option formatType is Date" ); diff --git a/addons/web/static/tests/views/fields/datetime_field_tests.js b/addons/web/static/tests/views/fields/datetime_field_tests.js index 9f33d0496cf..70eb8fd068d 100644 --- a/addons/web/static/tests/views/fields/datetime_field_tests.js +++ b/addons/web/static/tests/views/fields/datetime_field_tests.js @@ -4,6 +4,7 @@ import { registry } from "@web/core/registry"; import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services"; import { click, + clickSave, editInput, getFixture, patchTimeZone, @@ -67,13 +68,11 @@ QUnit.module("Fields", (hooks) => { const expectedDateString = "02/08/2017 12:00:00"; // 10:00:00 without timezone assert.strictEqual( - target.querySelector(".o_field_datetime").textContent, + target.querySelector(".o_field_datetime input").value, expectedDateString, "the datetime should be correctly displayed in readonly" ); - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_datepicker_input").value, expectedDateString, @@ -126,9 +125,9 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_datetime").textContent, + target.querySelector(".o_field_datetime input").value, newExpectedDateString, "the selected date should be displayed after saving" ); @@ -155,8 +154,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - await click(target, ".o_datepicker_input"); assert.containsOnce(document.body, ".bootstrap-datetimepicker-widget"); @@ -325,7 +322,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target.querySelector(".o_list_button_save")); + await clickSave(target); assert.strictEqual( target.querySelector("tr.o_data_row td:not(.o_list_record_selector)").textContent, newExpectedDateString, @@ -390,8 +387,6 @@ QUnit.module("Fields", (hooks) => { }, }); - // switch to edit mode - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_datepicker_input").value, "02/08/2017 12:00:00", @@ -408,7 +403,7 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_datetime").textContent, "", @@ -452,7 +447,7 @@ QUnit.module("Fields", (hooks) => { // switch to form view await click(target, ".o_field_widget[name='p'] .o_data_cell"); assert.strictEqual( - document.body.querySelector(".modal .o_field_date[name='datetime']").textContent, + document.body.querySelector(".modal .o_field_date[name='datetime'] input").value, "02/07/2017", "the datetime (date widget) should be correctly displayed in form view" ); @@ -495,7 +490,7 @@ QUnit.module("Fields", (hooks) => { // switch to form view await click(target, ".o_field_widget[name='p'] .o_data_cell"); assert.strictEqual( - document.body.querySelector(".modal .o_field_date[name='datetime']").textContent, + document.body.querySelector(".modal .o_field_date[name='datetime'] input").value, "02/08/2017", "the datetime (date widget) should be correctly displayed in form view" ); @@ -556,8 +551,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target, ".o_form_button_edit"); - const datetime = target.querySelector(".o_field_datetime input"); // Enter a beginning of date and press enter to validate @@ -573,10 +566,10 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(datetime.value, datetimeValue); // Save and check that it's still ok - await click(target, ".o_form_button_save"); + await clickSave(target); - const { textContent } = target.querySelector(".o_field_datetime span"); - assert.strictEqual(textContent, datetimeValue); + const { value } = target.querySelector(".o_field_datetime input"); + assert.strictEqual(value, datetimeValue); }); QUnit.test( @@ -595,15 +588,13 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target, ".o_form_button_edit"); - await editInput(target, ".o_field_widget input", "01/08/22"); await triggerEvent(target, ".o_field_widget input", "keydown", { key: "Enter" }); assert.strictEqual(target.querySelector(".o_field_widget input").value, "01/08/2022"); // Click outside the field to check that the field is not changed - await click(target); + await clickSave(target); assert.strictEqual(target.querySelector(".o_field_widget input").value, "01/08/2022"); } ); diff --git a/addons/web/static/tests/views/fields/domain_field_tests.js b/addons/web/static/tests/views/fields/domain_field_tests.js index a4460e31347..85d413641bc 100644 --- a/addons/web/static/tests/views/fields/domain_field_tests.js +++ b/addons/web/static/tests/views/fields/domain_field_tests.js @@ -3,6 +3,7 @@ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { click, + clickSave, editInput, getFixture, makeDeferred, @@ -112,7 +113,7 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_read_mode").textContent, + target.querySelector(".o_edit_mode").textContent, "This domain is not supported.", "The widget should not crash the view, but gracefully admit its failure." ); @@ -136,7 +137,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); // As the domain is empty, there should be a button to add the first // domain part @@ -189,7 +189,7 @@ QUnit.module("Fields", (hooks) => { // Saving the form view should show a readonly domain containing the // "color" field - await click(target, ".o_form_button_save"); + await clickSave(target); assert.ok( target.querySelector(".o_field_domain").textContent.includes("Color index"), "field selector readonly value should now contain 'Color index'" @@ -216,7 +216,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); // As the domain is equal to [["id", "=", 1]] there should be a field // selector to change this @@ -293,7 +292,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_domain_show_selection_button").textContent.trim(), @@ -312,7 +310,7 @@ QUnit.module("Fields", (hooks) => { ); QUnit.test("domain field: handle false domain as []", async function (assert) { - assert.expect(4); + assert.expect(3); serverData.models.partner.records[0].foo = false; serverData.models.partner.fields.bar.type = "char"; @@ -343,8 +341,6 @@ QUnit.module("Fields", (hooks) => { ".o_field_widget[name='foo']:not(.o_field_empty)", "there should be a domain field, not considered empty" ); - - await click(target, ".o_form_button_edit"); assert.containsNone( target, ".o_field_widget[name='foo'] .text-warning", @@ -460,13 +456,10 @@ QUnit.module("Fields", (hooks) => { await doAction(webClient, 1); assert.verifySteps(["[]"]); - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_domain_show_selection_button").textContent.trim(), "2 record(s)" ); - assert.verifySteps(["[]"]); await editInput(target, ".o_domain_debug_input", "[['id', '<', 40]]"); // the count should not be re-computed when editing with the textarea @@ -476,15 +469,12 @@ QUnit.module("Fields", (hooks) => { ); assert.verifySteps([]); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_domain_show_selection_button").textContent.trim(), "1 record(s)" ); - assert.verifySteps([ - '[["id","<",40]]', // to validate the domain, before saving - '[["id","<",40]]', // to render in readonly once it has been saved - ]); + assert.verifySteps(['[["id","<",40]]']); }); QUnit.test( @@ -531,13 +521,10 @@ QUnit.module("Fields", (hooks) => { await doAction(webClient, 1); assert.verifySteps(["[]"]); - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_domain_show_selection_button").textContent.trim(), "2 record(s)" ); - assert.verifySteps(["[]"]); await editInput(target, ".o_domain_debug_input", "[['abc']]"); // the count should not be re-computed when editing with the textarea @@ -547,7 +534,7 @@ QUnit.module("Fields", (hooks) => { ); assert.verifySteps([]); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.hasClass( target.querySelector(".o_field_domain"), "o_field_invalid", @@ -603,8 +590,6 @@ QUnit.module("Fields", (hooks) => { await doAction(webClient, 1); assert.verifySteps(["[]"]); - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_domain_show_selection_button").textContent.trim(), "2 record(s)" @@ -616,7 +601,6 @@ QUnit.module("Fields", (hooks) => { target.querySelector(".o_domain_show_selection_button").textContent.trim(), "2 record(s)" ); - assert.verifySteps(["[]"]); // click on the refresh button await click(target, ".o_refresh_count"); @@ -709,7 +693,6 @@ QUnit.module("Fields", (hooks) => { }); await doAction(webClient, 1); - await click(target, ".o_form_button_edit"); assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); rawDomain = ` @@ -720,7 +703,7 @@ QUnit.module("Fields", (hooks) => { await editInput(target, ".o_domain_debug_input", rawDomain); assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); - await click(target, ".o_form_button_save"); + await clickSave(target); }); QUnit.test("domain field: edit through selector (dynamic content)", async function (assert) { @@ -762,8 +745,6 @@ QUnit.module("Fields", (hooks) => { await doAction(webClient, 1); assert.verifySteps(["/web/action/load", "get_views", "read", "search_count", "fields_get"]); - await click(target, ".o_form_button_edit"); - assert.verifySteps(["search_count"]); assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); assert.containsOnce(target, ".o_datepicker", "there should be a datepicker"); @@ -775,15 +756,6 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); assert.verifySteps([]); - // Save - await click(target, ".o_form_button_save"); - assert.verifySteps(["search_count"]); - - // Edit again - await click(target, ".o_form_button_edit"); - assert.verifySteps(["search_count"]); - assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); - // Manually input a date rawDomain = `[("date", ">=", "2020-09-09")]`; await editInput(target, ".o_datepicker_input", "09/09/2020"); @@ -791,12 +763,8 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); // Save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.verifySteps(["write", "read", "search_count"]); - - // Edit again - await click(target, ".o_form_button_edit"); - assert.verifySteps(["search_count"]); assert.strictEqual(target.querySelector(".o_domain_debug_input").value, rawDomain); }); diff --git a/addons/web/static/tests/views/fields/email_field_tests.js b/addons/web/static/tests/views/fields/email_field_tests.js index 13d588a5633..eaaa211bc6c 100644 --- a/addons/web/static/tests/views/fields/email_field_tests.js +++ b/addons/web/static/tests/views/fields/email_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, editInput, getFixture } from "@web/../tests/helpers/utils"; +import { click, clickSave, editInput, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -58,13 +58,8 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - let mailtoLink = target.querySelector(".o_field_email a.o_form_uri.o_text_overflow"); - assert.containsOnce(target, mailtoLink, "should have a anchor with correct classes"); - assert.strictEqual(mailtoLink.textContent, "yop", "the value should be displayed properly"); - assert.hasAttrValue(mailtoLink, "href", "mailto:yop", "should have proper mailto prefix"); // switch to edit mode and check the result - await click(target.querySelector(".o_form_button_edit")); const mailtoEdit = target.querySelector('.o_field_email input[type="email"]'); assert.containsOnce(target, mailtoEdit, "should have an input for the email field"); assert.strictEqual( @@ -77,15 +72,9 @@ QUnit.module("Fields", (hooks) => { await editInput(target, ".o_field_email input[type='email']", "new"); // save - await click(target.querySelector(".o_form_button_save")); - mailtoLink = target.querySelector(".o_field_email a"); - assert.strictEqual(mailtoLink.textContent, "new", "new value should be displayed properly"); - assert.hasAttrValue( - mailtoLink, - "href", - "mailto:new", - "should still have proper mailto prefix" - ); + await clickSave(target); + const mailtoLink = target.querySelector(".o_field_email input[type='email']"); + assert.strictEqual(mailtoLink.value, "new", "new value should be displayed properly"); }); QUnit.test("EmailField in editable list view", async function (assert) { @@ -132,7 +121,7 @@ QUnit.module("Fields", (hooks) => { await editInput(cell, "input", "new"); // save - await click(target.querySelector(".o_list_button_save")); + await clickSave(target); cell = target.querySelector("tbody td:not(.o_list_record_selector)"); assert.doesNotHaveClass( cell.parentElement, @@ -168,11 +157,9 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual(target.querySelector(".o_field_email input").placeholder, "Placeholder"); - - await click(target.querySelector(".o_form_button_save")); - const mailtoLink = target.querySelector(".o_field_email a"); - assert.strictEqual(mailtoLink.textContent, "", "the value should be displayed properly"); + const input = target.querySelector(".o_field_email input"); + assert.strictEqual(input.placeholder, "Placeholder"); + assert.strictEqual(input.value, "", "the value should be displayed properly"); }); QUnit.test("EmailField trim user value", async function (assert) { @@ -184,8 +171,7 @@ QUnit.module("Fields", (hooks) => { }); await editInput(target, ".o_field_widget[name='foo'] input", " abc@abc.com "); const mailFieldInput = target.querySelector('.o_field_widget[name="foo"] input'); - await click(target.querySelector(".o_form_button_save")); - await click(target.querySelector(".o_form_button_edit")); + await clickSave(target); assert.strictEqual( mailFieldInput.value, "abc@abc.com", @@ -229,7 +215,6 @@ QUnit.module("Fields", (hooks) => { ); // edit the phone field, but with the mail in readonly mode - await click(target.querySelector(".o_form_button_edit")); await editInput(target, ".o_field_widget[name='int_field'] input", 3); // check rendering after changes diff --git a/addons/web/static/tests/views/fields/float_factor_field_tests.js b/addons/web/static/tests/views/fields/float_factor_field_tests.js index ff5ebeff88a..3e0ec00ac5e 100644 --- a/addons/web/static/tests/views/fields/float_factor_field_tests.js +++ b/addons/web/static/tests/views/fields/float_factor_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, editInput, getFixture } from "@web/../tests/helpers/utils"; +import { clickSave, editInput, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -25,7 +25,7 @@ QUnit.module("Fields", (hooks) => { QUnit.module("FloatFactorField"); QUnit.test("FloatFactorField in form view", async function (assert) { - assert.expect(4); + assert.expect(3); await makeView({ type: "form", @@ -45,13 +45,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "4.55", // 9.1 / 0.5 - "The formatted value should be displayed properly." - ); - - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_field_widget[name='qux'] input").value, "4.55", @@ -59,10 +52,10 @@ QUnit.module("Fields", (hooks) => { ); await editInput(target, ".o_field_widget[name='qux'] input", "2.3"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "2.30", "The new value should be saved and displayed properly." ); diff --git a/addons/web/static/tests/views/fields/float_field_tests.js b/addons/web/static/tests/views/fields/float_field_tests.js index 6a84d68e00e..478825efa80 100644 --- a/addons/web/static/tests/views/fields/float_field_tests.js +++ b/addons/web/static/tests/views/fields/float_field_tests.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services"; -import { click, editInput, getFixture, triggerEvent } from "@web/../tests/helpers/utils"; +import { click, clickSave, editInput, getFixture, triggerEvent } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { registry } from "@web/core/registry"; @@ -50,7 +50,7 @@ QUnit.module("Fields", (hooks) => { ); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "0.00", "Non-set float field should be considered as 0." ); @@ -68,7 +68,7 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_field_float").textContent, + target.querySelector(".o_field_float input").value, "0.4", "should contain a number rounded to 1 decimal" ); @@ -84,7 +84,7 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_field_float").textContent, + target.querySelector(".o_field_float input").value, "0.4", "should contain a number rounded to 1 decimal" ); @@ -100,7 +100,7 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_field_float").textContent, + target.querySelector(".o_field_float input").value, "0.4", "should contain a number rounded to 1 decimal" ); @@ -115,8 +115,6 @@ QUnit.module("Fields", (hooks) => { arch: `
`, }); - await click(target, ".o_form_button_edit"); - assert.ok( target.querySelector(".o_field_widget input").hasAttribute("step"), "Integer field with option type must have a step attribute." @@ -145,19 +143,11 @@ QUnit.module("Fields", (hooks) => { "Float field should be considered set for value 0." ); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "0.000", "The value should be displayed properly." ); - await click(target, ".o_form_button_edit"); - - assert.strictEqual( - target.querySelector(".o_field_widget[name=float_field] input").value, - "0.000", - "The value should be rendered with correct precision." - ); - await editInput(target, 'div[name="float_field"] input', "108.2451938598598"); assert.strictEqual( target.querySelector(".o_field_widget[name=float_field] input").value, @@ -166,10 +156,10 @@ QUnit.module("Fields", (hooks) => { ); await editInput(target, ".o_field_widget[name=float_field] input", "18.8958938598598"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "18.896", "The new value should be rounded properly." ); @@ -184,40 +174,36 @@ QUnit.module("Fields", (hooks) => { arch: `
`, }); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_widget[name=float_field] input", "=20+3*2"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "26.000", "The new value should be calculated properly." ); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_widget[name=float_field] input", "=2**3"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "8.000", "The new value should be calculated properly." ); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_widget[name=float_field] input", "=2^3"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "8.000", "The new value should be calculated properly." ); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_widget[name=float_field] input", "=100/3"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "33.333", "The new value should be calculated properly." ); @@ -232,9 +218,8 @@ QUnit.module("Fields", (hooks) => { arch: `
`, }); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_widget[name=float_field] input", "=abc"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.hasClass( target.querySelector(".o_field_widget[name=float_field]"), @@ -244,7 +229,7 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_form_editable", "form view should still be editable"); await editInput(target, ".o_field_widget[name=float_field] input", "=3:2?+4"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsOnce(target, ".o_form_editable", "form view should still be editable"); assert.hasClass( @@ -291,32 +276,6 @@ QUnit.module("Fields", (hooks) => { ); }); - QUnit.test("do not trigger a field_changed if they have not changed", async function (assert) { - serverData.models.partner.records[0].float_field = false; - serverData.models.partner.records[0].int_field = false; - await makeView({ - serverData, - type: "form", - resModel: "partner", - arch: ` -
- - - - -
`, - mockRPC(route, { method }) { - assert.step(method); - }, - resId: 1, - }); - - await click(target.querySelector(".o_form_button_edit")); - await click(target.querySelector(".o_form_button_save")); - - assert.verifySteps(["get_views", "read"]); // should not have save as nothing changed - }); - QUnit.test("float field with type number option", async function (assert) { await makeView({ serverData, @@ -336,7 +295,6 @@ QUnit.module("Fields", (hooks) => { makeFakeLocalizationService({ thousandsSep: ",", grouping: [3, 0] }) ); - await click(target.querySelector(".o_form_button_edit")); assert.ok( target.querySelector(".o_field_widget input").hasAttribute("type"), "Float field with option type must have a type attribute." @@ -348,19 +306,12 @@ QUnit.module("Fields", (hooks) => { 'Float field with option type must have a type attribute equals to "number".' ); await editInput(target, ".o_field_widget[name=float_field] input", "123456.7890"); - await click(target.querySelector(".o_form_button_save")); - await click(target.querySelector(".o_form_button_edit")); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "123456.789", "Float value must be not formatted if input type is number. (but the trailing 0 is gone)" ); - await click(target.querySelector(".o_form_button_save")); - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "123,456.79", - "Float value must be formatted in readonly view even if the input type is number." - ); }); QUnit.test( @@ -386,7 +337,6 @@ QUnit.module("Fields", (hooks) => { }) ); - await click(target.querySelector(".o_form_button_edit")); assert.ok( target.querySelector(".o_field_widget input").hasAttribute("type"), "Float field with option type must have a type attribute." @@ -398,19 +348,12 @@ QUnit.module("Fields", (hooks) => { 'Float field with option type must have a type attribute equals to "number".' ); await editInput(target, ".o_field_widget[name=float_field] input", "123456.789"); - await click(target.querySelector(".o_form_button_save")); - await click(target.querySelector(".o_form_button_edit")); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "123456.789", "Float value must be not formatted if input type is number." ); - await click(target.querySelector(".o_form_button_save")); - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "123.456,79", - "Float value must be formatted in readonly view even if the input type is number." - ); } ); @@ -430,7 +373,6 @@ QUnit.module("Fields", (hooks) => { makeFakeLocalizationService({ thousandsSep: ",", grouping: [3, 0] }) ); - await click(target.querySelector(".o_form_button_edit")); assert.hasAttrValue( target.querySelector(".o_field_widget input"), "type", @@ -439,8 +381,7 @@ QUnit.module("Fields", (hooks) => { ); await editInput(target, ".o_field_widget[name=float_field] input", "123456.7890"); - await click(target.querySelector(".o_form_button_save")); - await click(target.querySelector(".o_form_button_edit")); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "123,456.79", diff --git a/addons/web/static/tests/views/fields/float_time_field_tests.js b/addons/web/static/tests/views/fields/float_time_field_tests.js index e8aada0d09d..e9b9b59f313 100644 --- a/addons/web/static/tests/views/fields/float_time_field_tests.js +++ b/addons/web/static/tests/views/fields/float_time_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, editInput, getFixture, triggerEvent } from "@web/../tests/helpers/utils"; +import { clickSave, editInput, getFixture, triggerEvent } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -26,7 +26,7 @@ QUnit.module("Fields", (hooks) => { QUnit.module("FloatTimeField"); QUnit.test("FloatTimeField in form view", async function (assert) { - assert.expect(5); + assert.expect(4); await makeView({ serverData, @@ -52,13 +52,6 @@ QUnit.module("Fields", (hooks) => { }); // 9 + 0.1 * 60 = 9.06 - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "09:06", - "The formatted time value should be displayed properly." - ); - - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_field_float_time[name=qux] input").value, "09:06", @@ -76,9 +69,9 @@ QUnit.module("Fields", (hooks) => { "The new value should be displayed properly in the input." ); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "-11:48", "The new value should be saved and displayed properly." ); @@ -108,12 +101,11 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "09:06", "The formatted time value should be displayed properly." ); - await click(target, ".o_form_button_edit"); await editInput(target.querySelector(".o_field_float_time[name=qux] input"), null, "9.5"); assert.strictEqual( target.querySelector(".o_field_float_time[name=qux] input").value, @@ -121,9 +113,9 @@ QUnit.module("Fields", (hooks) => { "The new value should be displayed properly in the input." ); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "09:30", "The new value should be saved and displayed properly." ); @@ -145,7 +137,7 @@ QUnit.module("Fields", (hooks) => { null, "blabla" ); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_notification_title").textContent, "Invalid fields: " diff --git a/addons/web/static/tests/views/fields/float_toggle_field_tests.js b/addons/web/static/tests/views/fields/float_toggle_field_tests.js index 427c0c29b1a..76925189200 100644 --- a/addons/web/static/tests/views/fields/float_toggle_field_tests.js +++ b/addons/web/static/tests/views/fields/float_toggle_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, clickEdit, clickSave, getFixture } from "@web/../tests/helpers/utils"; +import { click, clickSave, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -47,9 +47,6 @@ QUnit.module("Fields", (hooks) => { "0.056", // 0.4444 * 0.125 "The formatted time value should be displayed properly." ); - - await clickEdit(target); - assert.strictEqual( target.querySelector("button.o_field_float_toggle").textContent, "0.056", diff --git a/addons/web/static/tests/views/fields/handle_field_tests.js b/addons/web/static/tests/views/fields/handle_field_tests.js index 4d17d8b8797..b638af8041f 100644 --- a/addons/web/static/tests/views/fields/handle_field_tests.js +++ b/addons/web/static/tests/views/fields/handle_field_tests.js @@ -73,16 +73,13 @@ QUnit.module("Fields", (hooks) => { "handle should not have any content" ); - assert.strictEqual( - getComputedStyle(target.querySelector("td span.o_row_handle")).display, - "none", - "handle should be invisible in readonly mode" + assert.isVisible( + target.querySelector("td span.o_row_handle"), + "handle should be invisible" ); assert.containsN(target, "span.o_row_handle", 2, "should have 2 handles"); - await click(target, ".o_form_button_edit"); - assert.hasClass( target.querySelector("td"), "o_handle_cell", @@ -150,16 +147,7 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual( getComputedStyle(target.querySelector("td span.o_row_handle")).display, "none", - "handle should be invisible in readonly mode" - ); - - await click(target, ".o_form_button_edit"); - - assert.containsN(target, ".o_row_handle", 3, "the handle fields should still be there"); - assert.strictEqual( - getComputedStyle(target.querySelector("td span.o_row_handle")).display, - "none", - "the handle icons should still not be displayed (on readonly fields)" + "handle should be invisible" ); }); }); diff --git a/addons/web/static/tests/views/fields/html_field_tests.js b/addons/web/static/tests/views/fields/html_field_tests.js index eccac230021..2ef1afd4acc 100644 --- a/addons/web/static/tests/views/fields/html_field_tests.js +++ b/addons/web/static/tests/views/fields/html_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, editInput, getFixture } from "@web/../tests/helpers/utils"; +import { editInput, getFixture } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { registry } from "@web/core/registry"; import { HtmlField } from "@web/views/fields/html/html_field"; @@ -34,7 +34,21 @@ QUnit.module("Fields", ({ beforeEach }) => { QUnit.module("HtmlField"); - QUnit.test("html fields are correctly rendered", async (assert) => { + QUnit.test("html fields are correctly rendered (readonly)", async (assert) => { + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: /* xml */ `
`, + }); + + assert.containsOnce(target, "div.kek"); + assert.strictEqual(target.querySelector(".o_field_html .kek").style.color, "red"); + assert.strictEqual(target.querySelector(".o_field_html").textContent, "some text"); + }); + + QUnit.test("html fields are correctly rendered (edit)", async (assert) => { await makeView({ type: "form", resModel: "partner", @@ -43,11 +57,6 @@ QUnit.module("Fields", ({ beforeEach }) => { arch: /* xml */ `
`, }); - assert.containsOnce(target, ".o_field_html", "should have a text area"); - assert.strictEqual(target.querySelector(".o_field_html .kek").style.color, "red"); - assert.strictEqual(target.querySelector(".o_field_html").textContent, "some text"); - - await click(target, ".o_form_button_edit"); const textarea = target.querySelector(".o_field_html textarea"); assert.ok(textarea, "should have a text area"); assert.strictEqual(textarea.value, RED_TEXT); @@ -58,10 +67,5 @@ QUnit.module("Fields", ({ beforeEach }) => { await editInput(textarea, null, BLUE_TEXT); assert.strictEqual(textarea.value, BLUE_TEXT); - - await click(target, ".o_form_button_save"); - - assert.strictEqual(target.querySelector(".o_field_html .kek").style.color, "blue"); - assert.strictEqual(target.querySelector(".o_field_html").textContent, "hello world"); }); }); diff --git a/addons/web/static/tests/views/fields/image_field_tests.js b/addons/web/static/tests/views/fields/image_field_tests.js index 510bf92807e..ce36b089db6 100644 --- a/addons/web/static/tests/views/fields/image_field_tests.js +++ b/addons/web/static/tests/views/fields/image_field_tests.js @@ -5,7 +5,6 @@ import { getFixture, nextTick, triggerEvent, - clickEdit, clickSave, editInput, } from "@web/../tests/helpers/utils"; @@ -131,8 +130,6 @@ QUnit.module("Fields", (hooks) => { "the image should correctly set its attributes" ); - await click(target, ".o_form_button_edit"); - assert.containsOnce( target, ".o_field_image .o_select_file_button", @@ -209,8 +206,6 @@ QUnit.module("Fields", (hooks) => { "the image should correctly set its attributes" ); - await click(target, ".o_form_button_edit"); - assert.containsOnce( target, ".o_field_image .o_select_file_button", @@ -241,7 +236,6 @@ QUnit.module("Fields", (hooks) => { "data:image/png;base64,coucou==", "the image should have the initial src" ); - await click(target, ".o_form_button_edit"); // Whitebox: replace the event target before the event is handled by the field so that we can modify // the files that it will take into account. This relies on the fact that it reads the files from // event.target and not from a direct reference to the input element. @@ -281,7 +275,6 @@ QUnit.module("Fields", (hooks) => { `, }); // The view must be in edit mode - await click(target.querySelector(".o_form_button_edit")); assert.strictEqual( target.querySelector("input.o_input_file").getAttribute("accept"), ".png,.jpeg", @@ -320,7 +313,7 @@ QUnit.module("Fields", (hooks) => { ); }); - QUnit.test("ImageField: zoom and zoom_delay options", async function (assert) { + QUnit.test("ImageField: zoom and zoom_delay options (readonly)", async (assert) => { serverData.models.partner.records[0].document = MY_IMAGE; await makeView({ @@ -330,7 +323,7 @@ QUnit.module("Fields", (hooks) => { serverData, arch: `
- + `, }); // data-tooltip attribute is used by the tooltip service @@ -344,8 +337,22 @@ QUnit.module("Fields", (hooks) => { "600", "tooltip has the right delay" ); + }); + + QUnit.test("ImageField: zoom and zoom_delay options (edit)", async function (assert) { + serverData.models.partner.records[0].document = MY_IMAGE; + + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: ` +
+ + `, + }); - await click(target.querySelector(".o_form_button_edit")); assert.ok( !target.querySelector(".o_field_image img").dataset["tooltipInfo"], "the tooltip is not present in edition" @@ -353,7 +360,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test( - "ImageField displays the right images with zoom and preview_image options", + "ImageField displays the right images with zoom and preview_image options (readonly)", async function (assert) { serverData.models.partner.records[0].document = "3 kb"; serverData.models.partner.records[0].__last_update = "2022-08-05 08:37:00"; @@ -365,7 +372,7 @@ QUnit.module("Fields", (hooks) => { serverData, arch: `
- + `, }); @@ -479,7 +486,7 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsOnce( target.querySelector(".o_form_view"), @@ -535,7 +542,6 @@ QUnit.module("Fields", (hooks) => { assert.verifySteps(["get_views", "read"]); assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); - await clickEdit(target); assert.verifySteps([]); // same unique as before assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); @@ -547,8 +553,8 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); assert.verifySteps(["write", "read"]); - // different unique: the record has been written - assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659692220000"); + + assert.strictEqual(getUnique(target.querySelector(".o_field_image img")), "1659688620000"); }); QUnit.test("unique in url change on record change", async (assert) => { diff --git a/addons/web/static/tests/views/fields/image_url_field_tests.js b/addons/web/static/tests/views/fields/image_url_field_tests.js index 6545ec6f7fe..274562f0b5b 100644 --- a/addons/web/static/tests/views/fields/image_url_field_tests.js +++ b/addons/web/static/tests/views/fields/image_url_field_tests.js @@ -228,7 +228,7 @@ QUnit.module("Fields", (hooks) => { }); assert.hasClass( - target.querySelector('div[name="foo"].o_field_empty'), + target.querySelector('div[name="foo"]'), "o_field_image_url", "the widget should have the correct class" ); diff --git a/addons/web/static/tests/views/fields/integer_field_tests.js b/addons/web/static/tests/views/fields/integer_field_tests.js index c6001131d7a..7b8ef00acb4 100644 --- a/addons/web/static/tests/views/fields/integer_field_tests.js +++ b/addons/web/static/tests/views/fields/integer_field_tests.js @@ -4,6 +4,7 @@ import { localization } from "@web/core/l10n/localization"; import { defaultLocalization } from "@web/../tests/helpers/mock_services"; import { click, + clickSave, editInput, getFixture, patchWithCleanup, @@ -56,7 +57,7 @@ QUnit.module("Fields", (hooks) => { ); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "0", "Non-set integer field should be recognized as 0." ); @@ -71,8 +72,6 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_field_widget[name=int_field] input").value, "10", @@ -87,10 +86,9 @@ QUnit.module("Fields", (hooks) => { "The value should be correctly displayed in the input." ); - await click(target, ".o_form_button_save"); - + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "30", "The new value should be saved and displayed properly." ); @@ -105,12 +103,11 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); - await click(target, ".o_form_button_edit"); await editInput(target, ".o_field_widget[name=int_field] input", "=100/3"); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, + target.querySelector(".o_field_widget input").value, "33", "The new value should be calculated properly." ); @@ -127,8 +124,6 @@ QUnit.module("Fields", (hooks) => { arch: `
`, }); - await click(target, ".o_form_button_edit"); - assert.ok( target.querySelector(".o_field_widget input").hasAttribute("type"), "Integer field with option type must have a type attribute." @@ -142,22 +137,13 @@ QUnit.module("Fields", (hooks) => { ); await editInput(target, ".o_field_widget[name=int_field] input", "1234567890"); - await click(target, ".o_form_button_save"); - await click(target, ".o_form_button_edit"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "1234567890", "Integer value must be not formatted if input type is number." ); - - await click(target, ".o_form_button_save"); - - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "1,234,567,890", - "Integer value must be formatted in readonly view even if the input type is number." - ); }); QUnit.test("with 'step' option", async function (assert) { @@ -169,13 +155,10 @@ QUnit.module("Fields", (hooks) => { arch: `
`, }); - await click(target, ".o_form_button_edit"); - assert.ok( target.querySelector(".o_field_widget input").hasAttribute("step"), "Integer field with option type must have a step attribute." ); - assert.hasAttrValue( target.querySelector(".o_field_widget input"), "step", @@ -195,8 +178,6 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); - await click(target, ".o_form_button_edit"); - assert.hasAttrValue( target.querySelector(".o_field_widget input"), "type", @@ -205,8 +186,7 @@ QUnit.module("Fields", (hooks) => { ); await editInput(target, ".o_field_widget[name=int_field] input", "1234567890"); - await click(target, ".o_form_button_save"); - await click(target, ".o_form_button_edit"); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, @@ -224,14 +204,6 @@ QUnit.module("Fields", (hooks) => { arch: `
`, }); - assert.strictEqual( - target.querySelector(".o_field_widget[name=int_field]").textContent, - "8069", - "Integer value must not be formatted" - ); - - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_field_widget input").value, "8069", @@ -250,14 +222,6 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); - assert.strictEqual( - target.querySelector(".o_field_widget[name=int_field]").textContent, - "8,069", - "Integer value must be formatted by default" - ); - - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_field_widget input").value, "8,069", diff --git a/addons/web/static/tests/views/fields/label_selection_field_tests.js b/addons/web/static/tests/views/fields/label_selection_field_tests.js index 73ef19c83d0..8b686dd979a 100644 --- a/addons/web/static/tests/views/fields/label_selection_field_tests.js +++ b/addons/web/static/tests/views/fields/label_selection_field_tests.js @@ -91,52 +91,6 @@ QUnit.module("Fields", (hooks) => { "Blocked", "the label should say 'Blocked' since this is the label value for that state" ); - - // // switch to edit mode and check the result - await click(target.querySelector(".o_form_button_edit")); - assert.containsOnce( - target, - ".o_field_widget .badge.text-bg-warning", - "should have a warning status label since selection is the second, blocked state" - ); - assert.containsNone( - target, - ".o_field_widget .badge.text-bg-secondary", - "should not have a default status since selection is the second, blocked state" - ); - assert.containsNone( - target, - ".o_field_widget .badge.text-bg-success", - "should not have a success status since selection is the second, blocked state" - ); - assert.strictEqual( - target.querySelector(".o_field_widget .badge.text-bg-warning").textContent, - "Blocked", - "the label should say 'Blocked' since this is the label value for that state" - ); - - // save - await click(target.querySelector(".o_form_button_save")); - assert.containsOnce( - target, - ".o_field_widget .badge.text-bg-warning", - "should have a warning status label since selection is the second, blocked state" - ); - assert.containsNone( - target, - ".o_field_widget .badge.text-bg-secondary", - "should not have a default status since selection is the second, blocked state" - ); - assert.containsNone( - target, - ".o_field_widget .badge.text-bg-success", - "should not have a success status since selection is the second, blocked state" - ); - assert.strictEqual( - target.querySelector(".o_field_widget .badge.text-bg-warning").textContent, - "Blocked", - "the label should say 'Blocked' since this is the label value for that state" - ); }); QUnit.test("LabelSelectionField in editable list view", async function (assert) { diff --git a/addons/web/static/tests/views/fields/many2many_binary_field_tests.js b/addons/web/static/tests/views/fields/many2many_binary_field_tests.js index 1737efc4c98..796327e478b 100644 --- a/addons/web/static/tests/views/fields/many2many_binary_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_binary_field_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { click, getFixture, nextTick } from "@web/../tests/helpers/utils"; +import { click, clickSave, getFixture, nextTick } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { registry } from "@web/core/registry"; @@ -111,19 +111,17 @@ QUnit.module("Fields", (hooks) => { "div.o_field_widget .oe_fileupload .o_attachments", "there should be one attachment" ); - assert.containsNone( + assert.containsOnce( target, "div.o_field_widget .oe_fileupload .o_attach", - "there should not be an Add button (readonly)" + "there should be an Add button (edit)" ); - assert.containsNone( + assert.containsOnce( target, "div.o_field_widget .oe_fileupload .o_attachment .o_attachment_delete", - "there should not be a Delete button (readonly)" + "there should be a Delete button (edit)" ); - // to edit mode - await click(target, ".o_form_button_edit"); assert.containsOnce( target, "div.o_field_widget .oe_fileupload .o_attach", @@ -176,7 +174,7 @@ QUnit.module("Fields", (hooks) => { ) ); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsOnce( target, "div.o_field_widget .oe_fileupload .o_attachments", @@ -251,20 +249,17 @@ QUnit.module("Fields", (hooks) => { "div.o_field_widget .oe_fileupload .o_attachments", "there should be one attachment" ); - assert.containsNone( + assert.containsOnce( target, "div.o_field_widget .oe_fileupload .o_attach", - "there should not be an Add button (readonly)" + "there should be an Add button (edit)" ); - assert.containsNone( + assert.containsOnce( target, "div.o_field_widget .oe_fileupload .o_attachment .o_attachment_delete", - "there should not be a Delete button (readonly)" + "there should be a Delete button (edit)" ); - // to edit mode - await click(target, ".o_form_button_edit"); - // Set and trigger the import of 2 files in the input const fileInput = target.querySelector('input[type="file"]'); const dataTransfer = new DataTransfer(); diff --git a/addons/web/static/tests/views/fields/many2many_checkboxes_field_tests.js b/addons/web/static/tests/views/fields/many2many_checkboxes_field_tests.js index 5059e041bb5..9acd753e8ea 100644 --- a/addons/web/static/tests/views/fields/many2many_checkboxes_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_checkboxes_field_tests.js @@ -67,22 +67,17 @@ QUnit.module("Fields", (hooks) => { assert.ok(checkboxes[0].checked); assert.notOk(checkboxes[1].checked); - assert.containsN(target, "div.o_field_widget div.form-check input:disabled", 2); - - await click(target, ".o_form_button_edit"); - assert.containsNone(target, "div.o_field_widget div.form-check input:disabled"); // add a m2m value by clicking on input checkboxes = target.querySelectorAll("div.o_field_widget div.form-check input"); await click(checkboxes[1]); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.containsN(target, "div.o_field_widget div.form-check input:checked", 2); // remove a m2m value by clinking on label - await click(target, ".o_form_button_edit"); await click(target.querySelector("div.o_field_widget div.form-check > label")); - await click(target, ".o_form_button_save"); + await clickSave(target); checkboxes = target.querySelectorAll("div.o_field_widget div.form-check input"); assert.notOk(checkboxes[0].checked); assert.ok(checkboxes[1].checked); @@ -111,25 +106,6 @@ QUnit.module("Fields", (hooks) => { 2, "should have fetched and displayed the 2 values of the many2many" ); - - assert.ok( - target.querySelector("div.o_field_widget div.form-check input").checked, - "first checkbox should be checked" - ); - assert.notOk( - target.querySelectorAll("div.o_field_widget div.form-check input")[1].checked, - "second checkbox should not be checked" - ); - - assert.containsN( - target, - "div.o_field_widget div.form-check input:disabled", - 2, - "the checkboxes should be disabled" - ); - - await click(target, ".o_form_button_edit"); - assert.containsN( target, "div.o_field_widget div.form-check input:disabled", @@ -166,11 +142,9 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); - await click(target.querySelectorAll("div.o_field_widget div.form-check input")[0]); await click(target.querySelectorAll("div.o_field_widget div.form-check input")[1]); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.notOk( target.querySelectorAll("div.o_field_widget div.form-check input")[0].checked, "first checkbox should not be checked" @@ -197,8 +171,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); - assert.strictEqual( target.querySelector(".o_field_widget[name='int_field'] input").value, "10" @@ -255,8 +227,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - assert.containsN( target, ".o_field_widget[name='timmy'] input[type='checkbox']:checked", @@ -269,7 +239,7 @@ QUnit.module("Fields", (hooks) => { ); await click(checkboxes[checkboxes.length - 1]); - await click(target, ".o_form_button_save"); + await clickSave(target); checkboxes = target.querySelectorAll( ".o_field_widget[name='timmy'] input[type='checkbox']" ); @@ -316,8 +286,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - assert.containsN( target, ".o_field_widget[name='timmy'] input[type='checkbox']", @@ -331,7 +299,7 @@ QUnit.module("Fields", (hooks) => { // toggle the first value await click(target.querySelector(".o_field_widget[name='timmy'] input[type='checkbox']")); - await click(target, ".o_form_button_save"); + await clickSave(target); assert.notOk( target.querySelector(".o_field_widget[name='timmy'] input[type='checkbox']").checked ); @@ -367,7 +335,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target, ".o_form_button_edit"); await click(target.querySelector(".o_data_cell")); // edit the timmy field by (un)checking boxes on the widget @@ -379,7 +346,7 @@ QUnit.module("Fields", (hooks) => { assert.notOk(secondCheckbox.checked, "the checkbox should be unticked"); await click(target.querySelector(".modal .o_form_button_save")); - await click(target.querySelector(".o_form_button_save")); + await clickSave(target); }); QUnit.test("Many2ManyCheckBoxesField with default values", async function (assert) { diff --git a/addons/web/static/tests/views/fields/many2many_field_tests.js b/addons/web/static/tests/views/fields/many2many_field_tests.js index 6e1de164f77..3f4bc49f5c8 100644 --- a/addons/web/static/tests/views/fields/many2many_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_field_tests.js @@ -4,7 +4,6 @@ import { browser } from "@web/core/browser/browser"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { click, - clickEdit, clickSave, clickOpenedDropdownItem, editInput, @@ -223,7 +222,7 @@ QUnit.module("Fields", (hooks) => { QUnit.module("Many2ManyField"); QUnit.test("many2many kanban: edition", async function (assert) { - assert.expect(33); + assert.expect(31); serverData.views = { "partner_type,false,form": '
', @@ -296,17 +295,6 @@ QUnit.module("Fields", (hooks) => { }, }); - assert.ok( - !$(target).find(".o_kanban_renderer .delete_icon").length, - "delete icon should not be visible in readonly" - ); - assert.ok( - !$(target).find(".o_field_many2many .o-kanban-button-new").length, - '"Add" button should not be visible in readonly' - ); - - await clickEdit(target); - assert.strictEqual( $(target).find(".o_kanban_record:not(.o_kanban_ghost)").length, 2, @@ -467,7 +455,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.strictEqual( target .querySelector(".o_field_many2many[name=timmy] .o-kanban-button-new") @@ -549,13 +536,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - assert.ok( - !$(target).find(".o-kanban-button-new").length, - '"Add" button should not be available in readonly' - ); - - await clickEdit(target); - assert.ok( $(target).find(".o-kanban-button-new").length, '"Add" button should be available in edit' @@ -603,7 +583,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); // color is red assert.containsOnce(target, ".o-kanban-button-new", '"Add" button should be available'); @@ -653,7 +632,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("many2many list (non editable): edition", async function (assert) { - assert.expect(34); + assert.expect(32); serverData.models.partner.records[0].timmy = [12, 14]; serverData.models.partner_type.records.push({ id: 15, display_name: "bronze", color: 6 }); @@ -693,18 +672,6 @@ QUnit.module("Fields", (hooks) => { "read", // main record "read", // relational field ]); - assert.containsNone( - target, - ".o_list_record_remove", - "delete icon should not be visible in readonly" - ); - assert.containsNone( - target, - ".o_field_x2many_list_row_add", - '"Add an item" should not be visible in readonly' - ); - - await clickEdit(target); assert.containsN( target, @@ -842,7 +809,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("many2many list (editable): edition", async function (assert) { - assert.expect(31); + assert.expect(29); serverData.models.partner.records[0].timmy = [12, 14]; serverData.models.partner_type.records.push({ id: 15, display_name: "bronze", color: 6 }); @@ -879,11 +846,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - assert.containsNone(target, ".o_list_record_remove"); - assert.containsNone(target, ".o_field_x2many_list_row_add"); - - await clickEdit(target); - assert.containsN( target, ".o_list_renderer td.o_list_number", @@ -1020,8 +982,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - assert.containsOnce( target, ".o_field_x2many_list_row_add", @@ -1048,8 +1008,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - assert.containsOnce( target, ".o_field_x2many_list_row_add", @@ -1079,8 +1037,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - assert.containsNone(target, ".o_field_x2many_list_row_add"); - await clickEdit(target); assert.containsOnce(target, ".o_field_x2many_list_row_add"); }); @@ -1138,7 +1094,6 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_field_many2many .o_field_x2many_list_row_add"); await clickSave(target); - await clickEdit(target); assert.containsOnce(target, ".o_field_many2many .o_data_row .o_list_record_remove"); await click(target.querySelector(".o_field_many2many .o_data_row .o_list_record_remove")); @@ -1169,8 +1124,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - // color is red -> create and delete actions are available assert.containsOnce( target, @@ -1239,8 +1192,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - // color is red -> link and unlink actions are available assert.containsOnce( target, @@ -1300,8 +1251,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - // color is red -> link and unlink actions are available assert.containsOnce( target, @@ -1364,7 +1313,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); // color is red -> link and unlink actions are available assert.containsOnce(target, ".o-kanban-button-new", "should have the 'Add' button"); @@ -1416,7 +1364,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); // color is red -> link and unlink actions are available assert.containsOnce(target, ".o-kanban-button-new", "should have the 'Add' button"); @@ -1502,8 +1449,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - await click(target.querySelector(".o_field_x2many_list_row_add a")); await click($(target).find(".modal .o_data_row:first .o_data_cell")[0]); @@ -1569,7 +1514,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); await click(target.querySelector(".o_field_x2many_list_row_add a")); assert.strictEqual($(".modal .o_data_row").length, 1, "should contain only one row (gold)"); @@ -1608,7 +1552,6 @@ QUnit.module("Fields", (hooks) => { }); assert.verifySteps(["get_views", "read", "read"]); - await clickEdit(target); await click($(target).find("td.o_data_cell:first")[0]); assert.verifySteps(["get_views", "read"]); @@ -1671,8 +1614,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - assert.verifySteps(["get_views", "read"]); await editInput(target, ".o_field_widget[name=foo] input", "trigger onchange"); @@ -1823,7 +1764,6 @@ QUnit.module("Fields", (hooks) => { }); // First round: add 51 records in batch - await clickEdit(target); await click(target.querySelector(".o_field_x2many_list_row_add a")); var $modal = $(".modal-lg"); @@ -1844,7 +1784,6 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); // Secound round: remove one record - await clickEdit(target); var trash_buttons = $(target).find( ".o_field_many2many.o_field_widget .o_field_x2many.o_field_x2many_list .o_list_record_remove" ); @@ -1942,7 +1881,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); await click(target, ".o_field_many2many_selection input"); checkGetViews = true; await clickOpenedDropdownItem(target, "timmy", "Search More..."); @@ -2000,6 +1938,7 @@ QUnit.module("Fields", (hooks) => { await click(target.querySelector(".o_data_cell")); await editInput(target, ".o_field_many2many_selection input", "indianapolis"); + await nextTick(); await clickOpenedDropdownItem(target, "timmy", "Create and edit..."); assert.containsOnce(target, ".modal .o_field_many2one"); assert.strictEqual( @@ -2055,8 +1994,8 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await editInput(target, ".o_field_many2many_selection input", "indianapolis"); + await nextTick(); await clickOpenedDropdownItem(target, "timmy", "Create and edit..."); assert.containsOnce(target, ".modal .o_field_many2one"); assert.strictEqual( @@ -2120,7 +2059,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target, ".o_data_cell"); await editInput(target, ".o_field_many2many_selection input", "indianapolis"); await clickOpenedDropdownItem(target, "timmy", "Create and edit..."); diff --git a/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js b/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js index 00abfbaf44e..71ddfc2d1c2 100644 --- a/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js @@ -1,12 +1,6 @@ /** @odoo-module **/ -import { - click, - clickEdit, - clickSave, - getFixture, - selectDropdownItem, -} from "@web/../tests/helpers/utils"; +import { click, clickSave, getFixture, selectDropdownItem } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -206,12 +200,12 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual( tag.dataset["tooltipTemplate"], "web.TagsList.Tooltip", - "uses the proper tooltip template", + "uses the proper tooltip template" ); const tooltipInfo = JSON.parse(tag.dataset["tooltipInfo"]); assert.strictEqual( - tooltipInfo.tags.map(tag => tag.text).join(" "), - 'record 6 record 7', + tooltipInfo.tags.map((tag) => tag.text).join(" "), + "record 6 record 7", "shows a tooltip on hover" ); @@ -363,12 +357,12 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual( tag.dataset["tooltipTemplate"], "web.TagsList.Tooltip", - "uses the proper tooltip template", + "uses the proper tooltip template" ); const tooltipInfo = JSON.parse(tag.dataset["tooltipInfo"]); assert.strictEqual( - tooltipInfo.tags.map(tag => tag.text).join(" "), - 'aaa record 5', + tooltipInfo.tags.map((tag) => tag.text).join(" "), + "aaa record 5", "shows a tooltip on hover" ); @@ -391,7 +385,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await clickEdit(target); assert.containsN( target, ".o_field_many2many_tags_avatar.o_field_widget .badge", diff --git a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js index 7cccd6f41f5..32ace21498f 100644 --- a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js @@ -6,7 +6,6 @@ import { Many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_t import { click, clickDropdown, - clickEdit, clickOpenedDropdownItem, clickSave, editInput, @@ -162,7 +161,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("Many2ManyTagsField with color: rendering and edition", async function (assert) { - assert.expect(28); + assert.expect(26); serverData.models.partner.records[0].timmy = [12, 14]; serverData.models.partner_type.records.push({ id: 13, display_name: "red", color: 8 }); @@ -211,19 +210,6 @@ QUnit.module("Fields", (hooks) => { "o_tag_color_2", "should have correctly set the color" ); - - await clickEdit(target); - - assert.containsN( - target, - ".o_field_many2many_tags .badge", - 2, - "should still contain 2 tags in edit mode" - ); - assert.ok( - target.querySelector(".o_tag_color_2 .o_tag_badge_text").textContent === "gold", - 'first tag should still contain "gold" and be color 2 in edit mode' - ); assert.containsN( target, ".o_field_many2many_tags .o_delete", @@ -274,7 +260,7 @@ QUnit.module("Fields", (hooks) => { `should not contain tag 'silver' anymore but found: ${textContent}` ); // save the record (should do the write RPC with the correct commands) - await click(target.querySelector(".o_form_button_save")); + await clickSave(target); // checkbox 'Hide in Kanban' const badgeElement = target.querySelectorAll(".o_field_many2many_tags .badge")[1]; // selects 'red' tag @@ -426,8 +412,6 @@ QUnit.module("Fields", (hooks) => { "should have fetched and rendered gold partner tag" ); - await clickEdit(target); - await clickDropdown(target, "timmy"); const autocompleteDropdown = target.querySelector(".o-autocomplete--dropdown-menu"); @@ -540,7 +524,6 @@ QUnit.module("Fields", (hooks) => { ); // Update the color in edit => write on save with rest of the record - await clickEdit(target); await click(badgeNode); await click(target, '.o_colorlist button[data-color="6"]'); await nextTick(); @@ -727,7 +710,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.containsOnce(target, ".o_field_many2many_tags .badge", "should contain one tag"); // update foo, which will trigger an onchange and update timmy @@ -840,8 +822,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - assert.strictEqual( target.querySelectorAll(".o_field_many2many_tags .badge").length, 1, @@ -913,7 +893,7 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_field_many2many_tags .badge"); - await click(target.querySelector(".o_form_button_save")); + await clickSave(target); assert.strictEqual( target.querySelector(".o_field_many2many_tags").textContent.trim(), @@ -986,7 +966,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.deepEqual( getNodesTextContent(target.querySelectorAll(".o_data_cell")), ["second recordaaa"], @@ -1027,7 +1006,6 @@ QUnit.module("Fields", (hooks) => { }); assert.verifySteps(["world"]); - await clickEdit(target); await selectDropdownItem(target, "timmy", "silver"); assert.verifySteps(["world"]); }); @@ -1056,7 +1034,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await clickEdit(target); await selectDropdownItem(target, "timmy", "Search More..."); assert.ok(target.querySelector(".o_dialog"), "should have open the modal"); @@ -1113,7 +1090,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await clickEdit(target); await selectDropdownItem(target, "timmy", "Search More..."); // -1 for the one that is already on the form & +1 for the select all, @@ -1172,7 +1148,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); await editInput(target, `div[name="timmy"] input`, "Ralts"); await nameSearchProm; @@ -1244,8 +1219,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - await editInput(target, ".o_field_widget input", "hello"); await nameSearchProm; await nextTick(); @@ -1301,8 +1274,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - // turtle_bar is true -> create and delete actions are available assert.containsOnce( target, diff --git a/addons/web/static/tests/views/fields/many2one_avatar_field_tests.js b/addons/web/static/tests/views/fields/many2one_avatar_field_tests.js index 8d857f7c2fa..514e7490bf1 100644 --- a/addons/web/static/tests/views/fields/many2one_avatar_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_avatar_field_tests.js @@ -3,7 +3,6 @@ import { browser } from "@web/core/browser/browser"; import { click, - clickEdit, clickSave, editInput, getFixture, @@ -78,7 +77,7 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual( - target.querySelector(".o_field_widget[name=user_id]").textContent.trim(), + target.querySelector(".o_field_widget[name=user_id] input").value, "Aline" ); assert.containsOnce( @@ -86,8 +85,6 @@ QUnit.module("Fields", (hooks) => { '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]' ); - await clickEdit(target); - assert.containsOnce(target, ".o_input_dropdown"); assert.strictEqual(target.querySelector(".o_input_dropdown input").value, "Aline"); assert.containsOnce(target, ".o_external_button"); @@ -105,7 +102,7 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget[name=user_id]").textContent.trim(), + target.querySelector(".o_field_widget[name=user_id] input").value, "Christine" ); assert.containsOnce( @@ -113,7 +110,6 @@ QUnit.module("Fields", (hooks) => { '.o_m2o_avatar > img[data-src="/web/image/user/19/avatar_128"]' ); - await clickEdit(target); await editInput(target, '.o_field_widget[name="user_id"] input', ""); assert.containsNone(target, ".o_m2o_avatar > img"); @@ -121,7 +117,7 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); assert.containsNone(target, ".o_m2o_avatar > img"); - assert.containsNone(target, ".o_m2o_avatar > .o_m2o_avatar_empty"); + assert.containsOnce(target, ".o_m2o_avatar > .o_m2o_avatar_empty"); }); QUnit.test("onchange in form view flow", async function (assert) { diff --git a/addons/web/static/tests/views/fields/many2one_field_tests.js b/addons/web/static/tests/views/fields/many2one_field_tests.js index 01f68ad3fbf..13b1bd742d7 100644 --- a/addons/web/static/tests/views/fields/many2one_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_field_tests.js @@ -10,7 +10,6 @@ import { click, clickDiscard, clickDropdown, - clickEdit, clickOpenedDropdownItem, clickSave, dragAndDrop, @@ -214,7 +213,7 @@ QUnit.module("Fields", (hooks) => { QUnit.module("Many2oneField"); QUnit.test("many2ones in form views", async function (assert) { - assert.expect(5); + assert.expect(2); function createMockActionService(assert) { return { @@ -253,7 +252,7 @@ QUnit.module("Fields", (hooks) => {
- +
`, @@ -278,11 +277,6 @@ QUnit.module("Fields", (hooks) => { }, }); - assert.containsOnce(target, "a.o_form_uri:contains(aaa)", "should contain a link"); - await click(target, "a.o_form_uri"); - - await click(target, ".o_form_button_edit"); - await click(target, ".o_external_button"); assert.strictEqual( target.querySelector(".modal .modal-title").textContent.trim(), @@ -313,7 +307,7 @@ QUnit.module("Fields", (hooks) => { arch: `
- +
`, mockRPC(route, { args, method }) { @@ -324,8 +318,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - // click on the external button (should do an RPC) await click(target, ".o_external_button"); // save and close modal @@ -390,7 +382,7 @@ QUnit.module("Fields", (hooks) => { arch: `
- +
`, mockRPC(route, { method }) { @@ -400,8 +392,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - // click on the external button (should do an RPC) await click(target, ".o_external_button"); @@ -412,7 +402,7 @@ QUnit.module("Fields", (hooks) => { // save and close modal await clickSave(target.querySelector(".modal")); // save form - await click(target, ".o_form_button_save"); + await clickSave(target); // click next on pager await click(target, ".o_pager .o_pager_next"); @@ -447,13 +437,6 @@ QUnit.module("Fields", (hooks) => { }, }); - assert.strictEqual( - target.querySelector("a.o_form_uri").innerHTML, - "aaa
Street
City ZIP", - "input should have a multi-line content in readonly due to show_address" - ); - - await click(target, ".o_form_button_edit"); assert.strictEqual(target.querySelector("input.o_input").value, "aaa"); assert.strictEqual( target.querySelector(".o_field_many2one_extra").innerHTML, @@ -493,7 +476,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); const input = target.querySelector(".o_field_widget input"); assert.strictEqual(input.value, "aaa"); @@ -543,7 +525,7 @@ QUnit.module("Fields", (hooks) => { serverData, resId: 1, arch: ` -
+ `, @@ -587,7 +569,7 @@ QUnit.module("Fields", (hooks) => { serverData, resId: 1, arch: ` -
+ `, @@ -644,8 +626,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); - await selectDropdownItem(target, "trululu", "Search More..."); assert.strictEqual($("tr.o_data_row").length, 9, "should display 9 records"); @@ -754,7 +734,7 @@ QUnit.module("Fields", (hooks) => { QUnit.test( "onchanges on many2ones trigger when editing record in form view", async function (assert) { - assert.expect(11); + assert.expect(10); serverData.models.partner.onchanges.user_id = function () {}; serverData.models.user.fields.other_field = { string: "Other Field", type: "char" }; @@ -774,7 +754,7 @@ QUnit.module("Fields", (hooks) => {
- +
`, @@ -794,7 +774,6 @@ QUnit.module("Fields", (hooks) => { }); // open the many2one in form view and change something - await click(target, ".o_form_button_edit"); await click(target, ".o_external_button"); await editInput( target, @@ -814,11 +793,6 @@ QUnit.module("Fields", (hooks) => { "read", "onchange", ]); - - // save the main record, and check that no extra rpcs are done (record - // is not dirty, only a related record was modified) - await click(target, ".o_form_button_save"); - assert.verifySteps([]); } ); @@ -938,14 +912,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.containsOnce( - target, - ".o_field_widget[name='trululu'] > span", - "should be displayed inside a span (sanity check)" - ); - assert.containsNone(target, "span.o_form_uri", "should not have an anchor"); - - await click(target, ".o_form_button_edit"); assert.containsNone( target, ".o_field_widget[name='trululu'] .o_external_button", @@ -1086,14 +1052,12 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - // trigger the onchange await editInput(target, ".o_field_widget[name='foo'] input", "3"); assert.verifySteps(["onchange"]); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.verifySteps(["write"]); } ); @@ -1142,7 +1106,6 @@ QUnit.module("Fields", (hooks) => { // event to specify a fake session to prevent it from crashing patchWithCleanup(session.user_context, {}); - await click(target, ".o_form_button_edit"); await clickDropdown(target, "trululu"); let dropdown = target.querySelector(".o_field_many2one[name='trululu'] .dropdown-menu"); @@ -1229,22 +1192,22 @@ QUnit.module("Fields", (hooks) => { ); // save - await click(target, ".o_form_button_save"); + await clickSave(target); assert.strictEqual( - target.querySelector("a.o_form_uri").textContent, + target.querySelector(".o_field_many2one input").value, "Partner 20", "should display correct value after save" ); }); - QUnit.test("many2one in non edit mode", async function (assert) { + QUnit.test("many2one in non edit mode (with value)", async function (assert) { await makeView({ type: "form", resModel: "partner", resId: 1, serverData, arch: ` -
+ `, }); @@ -1256,12 +1219,23 @@ QUnit.module("Fields", (hooks) => { "#id=4&model=partner", "href should contain id and model" ); + }); + + QUnit.test("many2one in non edit mode (without value)", async function (assert) { + serverData.models.partner.records[0].trululu = false; + + await makeView({ + type: "form", + resModel: "partner", + resId: 1, + serverData, + arch: ` +
+ + `, + }); // Remove value from many2one and then save, there should be no link anymore - await click(target, ".o_form_button_edit"); - await editInput(target, ".o_field_many2one input", ""); - - await click(target, ".o_form_button_save"); assert.containsNone(target, "a.o_form_uri"); }); @@ -1320,7 +1294,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_field_many2one input").value, @@ -1404,7 +1377,34 @@ QUnit.module("Fields", (hooks) => { assert.verifySteps(["search: ", "search: first", "search: first", "search: first"]); }); - QUnit.test("many2one field with option always_reload", async function (assert) { + QUnit.test("many2one field with option always_reload (readonly)", async function (assert) { + let count = 0; + await makeView({ + type: "form", + resModel: "partner", + resId: 2, + serverData, + arch: ` +
+ + `, + mockRPC(route, { method }) { + if (method === "name_get") { + count++; + return Promise.resolve([[1, "first record\nand some address"]]); + } + }, + }); + + assert.strictEqual(count, 1, "an extra name_get should have been done"); + assert.ok( + target.querySelector("a.o_form_uri").textContent.includes("and some address"), + "should display additional result" + ); + assert.containsNone(target, ".o_field_many2one_extra"); + }); + + QUnit.test("many2one field with option always_reload (edit)", async function (assert) { let count = 0; await makeView({ type: "form", @@ -1424,22 +1424,15 @@ QUnit.module("Fields", (hooks) => { }); assert.strictEqual(count, 1, "an extra name_get should have been done"); - assert.ok( - target.querySelector("a.o_form_uri").textContent.includes("and some address"), - "should display additional result" - ); - - await click(target, ".o_form_button_edit"); assert.strictEqual( target.querySelector(".o_field_widget[name='trululu'] input").value, "first record", "actual field value should be displayed to be edited" ); - - await click(target, ".o_form_button_save"); - assert.ok( - target.querySelector("a.o_form_uri").textContent.includes("and some address"), - "should still display additional result" + assert.containsOnce(target, ".o_field_many2one_extra"); + assert.strictEqual( + target.querySelector(".o_field_many2one_extra").textContent, + "and some address" ); }); @@ -2469,7 +2462,7 @@ QUnit.module("Fields", (hooks) => {
- +
`, @@ -2480,7 +2473,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target.querySelector("td.o_data_cell")); await click(target, ".o_external_button"); assert.containsOnce(target, ".modal"); @@ -2568,7 +2560,7 @@ QUnit.module("Fields", (hooks) => { - + `, @@ -2593,7 +2585,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); await click(target, ".o_data_cell"); await click(target, ".o_external_button"); @@ -2634,8 +2625,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await click(target, ".o_form_button_edit"); - click(target, ".o_field_widget[name=product_id] input"); }); @@ -2661,8 +2650,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await click(target, ".o_form_button_edit"); - click(target, ".o_field_widget[name='trululu'] input"); } ); @@ -2738,11 +2725,10 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); assert.strictEqual( - target.querySelector(".o_field_widget[name='product_id']").textContent, + target.querySelector(".o_field_widget[name='product_id'] input").value, "", "the tag a should be empty" ); - await click(target, ".o_form_button_edit"); await click(target, ".o_field_widget[name='product_id'] input"); await click(target.querySelector(".o_field_widget[name='product_id'] .dropdown-item")); @@ -2752,9 +2738,9 @@ QUnit.module("Fields", (hooks) => { "should have selected xphone" ); - await click(target, ".o_form_button_cancel"); + await clickDiscard(target); assert.strictEqual( - target.querySelector(".o_field_widget[name='product_id']").textContent, + target.querySelector(".o_field_widget[name='product_id'] input").value, "", "the tag a should be empty" ); @@ -2812,7 +2798,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); click(target, ".o_field_widget[name='product_id'] input"); click(target, ".o_field_widget[name='trululu'] input"); } @@ -3068,7 +3053,7 @@ QUnit.module("Fields", (hooks) => { arch: `
- +
`, mockRPC(route) { @@ -3150,7 +3135,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); await click(target.querySelector(".o_field_widget[name=product_id] input")); assert.containsNone(target, ".o-autocomplete a:contains(Start typing...)"); @@ -3321,8 +3305,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - const input = target.querySelector(".o_field_many2one input"); await editInput(input, null, "first"); @@ -3395,8 +3377,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); - const input = target.querySelector(".o_field_many2one input"); assert.ok(input.value, "many2one should have value"); @@ -3580,8 +3560,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - // trigger a name_search (domain should be []) await click(target, ".o_field_widget[name=trululu] input"); // close the dropdown @@ -3629,8 +3607,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await click(target, ".o_form_button_edit"); - // add a first row with a specific domain for the m2o domain = [["id", "in", [10]]]; // domain for subrecord 1 await click(target, ".o_field_x2many_list_row_add a"); @@ -3835,7 +3811,7 @@ QUnit.module("Fields", (hooks) => { - + `, @@ -3848,7 +3824,6 @@ QUnit.module("Fields", (hooks) => { }); // Opening the modal - await clickEdit(target); await click(target.querySelectorAll(".o_data_row td")[1]); await click(target, ".o_external_button"); assert.containsOnce(target, ".modal"); @@ -3940,8 +3915,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await click(target, ".o_form_button_edit"); - await click(target, ".o_field_many2one input"); assert.containsOnce(target, ".o_field_many2one .dropdown-menu"); @@ -4001,8 +3974,6 @@ QUnit.module("Fields", (hooks) => { arch: '
', }); - await clickEdit(target); - await selectDropdownItem(target, "trululu", "Search More..."); const modal = target.querySelector(".modal"); await toggleGroupByMenu(modal); @@ -4017,7 +3988,7 @@ QUnit.module("Fields", (hooks) => { QUnit.test("focus when closing many2one modal in many2one modal", async function (assert) { serverData.views = { - "partner,false,form": '
', + "partner,false,form": '
', }; await makeView({ @@ -4025,7 +3996,7 @@ QUnit.module("Fields", (hooks) => { serverData, resModel: "partner", resId: 2, - arch: '
', + arch: '
', mockRPC(route, { method }) { if (method === "get_formview_id") { return Promise.resolve(false); @@ -4034,7 +4005,6 @@ QUnit.module("Fields", (hooks) => { }); // Open many2one modal - await clickEdit(target); await click(target, ".o_external_button"); const originalModal = target.querySelector(".modal"); @@ -4102,8 +4072,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await clickEdit(target); - await selectDropdownItem(target, "trululu", "Search More..."); const modal = target.querySelector(".modal"); @@ -4209,4 +4177,67 @@ QUnit.module("Fields", (hooks) => { "Placeholder" ); }); + + QUnit.test("open_target prop = current", async function (assert) { + serverData.views = { + "partner,false,form": '
', + "partner,false,search": "", + }; + serverData.actions = { + 1: { + name: "Partner", + res_model: "partner", + res_id: 1, + type: "ir.actions.act_window", + views: [[false, "form"]], + }, + }; + const webClient = await createWebClient({ + serverData, + mockRPC(route, { method }) { + if (method === "get_formview_action") { + assert.step("get_formview_action"); + return { + type: "ir.actions.act_window", + res_model: "partner", + view_type: "form", + view_mode: "form", + views: [[false, "form"]], + target: "current", + res_id: false, + }; + } + }, + }); + await doAction(webClient, 1); + + await selectDropdownItem(target, "trululu", "first record"); + assert.containsOnce(target, ".o_field_widget .o_external_button.fa-arrow-right"); + await click(target, ".o_field_widget .o_external_button"); + + assert.verifySteps(["get_formview_action"]); + assert.strictEqual(target.querySelector(".breadcrumb").textContent, "first recordNew"); + }); + + QUnit.test("open_target prop = new", async function (assert) { + await makeView({ + type: "form", + resModel: "partner", + serverData, + arch: '
', + mockRPC(route, { method }) { + if (method === "get_formview_id") { + assert.step("get_formview_id"); + return false; + } + }, + }); + + await selectDropdownItem(target, "trululu", "first record"); + assert.containsOnce(target, ".o_field_widget .o_external_button.fa-external-link"); + await click(target, ".o_field_widget .o_external_button"); + + assert.verifySteps(["get_formview_id"]); + assert.containsOnce(target, ".modal"); + }); }); diff --git a/addons/web/static/tests/views/fields/monetary_field_tests.js b/addons/web/static/tests/views/fields/monetary_field_tests.js index 5504a8d8217..2c56014771f 100644 --- a/addons/web/static/tests/views/fields/monetary_field_tests.js +++ b/addons/web/static/tests/views/fields/monetary_field_tests.js @@ -4,7 +4,6 @@ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { addRow, click, - clickEdit, clickSave, editInput, getFixture, @@ -108,14 +107,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a09.10", - "The value should be displayed properly." - ); - - await clickEdit(target); - assert.containsOnce( target, ".o_field_monetary > div.text-nowrap", @@ -141,8 +132,8 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a0108.25", + target.querySelector(".o_field_widget input").value, + "108.25", "The new value should be rounded properly." ); }); @@ -169,13 +160,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a09.10", - "The value should be displayed properly." - ); - - await clickEdit(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "9.10", @@ -196,8 +180,8 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a0108.25", + target.querySelector(".o_field_widget input").value, + "108.25", "The new value should be rounded properly." ); }); @@ -225,12 +209,11 @@ QUnit.module("Fields", (hooks) => { }); // Test computation and rounding - await clickEdit(target); await editInput(target, ".o_field_monetary input", "=100/3"); await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a033.33", + target.querySelector(".o_field_widget input").value, + "33.33", "The new value should be calculated and rounded properly." ); }); @@ -258,12 +241,11 @@ QUnit.module("Fields", (hooks) => { }); // Test computation and rounding - await clickEdit(target); await editInput(target, ".o_field_monetary input", "=100/3"); await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a033.33", + target.querySelector(".o_field_widget input").value, + "33.33", "The new value should be calculated and rounded properly." ); }); @@ -290,14 +272,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // Non-breaking space between the currency and the amount - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "0.00\u00a0€", - "The value should be displayed properly." - ); - - await clickEdit(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "0.00", @@ -317,10 +291,9 @@ QUnit.module("Fields", (hooks) => { ); await clickSave(target); - // Non-breaking space between the currency and the amount assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "108.25\u00a0€", + target.querySelector(".o_field_widget input").value, + "108.25", "The new value should be rounded properly." ); }); @@ -347,14 +320,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // Non-breaking space between the currency and the amount - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "0.00\u00a0€", - "The value should be displayed properly." - ); - - await clickEdit(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "0.00", @@ -374,10 +339,9 @@ QUnit.module("Fields", (hooks) => { ); await clickSave(target); - // Non-breaking space between the currency and the amount assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "108.25\u00a0€", + target.querySelector(".o_field_widget input").value, + "108.25", "The new value should be rounded properly." ); }); @@ -417,14 +381,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // Non-breaking space between the currency and the amount - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "99.1234\u00a0Bs.F", - "The value should be displayed properly." - ); - - await clickEdit(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "99.1234", @@ -444,10 +400,9 @@ QUnit.module("Fields", (hooks) => { ); await clickSave(target); - // Non-breaking space between the currency and the amount assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "99.1111\u00a0Bs.F", + target.querySelector(".o_field_widget input").value, + "99.1111", "The new value should be rounded properly." ); }); @@ -487,14 +442,6 @@ QUnit.module("Fields", (hooks) => { `, }); - // Non-breaking space between the currency and the amount - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "99.1234\u00a0Bs.F", - "The value should be displayed properly." - ); - - await clickEdit(target); assert.strictEqual( target.querySelector(".o_field_widget input").value, "99.1234", @@ -514,10 +461,9 @@ QUnit.module("Fields", (hooks) => { ); await clickSave(target); - // Non-breaking space between the currency and the amount assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "99.1111\u00a0Bs.F", + target.querySelector(".o_field_widget input").value, + "99.1111", "The new value should be rounded properly." ); }); @@ -734,14 +680,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual( - target.querySelector(".o_field_monetary").textContent, - "$\u00a04.20", - "readonly value should contain the currency" - ); - - await clickEdit(target); - // replace bottom with new helpers when they exist await click(target, ".o_field_many2one_selection input"); const euroM2OListItem = Array.from( @@ -757,10 +695,9 @@ QUnit.module("Fields", (hooks) => { ); await clickSave(target); - assert.strictEqual( - target.querySelector(".o_field_monetary").textContent, - "4.20\u00a0€", + target.querySelector(".o_field_monetary input").value, + "4.20", "The new value should still be correct." ); }); @@ -787,14 +724,6 @@ QUnit.module("Fields", (hooks) => { `, }); - assert.strictEqual( - target.querySelector(".o_field_monetary").textContent, - "$\u00a04.20", - "readonly value should contain the currency" - ); - - await clickEdit(target); - // replace bottom with new helpers when they exist await click(target, ".o_field_many2one_selection input"); const euroM2OListItem = Array.from( @@ -810,10 +739,9 @@ QUnit.module("Fields", (hooks) => { ); await clickSave(target); - assert.strictEqual( - target.querySelector(".o_field_monetary").textContent, - "4.20\u00a0€", + target.querySelector(".o_field_monetary input").value, + "4.20", "The new value should still be correct." ); }); @@ -831,7 +759,7 @@ QUnit.module("Fields", (hooks) => { type: "form", resModel: "partner", arch: ` -
+ @@ -1008,7 +936,7 @@ QUnit.module("Fields", (hooks) => { type: "form", resModel: "partner", arch: ` - + @@ -1059,14 +987,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - // Non-breaking space between the currency and the amount - assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a0-8.9", - "The value should be displayed properly." - ); - - await click(target.querySelector(".o_form_button_edit")); assert.strictEqual( target.querySelector(".o_field_widget[name=float_field] input").value, "-8.9", @@ -1086,11 +1006,10 @@ QUnit.module("Fields", (hooks) => { "The value should should be formatted on blur." ); - await click(target.querySelector(".o_form_button_save")); - // Non-breaking space between the currency and the amount + await clickSave(target); assert.strictEqual( - target.querySelector(".o_field_widget").textContent, - "$\u00a0109.2", + target.querySelector(".o_field_widget input").value, + "109.2", "The new value should be rounded properly." ); }); @@ -1110,8 +1029,6 @@ QUnit.module("Fields", (hooks) => { `, }); - await clickEdit(target); - // Non-breaking space between the currency and the amount assert.strictEqual( target.querySelector(".o_field_widget[name=float_field] input").value, @@ -1154,10 +1071,5 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_form_editable"); assert.strictEqual(target.querySelector("[name=monetary_field] input").value, "0.00"); - - await clickSave(target); - - assert.containsOnce(target, ".o_form_readonly"); - assert.strictEqual(target.querySelector("[name=monetary_field] span").innerText, "0.00"); }); }); diff --git a/addons/web/static/tests/views/fields/numeric_fields_tests.js b/addons/web/static/tests/views/fields/numeric_fields_tests.js index b67fd369a82..a64fadc872c 100644 --- a/addons/web/static/tests/views/fields/numeric_fields_tests.js +++ b/addons/web/static/tests/views/fields/numeric_fields_tests.js @@ -105,9 +105,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - // Record edit mode - await click(target.querySelector(".o_form_button_edit")); - // Get all inputs const floatFactorField = target.querySelector(".o_field_float_factor input"); const floatInput = target.querySelector(".o_field_float input"); diff --git a/addons/web/static/tests/views/fields/one2many_field_tests.js b/addons/web/static/tests/views/fields/one2many_field_tests.js index dc3bdc57c85..3b89e05dd1e 100644 --- a/addons/web/static/tests/views/fields/one2many_field_tests.js +++ b/addons/web/static/tests/views/fields/one2many_field_tests.js @@ -6,7 +6,6 @@ import { click, clickCreate, clickDiscard, - clickEdit, clickSave, clickM2OHighlightedItem, clickOpenedDropdownItem, @@ -308,7 +307,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await click(target, ".o_form_button_edit"); await addRow(target, ".o_field_x2many_list"); @@ -429,7 +427,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); await addRow(target); const targetInput = target.querySelector(".o_selected_row [name=foo] input"); @@ -492,15 +489,14 @@ QUnit.module("Fields", (hooks) => { }); assert.verifySteps(["get_views", "read", "read"]); - assert.containsNone(target, "td.o_list_record_selector"); - assert.containsNone(target, ".o_field_x2many_list_row_add"); - assert.containsNone(target, "td.o_list_record_remove", 1); - - await clickEdit(target); + // assert.containsNone(target, "td.o_list_record_selector"); + // assert.containsNone(target, ".o_field_x2many_list_row_add"); + // assert.containsNone(target, "td.o_list_record_remove"); + // await clickEdit(target); assert.containsOnce(target, ".o_field_x2many_list_row_add"); assert.hasAttrValue(target.querySelector(".o_field_x2many_list_row_add"), "colspan", "2"); - assert.containsOnce(target, "td.o_list_record_remove", 1); + assert.containsOnce(target, "td.o_list_record_remove"); }); QUnit.test("transferring class attributes in one2many sub fields", async function (assert) { @@ -520,9 +516,6 @@ QUnit.module("Fields", (hooks) => { }); assert.containsOnce(target, "td.hey"); - await clickEdit(target); - assert.containsOnce(target, "td.hey"); - await click(target.querySelector("td.o_data_cell")); assert.containsOnce(target, 'td.hey div[name="turtle_foo"] input'); // WOWL to check! hey on input? }); @@ -733,13 +726,6 @@ QUnit.module("Fields", (hooks) => { ["yop", "blip", "kawa"] ); - await clickEdit(target); - - assert.deepEqual( - [...target.querySelectorAll(".o_data_cell.o_list_char")].map((el) => el.innerText), - ["yop", "blip", "kawa"] - ); - // Drag and drop the second line in first position await dragAndDrop("tbody tr:nth-child(2) .o_handle_cell", "tbody tr", "top"); @@ -819,7 +805,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target.querySelectorAll(".o_data_cell")[1]); await editInput(target, ".o_selected_row .o_field_widget[name=turtle_foo] input", "hop"); await clickSave(target); @@ -899,7 +884,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target.querySelectorAll(".o_data_cell")[1]); await editInput( target, @@ -984,7 +968,6 @@ QUnit.module("Fields", (hooks) => { ["second record"] ); - await clickEdit(target); await click(target.querySelectorAll(".o_data_cell")[1]); await editInput(target, ".o_selected_row [name=turtle_foo] input", "hop"); await clickSave(target); @@ -1030,8 +1013,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_data_cell.o_list_char")), [ "yop", "blip", @@ -1092,8 +1073,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - assert.deepEqual( getNodesTextContent(target.querySelectorAll(".o_data_cell.o_list_char")), ["yop", "blip", "kawa"] @@ -1156,8 +1135,6 @@ QUnit.module("Fields", (hooks) => { ["yop", "blip", "kawa"] ); - await clickEdit(target); - await click(target.querySelector(".o_data_cell.o_list_char")); await editInput(target, '.o_list_renderer div[name="turtle_foo"] input', "blurp"); @@ -1242,13 +1219,12 @@ QUnit.module("Fields", (hooks) => { ).join(""); }; - await clickEdit(target); assert.strictEqual(getTurtleFooValues(), "#20#21#22#23#24#25#26#27#28#29"); await click(target.querySelector(".o_data_cell.o_list_char")); await editInput(target, "div[name=turtle_foo] input", "blurp"); // click outside of the one2many to unselect the row - await click(target, ".o_cp_bottom_left"); + await click(target, ".o_form_view"); assert.strictEqual(getTurtleFooValues(), "blurp#21#22#23#24#25#26#27#28#29"); // the domain fail if the widget does not use the already loaded data. @@ -1256,7 +1232,6 @@ QUnit.module("Fields", (hooks) => { assert.containsNone(target, ".modal"); assert.strictEqual(getTurtleFooValues(), "#20#21#22#23#24#25#26#27#28#29"); - await clickEdit(target); // Drag and drop the third line in second position await dragAndDrop("tbody tr:nth-child(3) .o_handle_cell", "tbody tr:nth-child(2)"); assert.strictEqual(getTurtleFooValues(), "#20#30#31#32#33#34#35#36#37#38"); @@ -1265,7 +1240,7 @@ QUnit.module("Fields", (hooks) => { await dragAndDrop("tbody tr:nth-child(3) .o_handle_cell", "tbody tr:nth-child(2)"); assert.strictEqual(getTurtleFooValues(), "#20#39#40#41#42#43#44#45#46#47"); - await click(target, ".o_cp_bottom_left"); + await click(target, ".o_form_view"); assert.strictEqual(getTurtleFooValues(), "#20#39#40#41#42#43#44#45#46#47"); await clickDiscard(target); @@ -1313,7 +1288,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); await click(target.querySelector(".o_field_widget[name=turtles] .o_pager_next")); await click( @@ -1449,7 +1423,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); await click(target.querySelector(".o_field_widget[name=turtles] .o_pager_next")); await click( @@ -1563,7 +1536,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.containsOnce(target, ".o_data_row"); // change the value of foo to trigger the onchange @@ -1619,7 +1591,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.containsOnce( target, ".o_data_row", @@ -1695,7 +1666,6 @@ QUnit.module("Fields", (hooks) => { }, resId: 1, }); - await clickEdit(target); assert.containsOnce(target, ".o_data_row"); assert.deepEqual( @@ -2005,8 +1975,6 @@ QUnit.module("Fields", (hooks) => { const formHandle = Object.keys(model.localData).find((k) => /partner/.test(k)); - await clickEdit(target); - assert.deepEqual( Object.values(model.get(formHandle).data.turtles.data).map((r) => { return r.data; @@ -2086,13 +2054,6 @@ QUnit.module("Fields", (hooks) => { "yop", ]); - await clickEdit(target); - assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_data_cell.o_list_char")), [ - "My little Foo Value", - "blip", - "yop", - ]); - assert.verifySteps([]); // Drag and drop the second line in first position @@ -2363,7 +2324,6 @@ QUnit.module("Fields", (hooks) => { // add a record on page one checkRead = true; - await clickEdit(target); await click(target.querySelector(".o-kanban-button-new")); await editInput(target, ".modal input", "new record"); @@ -2394,7 +2354,6 @@ QUnit.module("Fields", (hooks) => { // delete a record on page one checkRead = true; - await clickEdit(target); assert.strictEqual( target.querySelector(".o_kanban_record:not(.o_kanban_ghost)").innerText, "relational record 10" @@ -2423,7 +2382,6 @@ QUnit.module("Fields", (hooks) => { await clickSave(target); // add and delete records in both pages - await clickEdit(target); checkRead = true; readIDs = undefined; // add and delete a record in page 1 @@ -2500,7 +2458,7 @@ QUnit.module("Fields", (hooks) => { resModel: "partner", serverData, arch: ` -
+ @@ -2557,8 +2515,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - assert.strictEqual(target.querySelector(".o_kanban_record ").innerText, "donatello"); await click(target.querySelector(".o_kanban_record")); @@ -2633,7 +2589,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); await addRow(target); await editInput(target, 'div[name="turtle_int"] input', "5"); await click(target.querySelector(".modal-footer button.btn-primary")); @@ -2721,7 +2676,6 @@ QUnit.module("Fields", (hooks) => { "relational record 1" ); - await clickEdit(target); await click(target.querySelector(".o_field_one2many tbody td")); assert.hasClass( target.querySelector(".o_field_one2many tbody .o_data_row"), @@ -2754,7 +2708,6 @@ QUnit.module("Fields", (hooks) => { ); // edit again and save - await clickEdit(target); await click(target.querySelector(".o_field_one2many tbody td")); await editInput(target, ".o_field_one2many tbody td input", "new value"); await click(target.querySelector(".o_form_view")); @@ -2783,9 +2736,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); assert.containsNone(target, ".o_field_x2many_list_row_add"); - - await clickEdit(target); - assert.containsNone(target, ".o_field_x2many_list_row_add"); }); QUnit.test("one2many list: conditional create/delete actions", async function (assert) { @@ -2806,7 +2756,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); // bar is true -> create and delete action are available assert.containsOnce(target, ".o_field_x2many_list_row_add"); assert.containsN(target, "td.o_list_record_remove button", 2); @@ -2859,7 +2808,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); assert.containsN(target, "td.o_list_record_remove button", 3); await click(target.querySelector("td.o_list_record_remove button")); @@ -2905,7 +2853,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); assert.containsN(target, "td.o_list_record_remove button", 3); await click(target.querySelector("td.o_list_record_remove button")); @@ -2920,7 +2867,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("one2many kanban: edition", async function (assert) { - assert.expect(22); + assert.expect(20); serverData.models.partner.records[0].p = [2]; await makeView({ @@ -2969,11 +2916,6 @@ QUnit.module("Fields", (hooks) => { }, }); - assert.containsNone(target, ".delete_icon"); - assert.containsNone(target, ".o_field_one2many .o-kanban-button-new"); - - await clickEdit(target); - assert.containsOnce(target, ".o_kanban_record:not(.o_kanban_ghost)"); assert.strictEqual( target.querySelector(".o_kanban_record span").textContent, @@ -3100,7 +3042,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.deepEqual( [ ...target.querySelectorAll( @@ -3138,9 +3079,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - assert.containsNone(target, ".o-kanban-button-new"); - - await clickEdit(target); assert.containsNone(target, ".o-kanban-button-new"); assert.containsOnce(target, ".o_field_x2many_kanban .delete_icon"); @@ -3175,7 +3113,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); // bar is initially true -> create and delete actions are available assert.containsOnce(target, ".o-kanban-button-new", '"Add" button should be available'); @@ -3223,7 +3160,6 @@ QUnit.module("Fields", (hooks) => { }); // add a record, add value to turtle_foo then click in form view to confirm it - await clickEdit(target); await addRow(target); await editInput(target, 'div[name="turtle_foo"] input', "nora"); @@ -3237,7 +3173,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("one2many list (non editable): edition", async function (assert) { - assert.expect(12); + assert.expect(10); let nbWrite = 0; serverData.models.partner.records[0].p = [2, 4]; @@ -3271,11 +3207,6 @@ QUnit.module("Fields", (hooks) => { }, }); - assert.containsNone(target, ".o_list_record_remove"); - assert.containsNone(target, ".o_field_x2many_list_row_add"); - - await clickEdit(target); - assert.containsN(target, "td.o_list_number", 2); assert.strictEqual( target.querySelector(".o_list_renderer tbody td").textContent, @@ -3341,7 +3272,6 @@ QUnit.module("Fields", (hooks) => { }); // edit mode, then click on Add an item and enter a value - await clickEdit(target); await addRow(target); await editInput(target, ".o_selected_row > td input", "kartoffel"); assert.strictEqual(target.querySelector("td .o_field_char input").value, "kartoffel"); @@ -3383,7 +3313,6 @@ QUnit.module("Fields", (hooks) => { // edit mode, then click on Add an item, enter value in turtle_foo and Add an item again assert.containsOnce(target, "tr.o_data_row"); - await clickEdit(target); await addRow(target); await editInput(target, 'div[name="turtle_foo"] input', "nora"); await addRow(target); @@ -3429,7 +3358,6 @@ QUnit.module("Fields", (hooks) => { // edit mode, then click on Add an item assert.containsNone(target, "tr.o_data_row"); - await clickEdit(target); await addRow(target); assert.strictEqual(target.querySelector(".o_data_row textarea").value, ""); @@ -3460,7 +3388,6 @@ QUnit.module("Fields", (hooks) => { // edit mode, then click on Add an item, enter value in turtle_foo and Add an item again assert.containsOnce(target, "tr.o_data_row"); assert.strictEqual(target.querySelector(".o_data_cell").innerText, "blip"); - await clickEdit(target); await addRow(target); await editInput(target, ".o_field_widget[name=turtle_foo] input", "aaa"); assert.containsN(target, "tr.o_data_row", 2); @@ -3497,7 +3424,6 @@ QUnit.module("Fields", (hooks) => { // edit mode, then click on Add an item, then click elsewhere assert.containsNone(target, "tr.o_data_row"); - await clickEdit(target); await addRow(target); await click(target); assert.containsNone(target, "tr.o_data_row"); @@ -3528,7 +3454,6 @@ QUnit.module("Fields", (hooks) => { }); // add a record, to reach the page size limit - await clickEdit(target); await addRow(target); // the record currently being added should not count in the pager assert.containsNone(target, ".o_field_widget[name=turtles] .o_pager"); @@ -3567,7 +3492,6 @@ QUnit.module("Fields", (hooks) => { }); // add a record, then discard - await clickEdit(target); await addRow(target); await clickDiscard(target); @@ -3601,7 +3525,6 @@ QUnit.module("Fields", (hooks) => { }); // add a (empty) record - await clickEdit(target); await addRow(target); // go on next page. The new record is not valid and should be discarded @@ -3633,7 +3556,6 @@ QUnit.module("Fields", (hooks) => { }); // add a record with a dirty state, but not valid - await clickEdit(target); await addRow(target); await editInput(target, '.o_field_widget[name="turtle_int"] input', 4321); @@ -3708,7 +3630,6 @@ QUnit.module("Fields", (hooks) => { }); // add 4 records (to have more records than the limit) - await clickEdit(target); await addRow(target); await editInput(target, '.o_field_widget[name="turtle_foo"] input', "nora"); await addRow(target); @@ -3750,7 +3671,6 @@ QUnit.module("Fields", (hooks) => { // edit mode, then click on Add an item, then click elsewhere assert.containsNone(target, "tr.o_data_row"); - await clickEdit(target); await addRow(target); assert.containsOnce(target, "tr.o_data_row"); @@ -3793,7 +3713,6 @@ QUnit.module("Fields", (hooks) => { }); // edit mode, then click on Add an item, then press enter - await clickEdit(target); await addRow(target); triggerHotkey("Enter"); await nextTick(); @@ -3817,7 +3736,6 @@ QUnit.module("Fields", (hooks) => { resId: 2, }); - await clickEdit(target); await addRow(target); assert.containsOnce(target, ".o_data_row"); assert.hasClass(target.querySelectorAll(".o_data_row")[0], "o_selected_row"); @@ -3860,7 +3778,6 @@ QUnit.module("Fields", (hooks) => { resId: 2, }); - await clickEdit(target); await addRow(target); assert.strictEqual( document.activeElement, @@ -3910,7 +3827,6 @@ QUnit.module("Fields", (hooks) => { // edit mode, then click on Add an item assert.containsNone(target, "tr.o_data_row"); - await clickEdit(target); await addRow(target); // input some text in required turtle_foo field @@ -3954,7 +3870,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); assert.containsOnce(target, "tr.o_data_row"); @@ -3986,7 +3901,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); assert.containsOnce(target, "tr.o_data_row"); @@ -4024,8 +3938,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - await addRow(target); await addRow(target, ".modal"); @@ -4065,7 +3977,6 @@ QUnit.module("Fields", (hooks) => { }); assert.verifySteps(["get_views", "read", "read"]); - await clickEdit(target); await click(target.querySelector(".o_data_cell")); await editInput(target, ".o_field_widget[name=product_id] input", ""); assert.verifySteps([], "no onchange should be done as line is invalid"); @@ -4109,7 +4020,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target.querySelector(".o_field_one2many tbody td")); await editInput( target.querySelector(".o_field_one2many tbody td input"), @@ -4205,7 +4115,6 @@ QUnit.module("Fields", (hooks) => { assert.step(args.method); }, }); - await clickEdit(target); const td = target.querySelector("td"); assert.strictEqual(td.textContent, "9"); await click(td); @@ -4236,7 +4145,6 @@ QUnit.module("Fields", (hooks) => { assert.step(args.method); }, }); - await clickEdit(target); const td = target.querySelector("td"); assert.strictEqual(td.textContent, "01/25/2017"); @@ -4293,7 +4201,6 @@ QUnit.module("Fields", (hooks) => { }, resId: 1, }); - await clickEdit(target); assert.containsOnce(target, ".o_data_row"); // empty o2m by triggering the onchange @@ -4385,7 +4292,6 @@ QUnit.module("Fields", (hooks) => { }, resId: 1, }); - await clickEdit(target); assert.containsOnce(target, ".o_data_row"); await click(target.querySelector(".o_field_one2many td")); await editInput(target, ".o_field_widget[name=display_name] input", "blurp"); @@ -4481,7 +4387,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); assert.containsNone(target, ".o_data_row"); await addRow(target); @@ -4536,7 +4441,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.containsOnce(target, ".o_form_view .o_field_x2many_list_row_add "); assert.containsNone(target, ".o_form_view input"); @@ -4566,7 +4470,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); assert.deepEqual( [...target.querySelectorAll(".o_data_cell")].map((el) => el.textContent), ["first record", "second record", "aaa"] @@ -4601,7 +4504,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); // switch the first row in edition await click(target.querySelector(".o_data_cell")); @@ -4688,7 +4590,6 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_data_cell[data-tooltip='xphone']"); assert.containsNone(target, ".o_data_cell[data-tooltip='xpad']"); - await clickEdit(target); await addRow(target); checkOnchange = true; @@ -4741,7 +4642,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); await addRow(target); // write in the many2one field, value = 37 (xphone) @@ -4830,7 +4730,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); // open a modal await click(target.querySelector("tr.o_data_row td[data-tooltip='xphone']")); @@ -4870,7 +4769,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target, ".o_field_widget[name=product_id] input"); } ); @@ -4908,7 +4806,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await click(target.querySelector("tr.o_data_row td[data-tooltip='xphone']")); // trigger a name search @@ -4949,7 +4846,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); }); @@ -4992,7 +4888,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); await editInput(target, '[name="turtle_foo"] input', "hammer"); await addRow(target); @@ -5015,7 +4910,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); await addRow(target); assert.containsOnce( target, @@ -5067,7 +4961,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); } ); @@ -5103,7 +4996,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); assert.containsOnce(target, ".modal"); } @@ -5167,7 +5059,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); assert.strictEqual( @@ -5179,8 +5070,7 @@ QUnit.module("Fields", (hooks) => { await click($(".modal .o_data_row:first .o_list_record_selector input")[0]); await nextTick(); // additional render due to the change of selection (done in owl, not pure js) await click($(".modal .o_select_button")[0]); - await click($(".o_form_button_save")[0]); - await clickEdit(target); + await clickSave(target); await addRow(target); assert.strictEqual( @@ -5206,7 +5096,7 @@ QUnit.module("Fields", (hooks) => { "should display the record values in one2many list" ); - await click($(".o_form_button_save")[0]); + await clickSave(target); }); QUnit.test("one2many with many2many widget: edition", async function (assert) { @@ -5273,7 +5163,6 @@ QUnit.module("Fields", (hooks) => { }, }); //await new Promise(() => {}) - await clickEdit(target); await click($(target).find(".o_data_cell:first")[0]); assert.strictEqual( $(".modal .modal-title").first().text().trim(), @@ -5288,10 +5177,9 @@ QUnit.module("Fields", (hooks) => { await clickM2OHighlightedItem(target, "product_id"); await click($(".modal-footer button:first")[0]); - await click($(".o_form_button_save")[0]); + await clickSave(target); // add a one2many record - await clickEdit(target); await addRow(target); await click($(".modal .o_data_row:first .o_list_record_selector input")[0]); await nextTick(); // wait for re-rendering because of the change of selection @@ -5303,7 +5191,7 @@ QUnit.module("Fields", (hooks) => { await clickM2OHighlightedItem(target, "product_id"); await click($(".modal .modal-footer button:first")[0]); - await click($(".o_form_button_save")[0]); + await clickSave(target); }); QUnit.test("new record, the context is properly evaluated and sent", async function (assert) { @@ -5369,7 +5257,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); await addRow(target); }); @@ -5409,7 +5296,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); assert.containsOnce(target, ".o_data_row"); await click(target.querySelector(".o_data_row .o_data_cell")); @@ -5539,7 +5425,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); await addRow(target); await click(target.querySelector(".modal-footer button.btn-primary")); @@ -5733,8 +5618,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - await addRow(target, ".o_field_one2many"); assert.containsOnce(target, ".modal"); await click(target.querySelector(".modal-footer button")); @@ -5790,7 +5673,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("many2one and many2many in one2many", async function (assert) { - assert.expect(13); + assert.expect(12); serverData.models.turtle.records[1].product_id = 37; serverData.models.partner.records[0].turtles = [2, 3]; @@ -5862,13 +5745,9 @@ QUnit.module("Fields", (hooks) => { "m2m values should have been correctly fetched" ); - await click(target.querySelector(".o_data_row td")); - - assert.strictEqual(target.querySelector(".modal .o_field_widget").innerText, "xphone"); - - await click(target.querySelector(".modal-footer button")); - - await clickEdit(target); + // await click(target.querySelector(".o_data_row td")); + // assert.strictEqual(target.querySelector(".modal .o_field_widget").innerText, "xphone"); + // await click(target.querySelector(".modal-footer button")); assert.containsOnce( target, @@ -5946,7 +5825,6 @@ QUnit.module("Fields", (hooks) => { assert.step(args.method); }, }); - await clickEdit(target); assert.containsN(target, ".o_data_row", 2); await click(target.querySelector(".o_list_record_remove")); @@ -6185,7 +6063,7 @@ QUnit.module("Fields", (hooks) => { resModel: "partner", serverData, arch: ` -
+ @@ -6249,8 +6127,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - await click(target.querySelector(".o_data_row td")); // edit first record await click(target.querySelector('div[name="partner_ids"] input')); @@ -6282,7 +6158,7 @@ QUnit.module("Fields", (hooks) => { - + `, @@ -6301,8 +6177,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - // edit the first partner in the one2many partner form view await click(target.querySelector(".o_data_row td.o_data_cell")); // open form view for many2one @@ -6530,7 +6404,7 @@ QUnit.module("Fields", (hooks) => { resModel: "partner", serverData, arch: ` -
+ @@ -6602,8 +6476,7 @@ QUnit.module("Fields", (hooks) => { "should not have kanban records yet" ); - // // switch to edit mode and create a new kanban record - await clickEdit(target); + // create a new kanban record await click(target, ".o_field_widget .o-kanban-button-new"); // save & close the modal @@ -6761,9 +6634,6 @@ QUnit.module("Fields", (hooks) => { await click(target, btn1Warn); assert.verifySteps(["button_warn_partner_4"]); - // switch to edit mode - await clickEdit(target); - // click on existing buttons await click(target, btn1Disabled); assert.verifySteps(["button_disabled_partner_4"]); @@ -6813,8 +6683,7 @@ QUnit.module("Fields", (hooks) => { ]); // save the form - await clickSave(target); - assert.verifySteps([]); // the write has already been done + assert.containsOnce(target, ".o_form_saved"); // click all buttons await click(target, btn1Disabled); @@ -6852,7 +6721,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); await click(target.querySelector(".o_data_row td")); const turtleFooInput = target.querySelector('[name="turtle_foo"] input'); @@ -6891,8 +6759,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); - assert.containsOnce(target, ".o_data_row", "should start with one data row"); await addRow(target); @@ -6939,7 +6805,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); assert.containsOnce(target, ".o_data_row", "should start with one data row"); @@ -6980,15 +6845,12 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); await addRow(target); assert.containsOnce(target, ".modal"); assert.containsNone(target, ".modal .modal-footer .o_btn_remove"); // Discard a modal await click(target.querySelector(".modal-footer .btn-secondary")); - - await clickDiscard(target); }); QUnit.test('x2many fields use their "mode" attribute', async function (assert) { @@ -7054,7 +6916,6 @@ QUnit.module("Fields", (hooks) => { }, resId: 1, }); - await clickEdit(target); assert.strictEqual( target.querySelector('.o_field_widget[name="int_field"] input').value, "0" @@ -7121,7 +6982,6 @@ QUnit.module("Fields", (hooks) => { }, resId: 1, }); - await clickEdit(target); assert.strictEqual( $(target).find('.o_field_widget[name="int_field"] input').val(), @@ -7185,7 +7045,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); assert.strictEqual( target.querySelector('.o_field_widget[name="int_field"] input').value, "10" @@ -7234,7 +7093,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); assert.strictEqual( target.querySelector('.o_field_widget[name="int_field"] input').value, "10" @@ -7432,8 +7290,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); - patchWithCleanup(form.env.services.notification, { add: (message, params) => { assert.step(params.type); @@ -7512,8 +7368,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, context: { active_field: 2 }, }); - await clickEdit(target); - await click(target.querySelector(".o_data_cell")); await editInput(target, ".o_field_widget[name=display_name] input", "abc"); await clickSave(target); @@ -7539,8 +7393,6 @@ QUnit.module("Fields", (hooks) => { assert.step(args.method); }, }); - await clickEdit(target); - await editInput(target, '[name="foo"] input', "abcd"); assert.verifySteps(["get_views", "read", "onchange"]); }); @@ -7598,7 +7450,7 @@ QUnit.module("Fields", (hooks) => { ); QUnit.test("multi level of nested x2manys, onchange and rawChanges", async function (assert) { - assert.expect(8); + assert.expect(7); serverData.models.partner.records[0].p = [1]; serverData.models.partner.onchanges = { name: function () {}, @@ -7634,14 +7486,7 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_data_row"); - // open the o2m record in readonly first - await click(target.querySelector(".o_data_row td")); - assert.containsOnce(target, ".modal .o_form_readonly"); - - await clickDiscard(target.querySelector(".modal")); - - // switch to edit mode and open it again - await clickEdit(target); + // open the dialog await click(target.querySelector(".o_data_row td")); assert.containsOnce(target, ".modal .o_form_editable"); assert.containsOnce(target, ".modal .o_data_row"); @@ -7692,8 +7537,6 @@ QUnit.module("Fields", (hooks) => { assert.step(args.method); }, }); - await clickEdit(target); - // triggers an onchange on partner, because the new record is valid await addRow(target); @@ -7784,8 +7627,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - - await clickEdit(target); await addRow(target); assert.containsN(target, "tr.o_data_row", 41); assert.hasClass([...target.querySelectorAll("tr.o_data_row")].pop(), "o_selected_row"); @@ -7819,8 +7660,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - - await clickEdit(target); // add a new record page 1 (this increases the limit to 4) await addRow(target); await editInput(target, '.o_data_row [name="turtle_foo"] input', "rainbow dash"); @@ -7863,8 +7702,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - - await clickEdit(target); await click(target, ".o_field_widget[name=turtles] .o_pager_next"); assert.containsN(target, "tr.o_data_row", 2); @@ -8081,8 +7918,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); - let rows = target.querySelectorAll(".o_data_row"); await click(rows[0].querySelector(".o_data_cell")); assert.containsOnce(target, ".o_data_row.o_selected_row"); @@ -8191,7 +8026,6 @@ QUnit.module("Fields", (hooks) => { }, resId: 1, }); - await clickEdit(target); assert.strictEqual(target.querySelector(".o_data_row .o_data_cell").textContent, "blip"); // click and edit value to 'foo', which will trigger onchange @@ -8418,7 +8252,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); await click(target.querySelector(".o_list_record_remove button")); assert.deepEqual( [...target.querySelectorAll(".o_data_row")].map((el) => el.textContent), @@ -8460,7 +8293,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); assert.deepEqual( getNodesTextContent(target.querySelectorAll(".o_data_row .o_data_cell.o_list_char")), ["yop", "blip"] @@ -8493,8 +8325,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); - // starting condition assert.deepEqual( [...target.querySelectorAll(".o_data_row")].map((el) => el.textContent), @@ -8538,8 +8368,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - // starting condition assert.deepEqual( [...target.querySelectorAll(".o_data_row")].map((el) => el.textContent), @@ -8587,8 +8415,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - // starting condition assert.deepEqual( [...target.querySelectorAll(".o_data_row")].map((el) => el.textContent), @@ -8641,8 +8467,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); - // click add a new line // check turtle_int for new is the current max of the page await addRow(target); @@ -8676,9 +8500,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - - await clickEdit(target); - // click add a new line // check turtle_int for new is the current max of the page +1 await addRow(target); @@ -8806,7 +8627,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await click(target, ".o_form_button_edit"); assert.deepEqual( [...target.querySelectorAll(".o_data_cell.foo")].map((el) => el.textContent), ["blip", "kawa"] @@ -8907,7 +8727,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await click(target, ".o_form_button_edit"); await addRow(target); assert.containsN(target, ".o_data_row", 4); @@ -8956,9 +8775,6 @@ QUnit.module("Fields", (hooks) => { assert.step(args.method); }, }); - - await clickEdit(target); - // swap 2 lines in the one2many await dragAndDrop("tbody tr:nth-child(2) .o_handle_cell", "tbody tr", "top"); @@ -9224,7 +9040,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); assert.deepEqual( [...target.querySelector(".o_field_x2many_list_row_add").children].map( (el) => el.textContent @@ -9305,9 +9120,6 @@ QUnit.module("Fields", (hooks) => { `, }); - - await clickEdit(target); - assert.containsOnce(target, ".o_data_row"); assert.strictEqual(target.querySelector(".o_data_cell").textContent, "second record"); @@ -9390,7 +9202,6 @@ QUnit.module("Fields", (hooks) => { }); // add a turtle on second partner - await clickEdit(target); await click(target.querySelectorAll(".o_data_row")[1].querySelector(".o_data_cell")); await addRow(target.querySelector(".modal")); await editInput(target, ".modal .o_field_widget[name=display_name] input", "michelangelo"); @@ -9568,8 +9379,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); - // add a row, fill it, then trigger the tab shortcut await addRow(target); await editInput(target, "[name=turtle_foo] textarea", "ninja"); @@ -9617,8 +9426,6 @@ QUnit.module("Fields", (hooks) => { } }, }); - await clickEdit(target); - await click(target.querySelector(".o_data_row .o_data_cell")); await editInput(target, "[name='turtle_foo'] input", "Test"); assert.containsOnce(target, ".o_data_row"); @@ -9791,7 +9598,7 @@ QUnit.module("Fields", (hooks) => { await addRow(target); await clickSave(target); - assert.containsOnce(target, ".o_form_readonly"); + assert.containsOnce(target, ".o_form_editable"); assert.strictEqual( target.querySelector(".o_data_row").textContent.trim(), "some foo value" @@ -9833,8 +9640,6 @@ QUnit.module("Fields", (hooks) => { }, }); - await clickEdit(target); - // Add a record in the list await addRow(target); await editInput(target, ".modal div[name=display_name] input", "michelangelo"); @@ -9893,7 +9698,6 @@ QUnit.module("Fields", (hooks) => { "should be 2 columns in the one2many" ); - await clickEdit(target); await selectDropdownItem(target, "product_id", "xphone"); assert.containsOnce( @@ -9934,8 +9738,6 @@ QUnit.module("Fields", (hooks) => { `, resId: 1, }); - await clickEdit(target); - assert.strictEqual( target.querySelector(".o_field_widget[name=product_id] input").value, "" @@ -9975,7 +9777,6 @@ QUnit.module("Fields", (hooks) => { resId: 1, }); - await clickEdit(target); assert.strictEqual( target.querySelector(".o_field_widget[name=product_id] input").value, "" @@ -10083,17 +9884,14 @@ QUnit.module("Fields", (hooks) => { ["yop", "blip", "kawa"] ); - // should not work (form in mode "readonly") - await dragAndDrop(".o_kanban_record:nth-child(1)", ".o_kanban_record:nth-child(3)"); - - assert.deepEqual( - [...target.querySelectorAll(".o_kanban_record:not(.o_kanban_ghost)")].map( - (el) => el.innerText - ), - ["yop", "blip", "kawa"] - ); - - await clickEdit(target); + // // should not work (form in mode "readonly") + // await dragAndDrop(".o_kanban_record:nth-child(1)", ".o_kanban_record:nth-child(3)"); + // assert.deepEqual( + // [...target.querySelectorAll(".o_kanban_record:not(.o_kanban_ghost)")].map( + // (el) => el.innerText + // ), + // ["yop", "blip", "kawa"] + // ); await dragAndDrop(".o_kanban_record:nth-child(1)", ".o_kanban_record:nth-child(3)"); @@ -10133,7 +9931,6 @@ QUnit.module("Fields", (hooks) => { assert.containsOnce(target, ".o_data_row"); // edit first row - await clickEdit(target); await click(target.querySelector(".o_data_row .o_data_cell")); assert.hasClass(target.querySelector(".o_data_row"), "o_selected_row"); target.querySelector(".o_selected_row .o_field_widget[name=turtle_int] input").value = "44"; @@ -10446,40 +10243,6 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(target.querySelector('th[data-name="date"]').offsetWidth, width); }); - QUnit.test("editable one2many list with oe_read_only button", async function (assert) { - await makeView({ - type: "form", - resModel: "partner", - serverData, - arch: ` -
- - - -