diff options
Diffstat (limited to 'addons/web/static/tests/fields')
4 files changed, 147 insertions, 17 deletions
diff --git a/addons/web/static/tests/fields/relational_fields/field_many2many_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2many_tests.js index d7d18dfb86b..ed5fcd7f985 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2many_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2many_tests.js @@ -1383,8 +1383,7 @@ QUnit.module('fields', {}, function () { await testUtils.dom.click($modal.find('.btn.btn-primary.o_select_button')); - var m2m_records = form.$('.o_field_many2many.o_field_widget.o_field_x2many.o_field_x2many_list .o_data_cell:not(.o_many2many_tags_cell)'); - assert.equal(m2m_records.length, 51, + assert.strictEqual(form.$('.o_data_row').length, 51, 'We should have added all the records present in the search view to the m2m field'); // the 50 in batch + 'gold' await testUtils.dom.click(form.$buttons.find('.btn.btn-primary.o_form_button_save')); @@ -1411,8 +1410,7 @@ QUnit.module('fields', {}, function () { await testUtils.dom.click($modal.find('.btn.btn-primary.o_select_button')); - pager_limit = form.$('.o_field_many2many.o_field_widget.o_field_x2many.o_field_x2many_list .o_pager_limit'); - assert.equal(pager_limit.text(), '51', + assert.strictEqual(form.$('.o_data_row').length, 51, 'We should have 51 records in the m2m field'); form.destroy(); diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js index 50e7ae2e6cb..e94797ff5f6 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js @@ -3293,13 +3293,16 @@ QUnit.module('fields', {}, function () { await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('.o_field_many2one[name="product_id"] input')); await testUtils.dom.click($('li.ui-menu-item a:contains(xpad)').trigger('mouseenter')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column when the product_id is set"); await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="product_id"] input'), '', 'keyup'); + await testUtils.owlCompatibilityNextTick(); assert.containsN(form, 'th:not(.o_list_record_remove_header)', 2, "should be 2 columns in the one2many when product_id is not set"); await testUtils.dom.click(form.$('.o_field_boolean[name="bar"] input')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column after the value change"); form.destroy(); @@ -3386,13 +3389,16 @@ QUnit.module('fields', {}, function () { await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('.o_field_many2one[name="product_id"] input')); await testUtils.dom.click($('li.ui-menu-item a:contains(xpad)').trigger('mouseenter')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column when the product_id is set"); await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="product_id"] input'), '', 'keyup'); + await testUtils.owlCompatibilityNextTick(); assert.containsN(form, 'th:not(.o_list_record_remove_header)', 2, "should be 2 columns in the one2many when product_id is not set"); await testUtils.dom.click(form.$('.o_field_boolean[name="bar"] input')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column after the value change"); form.destroy(); diff --git a/addons/web/static/tests/fields/relational_fields/field_one2many_tests.js b/addons/web/static/tests/fields/relational_fields/field_one2many_tests.js index 4d0055eb3b1..75346009aaf 100644 --- a/addons/web/static/tests/fields/relational_fields/field_one2many_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_one2many_tests.js @@ -3,6 +3,8 @@ odoo.define('web.field_one_to_many_tests', function (require) { var AbstractField = require('web.AbstractField'); var AbstractStorageService = require('web.AbstractStorageService'); +const ControlPanel = require('web.ControlPanel'); +const fieldRegistry = require('web.field_registry'); var FormView = require('web.FormView'); var KanbanRecord = require('web.KanbanRecord'); var ListRenderer = require('web.ListRenderer'); @@ -2557,7 +2559,7 @@ QUnit.module('fields', {}, function () { await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); await testUtils.dom.click(form.$el); - assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text().trim(), '1-3 / 5', + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text().trim(), '1-4 / 5', "pager should display the correct total"); form.destroy(); }); @@ -2869,7 +2871,7 @@ QUnit.module('fields', {}, function () { }); QUnit.test('editable one2many list, adding line when only one page', async function (assert) { - assert.expect(1); + assert.expect(5); this.data.partner.records[0].turtles = [1, 2, 3]; var form = await createView({ @@ -2892,6 +2894,15 @@ QUnit.module('fields', {}, function () { // the record currently being added should not count in the pager assert.containsNone(form, '.o_field_widget[name=turtles] .o_pager'); + // unselect the row + await testUtils.dom.click(form.$el); + assert.containsNone(form, '.o_selected_row'); + assert.containsNone(form, '.o_field_widget[name=turtles] .o_pager'); + + await testUtils.form.clickSave(form); + assert.containsOnce(form, '.o_field_widget[name=turtles] .o_pager'); + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text(), "1-3 / 4"); + form.destroy(); }); @@ -2993,13 +3004,13 @@ QUnit.module('fields', {}, function () { // see a confirm dialog await testUtils.dom.click(form.$('.o_field_widget[name=turtles] .o_pager_next')); - assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text().trim(), '1-3 / 4', + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text().trim(), '1-4 / 5', "pager should still display the correct total"); // click on cancel await testUtils.dom.click($('.modal .modal-footer .btn-secondary')); - assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text().trim(), '1-3 / 4', + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_pager').text().trim(), '1-4 / 5', "pager should again display the correct total"); assert.containsOnce(form, '.o_field_one2many input.o_field_invalid', "there should be an invalid input in the one2many"); @@ -3007,7 +3018,7 @@ QUnit.module('fields', {}, function () { }); QUnit.test('editable one2many list, adding, discarding, and pager', async function (assert) { - assert.expect(2); + assert.expect(4); this.data.partner.records[0].turtles = [1]; @@ -3025,15 +3036,15 @@ QUnit.module('fields', {}, function () { res_id: 1, }); - // add a 4 records record (to make the pager appear) + // add 4 records (to have more records than the limit) await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - // go on next page - await testUtils.dom.click(form.$('.o_field_widget[name=turtles] .o_pager_next')); + assert.containsN(form, 'tr.o_data_row', 5); + assert.containsNone(form, '.o_field_widget[name=turtles] .o_pager'); // discard await testUtils.form.clickDiscard(form); @@ -4541,6 +4552,12 @@ QUnit.module('fields', {}, function () { await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('tbody td.o_field_x2many_list_row_add a')); + // use of owlCompatibilityNextTick because we have an x2many field with a boolean field + // (written in owl), so when we add a line, we sequentially render the list itself + // (including the boolean field), so we have to wait for the next animation frame, and + // then we render the control panel (also in owl), so we have to wait again for the + // next animation frame + await testUtils.owlCompatibilityNextTick(); form.destroy(); }); @@ -4571,6 +4588,12 @@ QUnit.module('fields', {}, function () { }, }); await testUtils.dom.click(form.$('tbody td.o_field_x2many_list_row_add a')); + // use of owlCompatibilityNextTick because we have an x2many field with a boolean field + // (written in owl), so when we add a line, we sequentially render the list itself + // (including the boolean field), so we have to wait for the next animation frame, and + // then we render the control panel (also in owl), so we have to wait again for the + // next animation frame + await testUtils.owlCompatibilityNextTick(); assert.verifySteps(['onchange', 'onchange']); form.destroy(); }); @@ -6071,6 +6094,7 @@ QUnit.module('fields', {}, function () { // add a new row (which is invalid at first) await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.owlCompatibilityNextTick(); assert.strictEqual(form.$('.o_field_widget[name="int_field"]').val(), "0", "int_field should still be 0 (no onchange should have been done yet)"); assert.verifySteps(['load_views', 'read', 'onchange']); @@ -6693,13 +6717,14 @@ QUnit.module('fields', {}, function () { await testUtils.form.clickEdit(form); + // add a new record page 1 (this increases the limit to 4) await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); await testUtils.fields.editInput(form.$('.o_data_row input[name="turtle_foo"]'), 'rainbow dash'); - await testUtils.dom.click(form.$('.o_x2m_control_panel .o_pager_next')); - await testUtils.dom.click(form.$('.o_x2m_control_panel .o_pager_next')); + await testUtils.dom.click(form.$('.o_x2m_control_panel .o_pager_next')); // page 2: 4 records + await testUtils.dom.click(form.$('.o_x2m_control_panel .o_pager_next')); // page 3: 2 records - assert.containsN(form, 'tr.o_data_row', 3, - "should have 3 data rows on the current page"); + assert.containsN(form, 'tr.o_data_row', 2, + "should have 2 data rows on the current page"); form.destroy(); }); @@ -6885,7 +6910,11 @@ QUnit.module('fields', {}, function () { }); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); - + // use of owlCompatibilityNextTick because we have a boolean field (owl) inside the + // x2many, so an update of the x2many requires to wait for 2 animation frames: one + // for the list to be re-rendered (with the boolean field) and one for the control + // panel. + await testUtils.owlCompatibilityNextTick(); await testUtils.dom.click(form.$('.o_field_widget[name=bar] input')); assert.notOk(form.$('.o_field_widget[name=bar] input').prop('checked'), "the checkbox should be unticked"); @@ -8430,13 +8459,16 @@ QUnit.module('fields', {}, function () { await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('.o_field_many2one[name="product_id"] input')); await testUtils.dom.click($('li.ui-menu-item a:contains(xpad)').trigger('mouseenter')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column when the product_id is set"); await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="product_id"] input'), '', 'keyup'); + await testUtils.owlCompatibilityNextTick(); assert.containsN(form, 'th:not(.o_list_record_remove_header)', 2, "should be 2 columns in the one2many when product_id is not set"); await testUtils.dom.click(form.$('.o_field_boolean[name="bar"] input')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column after the value change"); form.destroy(); @@ -8611,13 +8643,16 @@ QUnit.module('fields', {}, function () { await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('.o_field_many2one[name="product_id"] input')); await testUtils.dom.click($('li.ui-menu-item a:contains(xpad)').trigger('mouseenter')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column when the product_id is set"); await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="product_id"] input'), '', 'keyup'); + await testUtils.owlCompatibilityNextTick(); assert.containsN(form, 'th:not(.o_list_record_remove_header)', 2, "should be 2 columns in the one2many when product_id is not set"); await testUtils.dom.click(form.$('.o_field_boolean[name="bar"] input')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column after the value change"); form.destroy(); @@ -9110,6 +9145,12 @@ QUnit.module('fields', {}, function () { // resolve the name_create to trigger the onchange, and the reset of p prom.resolve(); await testUtils.nextTick(); + // use of owlCompatibilityNextTick because we have two sequential updates of the + // fieldX2Many: one because of the onchange, and one because of the click on add a line. + // As an update requires an update of the ControlPanel, which is an Owl Component, and + // waits for it, we need to wait for two animation frames before seeing the new line in + // the DOM + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, '.o_data_row'); assert.hasClass(form.$('.o_data_row'), 'o_selected_row'); @@ -9331,6 +9372,74 @@ QUnit.module('fields', {}, function () { form.destroy(); }); + + QUnit.test('mounted is called only once for x2many control panel', async function (assert) { + // This test could be removed as soon as the field widgets will be converted in owl. + // It comes with a fix for a bug that occurred because in some circonstances, 'mounted' + // is called twice for the x2many control panel. + // Specifically, this occurs when there is 'pad' widget in the form view, because this + // widget does a 'setValue' in its 'start', which thus resets the field x2many. + assert.expect(5); + + const PadLikeWidget = fieldRegistry.get('char').extend({ + start() { + this._setValue("some value"); + } + }); + fieldRegistry.add('pad_like', PadLikeWidget); + + let resolveCP; + const prom = new Promise(r => { + resolveCP = r; + }); + ControlPanel.patch('cp_patch_mock', T => + class extends T { + constructor() { + super(...arguments); + owl.hooks.onMounted(() => { + assert.step('mounted'); + }); + owl.hooks.onWillUnmount(() => { + assert.step('willUnmount'); + }); + } + async update() { + // the issue is a race condition, so we manually delay the update to turn it deterministic + await prom; + super.update(...arguments); + } + } + ); + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ` + <form> + <field name="foo" widget="pad_like"/> + <field name="p"> + <tree><field name="display_name"/></tree> + </field> + </form>`, + viewOptions: { + withControlPanel: false, // s.t. there is only one CP: the one of the x2many + }, + }); + + assert.verifySteps(['mounted']); + + resolveCP(); + await testUtils.nextTick(); + + assert.verifySteps([]); + + ControlPanel.unpatch('cp_patch_mock'); + delete fieldRegistry.map.pad_like; + form.destroy(); + + assert.verifySteps(["willUnmount"]); + }); }); }); }); diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 6ef0e402562..f93e7c35ace 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -474,9 +474,11 @@ QUnit.module('relational_fields', { await testUtils.form.clickEdit(form); // add a line (virtual record) await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.owlCompatibilityNextTick(); await testUtils.fields.editInput(form.$('.o_input'), 'pi'); // delete the line above it await testUtils.dom.click(form.$('.o_list_record_remove').first()); + await testUtils.owlCompatibilityNextTick(); // the next line should be displayed below the newly added one assert.strictEqual(form.$('.o_data_row').length, 2, "should have 2 records"); assert.strictEqual(form.$('.o_data_row .o_data_cell:first-child').text(), 'pikawa', @@ -2535,6 +2537,7 @@ QUnit.module('relational_fields', { }); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + await testUtils.owlCompatibilityNextTick(); await testUtils.fields.many2one.searchAndClickItem('product_id', {selector: '.modal', search: 'new record'}); @@ -3070,13 +3073,16 @@ QUnit.module('relational_fields', { await testUtils.form.clickEdit(form); await testUtils.fields.many2one.clickOpenDropdown("product_id"); await testUtils.fields.many2one.clickHighlightedItem("product_id"); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column when the product_id is set"); await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="product_id"] input'), '', 'keyup'); + await testUtils.owlCompatibilityNextTick(); assert.containsN(form, 'th:not(.o_list_record_remove_header)', 2, "should be 2 columns in the one2many when product_id is not set"); await testUtils.dom.click(form.$('.o_field_boolean[name="bar"] input')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column after the value change"); form.destroy(); @@ -3164,13 +3170,16 @@ QUnit.module('relational_fields', { await testUtils.form.clickEdit(form); await testUtils.dom.click(form.$('.o_field_many2one[name="product_id"] input')); await testUtils.fields.many2one.clickHighlightedItem("product_id"); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column when the product_id is set"); await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="product_id"] input'), '', 'keyup'); + await testUtils.owlCompatibilityNextTick(); assert.containsN(form, 'th:not(.o_list_record_remove_header)', 2, "should be 2 columns in the one2many when product_id is not set"); await testUtils.dom.click(form.$('.o_field_boolean[name="bar"] input')); + await testUtils.owlCompatibilityNextTick(); assert.containsOnce(form, 'th:not(.o_list_record_remove_header)', "should be 1 column after the value change"); form.destroy(); @@ -3230,6 +3239,13 @@ QUnit.module('relational_fields', { assert.containsN(form.$('.o_field_one2many'), 'div.o_optional_columns div.dropdown-item:visible', 2, "dropdown is still open"); await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')); + // use of owlCompatibilityNextTick because the x2many field is reset, meaning that + // 1) its list renderer is updated (updateState is called): this is async and as it + // contains a FieldBoolean, which is written in Owl, it completes in the nextAnimationFrame + // 2) when this is done, the control panel is updated: as it is written in owl, this is + // done in the nextAnimationFrame + // -> we need to wait for 2 nextAnimationFrame to ensure that everything is fine + await testUtils.owlCompatibilityNextTick(); assert.containsN(form.$('.o_field_one2many'), 'div.o_optional_columns div.dropdown-item:visible', 0, "dropdown is closed"); var $selectedRow = form.$('.o_field_one2many tr.o_selected_row'); @@ -3329,6 +3345,7 @@ QUnit.module('relational_fields', { which: $.ui.keyCode.TAB, keyCode: $.ui.keyCode.TAB, })); + await testUtils.owlCompatibilityNextTick(); await testUtils.dom.click(document.activeElement); assert.strictEqual(assert.strictEqual(form.$el.find('input[name="turtle_foo"]')[0], document.activeElement, |
