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/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/progress_bar/progress_bar_field.js b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js index bba216f92b9..0eb33fc45c8 100644 --- a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js +++ b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js @@ -3,12 +3,11 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useAutofocus } from "@web/core/utils/hooks"; -import { useDebounced } from "@web/core/utils/timing"; import { useNumpadDecimal } from "../numpad_decimal_hook"; import { parseFloat } from "../parsers"; import { standardFieldProps } from "../standard_field_props"; -const { Component, onWillUpdateProps, useRef, useState } = owl; +const { Component, onWillUpdateProps, useRef, useState, useExternalListener } = owl; const formatters = registry.category("formatters"); const parsers = registry.category("parsers"); @@ -17,13 +16,13 @@ export class ProgressBarField extends Component { useNumpadDecimal(); useAutofocus({ refName: "maxValue", selectAll: true }); useAutofocus({ refName: "currentValue", selectAll: true }); + useExternalListener(document.body, "click", this.onClickAway, { capture: true }); this.root = useRef("numpadDecimal"); this.state = useState({ currentValue: this.getCurrentValue(this.props), maxValue: this.getMaxValue(this.props), isEditing: false, }); - this.onBlurDebounced = useDebounced(this.onBlur); onWillUpdateProps((nextProps) => { Object.assign(this.state, { currentValue: this.getCurrentValue(nextProps), @@ -113,14 +112,13 @@ export class ProgressBarField extends Component { this.state.isEditing = true; } } - // When both max and current value are editable, as one input is blurred when - // switching to the other, the state would revert to isEditing = false. We need - // to stay in edition mode if the focus is still in the field. - onBlur() { - if (this.root.el && this.root.el.contains(document.activeElement)) { - return; + /** + * @param {MouseEvent} ev + */ + onClickAway(ev) { + if (this.root.el && !this.root.el.contains(ev.target)) { + this.state.isEditing = false; } - this.state.isEditing = false; } onCurrentValueInput(ev) { diff --git a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml index 0c782b1a2f6..eb5107ec10b 100644 --- a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml +++ b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml @@ -33,7 +33,6 @@ t-att-required="props.required" t-on-change="onCurrentValueChange" t-on-input="onCurrentValueInput" - t-on-blur="onBlur" /> % @@ -48,7 +47,6 @@ t-att-value="formatCurrentValue()" t-on-change="onCurrentValueChange" t-on-input="onCurrentValueInput" - t-on-blur="onBlurDebounced" /> diff --git a/addons/web/static/src/views/fields/relational_utils.js b/addons/web/static/src/views/fields/relational_utils.js index f0f6b82b918..9ec84a605b9 100644 --- a/addons/web/static/src/views/fields/relational_utils.js +++ b/addons/web/static/src/views/fields/relational_utils.js @@ -437,9 +437,7 @@ export class X2ManyFieldDialog extends Component { this.modalRef.el.querySelector(`#${autofocusFieldId}`)) || this.modalRef.el.querySelector(".o_field_widget input"); } else { - elementToFocus = - this.modalRef.el.querySelector("button.btn-primary") || - this.modalRef.el.querySelector(".o_control_panel .o_form_button_edit"); + elementToFocus = this.modalRef.el.querySelector("button.btn-primary"); } if (elementToFocus) { elementToFocus.focus(); diff --git a/addons/web/static/src/views/fields/x2many/x2many_field.js b/addons/web/static/src/views/fields/x2many/x2many_field.js index abb6a95284a..cecb17b0a59 100644 --- a/addons/web/static/src/views/fields/x2many/x2many_field.js +++ b/addons/web/static/src/views/fields/x2many/x2many_field.js @@ -151,7 +151,6 @@ export class X2ManyField extends Component { return props; } - const mode = this.props.record.mode; // handle column_invisible modifiers const columns = archInfo.columns .map((col) => { @@ -172,24 +171,6 @@ export class X2ManyField extends Component { return col.buttons.length > 0; } return true; - }) - .filter((col) => { - // filter out oe_read_only/oe_edit_only columns - // note: remove this oe_read/edit_only logic when form view - // will always be in edit mode - if (col.type === "field") { - if (mode === "readonly") { - return !/\boe_edit_only\b/.test(col.className); - } else { - return !/\boe_read_only\b/.test(col.className); - } - } else if (col.type === "button_group") { - if (mode === "readonly") { - return col.buttons.some((btn) => !/\boe_edit_only\b/.test(btn.className)); - } else { - return col.buttons.some((btn) => !/\boe_read_only\b/.test(btn.className)); - } - } }); const editable = archInfo.editable || this.props.editable; diff --git a/addons/web/static/src/views/form/control_panel/form_control_panel.js b/addons/web/static/src/views/form/control_panel/form_control_panel.js new file mode 100644 index 00000000000..b4ec4b5ead3 --- /dev/null +++ b/addons/web/static/src/views/form/control_panel/form_control_panel.js @@ -0,0 +1,6 @@ +/** @odoo-module **/ + +import { ControlPanel } from "@web/search/control_panel/control_panel"; + +export class FormControlPanel extends ControlPanel {} +FormControlPanel.template = "web.FormControlPanel"; diff --git a/addons/web/static/src/views/form/control_panel/form_control_panel.scss b/addons/web/static/src/views/form/control_panel/form_control_panel.scss new file mode 100644 index 00000000000..8800ecb22ef --- /dev/null +++ b/addons/web/static/src/views/form/control_panel/form_control_panel.scss @@ -0,0 +1,6 @@ + +.o_form_view .o_control_panel { + .o_cp_action_menus { + border-right: 1px solid $border-color; + } +} diff --git a/addons/web/static/src/views/form/control_panel/form_control_panel.xml b/addons/web/static/src/views/form/control_panel/form_control_panel.xml new file mode 100644 index 00000000000..165afc5c3c5 --- /dev/null +++ b/addons/web/static/src/views/form/control_panel/form_control_panel.xml @@ -0,0 +1,31 @@ + + + + +
+
+
+ + + + + + + + + + + +
+
+ + + +
+
+
+
+ +
diff --git a/addons/web/static/src/views/form/form_compiler.js b/addons/web/static/src/views/form/form_compiler.js index 2709bcebfdc..92b80fa3d94 100644 --- a/addons/web/static/src/views/form/form_compiler.js +++ b/addons/web/static/src/views/form/form_compiler.js @@ -175,6 +175,13 @@ export class FormCompiler extends ViewCompiler { return hasContent ? buttonBox : null; } + compileButton(el, params) { + const compiled = super.compileButton(el, params); + compiled.setAttribute("disable", "props.disableViewButtons"); + compiled.setAttribute("enable", "props.enableViewButtons"); + return compiled; + } + /** * @override */ @@ -218,9 +225,11 @@ export class FormCompiler extends ViewCompiler { const displayClasses = sheetNode ? `d-flex {{ uiService.size < ${SIZES.XXL} ? "flex-column" : "flex-nowrap h-100" }}` : "d-block"; + const stateClasses = + "{{ props.record.isVirtual or props.record.isDirty ? 'o_form_dirty' : 'o_form_saved' }}"; const form = createElement("div", { "t-att-class": "props.class", - "t-attf-class": `{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} ${displayClasses}`, + "t-attf-class": `{{props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} ${displayClasses} ${stateClasses}`, }); if (!sheetNode) { for (const child of el.childNodes) { @@ -329,13 +338,6 @@ export class FormCompiler extends ViewCompiler { : `props.record.fields.${fieldName}.string`, fieldInfo: `props.archInfo.fieldNodes[${fieldId}]`, }; - // note: remove this oe_read/edit_only logic when form view - // will always be in edit mode - if (child.classList.contains("oe_read_only")) { - props.className = `'oe_read_only'`; - } else if (child.classList.contains("oe_edit_only")) { - props.className = `'oe_edit_only'`; - } mainSlot.setAttribute("props", objectToString(props)); mainSlot.setAttribute("Component", "constructor.components.FormLabel"); mainSlot.setAttribute("subType", "'item_component'"); @@ -422,9 +424,6 @@ export class FormCompiler extends ViewCompiler { const slot = createElement("t", { "t-set-slot": `button_${slotId++}`, isVisible: button.getAttribute("t-if") || true, - displayInReadOnly: - button.hasAttribute("className") && - button.getAttribute("className").includes("oe_read_only"), }); append(slot, button); append(statusBarButtons, slot); diff --git a/addons/web/static/src/views/form/form_controller.js b/addons/web/static/src/views/form/form_controller.js index 3e6a81c2360..c2e69a98dad 100644 --- a/addons/web/static/src/views/form/form_controller.js +++ b/addons/web/static/src/views/form/form_controller.js @@ -15,6 +15,8 @@ import { standardViewProps } from "@web/views/standard_view_props"; import { isX2Many } from "@web/views/utils"; import { useViewButtons } from "@web/views/view_button/view_button_hook"; import { useSetupView } from "@web/views/view_hook"; +import { FormStatusIndicator } from "./form_status_indicator/form_status_indicator"; +import { useFormErrorDialog } from "./form_error_dialog/form_error_dialog"; const { Component, onWillStart, useEffect, useRef, onRendered, useState, toRaw } = owl; @@ -94,7 +96,14 @@ export class FormController extends Component { this.user = useService("user"); this.viewService = useService("view"); this.ui = useService("ui"); + this.state = useState({ + isDisabled: false, + }); useBus(this.ui.bus, "resize", this.render); + useFormErrorDialog(async () => { + await this.discard(); + this.env.config.historyBack(); + }); this.archInfo = this.props.archInfo; const activeFields = this.archInfo.activeFields; @@ -103,6 +112,16 @@ export class FormController extends Component { const beforeLoadProm = new Promise((r) => { this.beforeLoadResolver = r; }); + + const { create, edit } = this.archInfo.activeActions; + this.canCreate = create && !this.props.preventCreate; + this.canEdit = edit && !this.props.preventEdit; + + let mode = this.props.mode || "edit"; + if (!this.canEdit) { + mode = "readonly"; + } + this.model = useModel( this.props.Model, { @@ -113,17 +132,13 @@ export class FormController extends Component { activeFields, viewMode: "form", rootType: "record", - mode: this.props.mode, + mode, beforeLoadProm, }, { ignoreUseSampleModel: true, } ); - const { create, edit } = this.archInfo.activeActions; - - this.canCreate = create && !this.props.preventCreate; - this.canEdit = edit && !this.props.preventEdit; this.cpButtonsRef = useRef("cpButtons"); @@ -235,9 +250,9 @@ export class FormController extends Component { !isInEdition && !rootRef.el.querySelector(".o_content").contains(document.activeElement) ) { - const elementToFocus = - rootRef.el.querySelector(".o_content button.btn-primary") || - rootRef.el.querySelector(".o_control_panel .o_form_button_edit"); + const elementToFocus = rootRef.el.querySelector( + ".o_content button.btn-primary" + ); if (elementToFocus) { elementToFocus.focus(); } @@ -301,11 +316,18 @@ export class FormController extends Component { key: "delete", description: this.env._t("Delete"), callback: () => this.deleteRecord(), + skipSave: true, }); } return Object.assign({}, this.props.info.actionMenus, { other: otherActionItems }); } + async beforeAction(item) { + if ((this.model.root.isDirty || this.model.root.isVirtual) && !item.skipSave) { + await this.model.root.save({ stayInEdition: true }); + } + } + async duplicateRecord() { await this.model.root.duplicate(); } @@ -325,16 +347,11 @@ export class FormController extends Component { } disableButtons() { - const btns = this.cpButtonsRef.el.querySelectorAll(".o_cp_buttons button"); - for (const btn of btns) { - btn.setAttribute("disabled", "1"); - } - return btns; + this.state.isDisabled = true; } - enableButtons(btns) { - for (const btn of btns) { - btn.removeAttribute("disabled"); - } + + enableButtons() { + this.state.isDisabled = false; } async edit() { @@ -342,12 +359,20 @@ export class FormController extends Component { } async create() { - this.disableButtons(); - await this.model.load({ resId: null }); + await this.model.root.askChanges(); // ensures that isDirty is correct + let canProceed = true; + if (this.model.root.isDirty) { + canProceed = await this.model.root.save({ stayInEdition: true }); + } + if (canProceed) { + this.disableButtons(); + await this.model.load({ resId: null }); + this.enableButtons(); + } } async save(params = {}) { - const disabledButtons = this.disableButtons(); + this.disableButtons(); const record = this.model.root; let saved = false; @@ -360,13 +385,17 @@ export class FormController extends Component { ...record.dirtyTranslatableFields, ]); } - - if (this.props.saveRecord) { - saved = await this.props.saveRecord(record, params); - } else { - saved = await record.save(); + try { + if (this.props.saveRecord) { + saved = await this.props.saveRecord(record, params); + } else { + saved = await record.save(); + } + } catch { + // if the save failed, we want to re-enable buttons + this.enableButtons(); } - this.enableButtons(disabledButtons); + this.enableButtons(); if (saved && this.props.onSave) { this.props.onSave(record); } @@ -424,7 +453,7 @@ export class FormController extends Component { } FormController.template = `web.FormView`; -FormController.components = { ActionMenus, Layout }; +FormController.components = { ActionMenus, FormStatusIndicator, Layout }; FormController.props = { ...standardViewProps, discardRecord: { type: Function, optional: true }, diff --git a/addons/web/static/src/views/form/form_controller.scss b/addons/web/static/src/views/form/form_controller.scss index 6a44a81253e..21a10b58558 100644 --- a/addons/web/static/src/views/form/form_controller.scss +++ b/addons/web/static/src/views/form/form_controller.scss @@ -201,7 +201,7 @@ } .oe_title { - max-width: map-get($container-max-widths, md) - (2 * $o-horizontal-padding); + max-width: 75%; } .o_field_x2many .o_list_table .o_handle_cell .o_row_handle { @@ -986,8 +986,15 @@ max-width: 300px; } } - @include media-breakpoint-down(md) { + .o_control_panel { + padding-right: 16px; + + > div.o_cp_top { + display: grid; + } + } + .o_cp_buttons { width: 100%; div, .o-kanban-button-new { diff --git a/addons/web/static/src/views/form/form_controller.xml b/addons/web/static/src/views/form/form_controller.xml index 89541836651..348f0c484ae 100644 --- a/addons/web/static/src/views/form/form_controller.xml +++ b/addons/web/static/src/views/form/form_controller.xml @@ -7,52 +7,37 @@ - + - - + + + onBeforeAction.bind="beforeAction" + /> - - - + + + - - - + + + - + @@ -68,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 8725b330733..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] ); } } 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/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/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 bc1163f45c0..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"); 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: ` -
- - - -