diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-10-09 02:08:17 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-10-09 02:08:17 +0200 |
| commit | d74b4518052066446e2a1975a91ce53c745ca75a (patch) | |
| tree | c08180f88a512d15e37fbec628931f02b3825f65 /addons/web/static/tests | |
| parent | ec162dd0cb5622ab6e56db1a63dab5f531a96f4a (diff) | |
| parent | f4105eb9c7010f8693e5d2fd20c0bd9d9cc2e483 (diff) | |
[MERGE] forward port branch 13.0 up to f4105eb9c70
Diffstat (limited to 'addons/web/static/tests')
15 files changed, 1322 insertions, 286 deletions
diff --git a/addons/web/static/tests/chrome/action_manager_tests.js b/addons/web/static/tests/chrome/action_manager_tests.js index 002a52f572f..b878e642b4f 100644 --- a/addons/web/static/tests/chrome/action_manager_tests.js +++ b/addons/web/static/tests/chrome/action_manager_tests.js @@ -4227,6 +4227,45 @@ QUnit.module('ActionManager', { actionManager.destroy(); }); + + QUnit.test('Call twice clearUncommittedChanges in a row does not display twice the discard warning', async function (assert) { + assert.expect(4); + + var actionManager = await createActionManager({ + actions: this.actions, + archs: this.archs, + data: this.data, + intercepts: { + clear_uncommitted_changes: function () { + actionManager.clearUncommittedChanges(); + }, + }, + }); + + // execute an action and edit existing record + await actionManager.doAction(3); + + await testUtils.dom.click(actionManager.$('.o_list_view .o_data_row:first')); + assert.containsOnce(actionManager, '.o_form_view.o_form_readonly'); + + await testUtils.dom.click($('.o_control_panel .o_form_button_edit')); + assert.containsOnce(actionManager, '.o_form_view.o_form_editable'); + + await testUtils.fields.editInput(actionManager.$('input[name=foo]'), 'val'); + actionManager.trigger_up('clear_uncommitted_changes'); + await testUtils.nextTick(); + + assert.containsOnce($('body'), '.modal'); // confirm discard dialog + // confirm discard changes + await testUtils.dom.click($('.modal .modal-footer .btn-primary')); + + actionManager.trigger_up('clear_uncommitted_changes'); + await testUtils.nextTick(); + + assert.containsNone($('body'), '.modal'); + + actionManager.destroy(); + }); }); }); diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index aa86ff925e4..c4bfc8a4341 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -511,6 +511,42 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('float field in list view no widget', async function (assert) { + assert.expect(5); + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'<form string="Partners">' + + '<sheet>' + + '<field name="qux" digits="[5,3]"/>' + + '</sheet>' + + '</form>', + res_id: 2, + }); + + assert.doesNotHaveClass(form.$('.o_field_widget'), 'o_field_empty', + 'Float field should be considered set for value 0.'); + assert.strictEqual(form.$('.o_field_widget').first().text(), '0.000', + 'The value should be displayed properly.'); + + await testUtils.form.clickEdit(form); + assert.strictEqual(form.$('input[name=qux]').val(), '0.000', + 'The value should be rendered with correct precision.'); + + await testUtils.fields.editInput(form.$('input[name=qux]'), '108.2458938598598'); + assert.strictEqual(form.$('input[name=qux]').val(), '108.2458938598598', + 'The value should not be formated yet.'); + + await testUtils.fields.editInput(form.$('input[name=qux]'), '18.8958938598598'); + await testUtils.form.clickSave(form); + assert.strictEqual(form.$('.o_field_widget').first().text(), '18.896', + 'The new value should be rounded properly.'); + + form.destroy(); + }); + QUnit.test('float field in form view', async function (assert) { assert.expect(5); @@ -1073,7 +1109,7 @@ QUnit.module('basic_fields', { }); QUnit.test('char field translatable', async function (assert) { - assert.expect(4); + assert.expect(12); this.data.partner.fields.foo.translate = true; @@ -1092,6 +1128,9 @@ QUnit.module('basic_fields', { '</sheet>' + '</form>', res_id: 1, + session: { + user_context: {lang: 'en_US'}, + }, mockRPC: function (route, args) { if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route'); @@ -1101,7 +1140,18 @@ QUnit.module('basic_fields', { }); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return Promise.resolve([["en_US"], ["fr_BE"]]); + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); + } + if (args.method === "search_read" && args.model == "ir.translation") { + return Promise.resolve([ + {lang: 'en_US', src: 'yop', value: 'yop', id: 42}, + {lang: 'fr_BE', src: 'yop', value: 'valeur français', id: 43} + ]); + } + if (args.method === "write" && args.model == "ir.translation") { + assert.deepEqual(args.args[1], {value: "english value"}, + "the new translation value should be written"); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -1114,6 +1164,117 @@ QUnit.module('basic_fields', { await testUtils.nextTick(); assert.containsOnce($(document), '.modal', 'a translate modal should be visible'); + assert.containsN($('.modal .o_translation_dialog'), '.translation', 2, + 'two rows should be visible'); + + var $enField = $('.modal .o_translation_dialog .translation:first() input'); + assert.strictEqual($enField.val(), 'yop', + 'English translation should be filled'); + assert.strictEqual($('.modal .o_translation_dialog .translation:last() input').val(), 'valeur français', + 'French translation should be filled'); + + await testUtils.fields.editInput($enField, "english value"); + await testUtils.dom.click($('.modal button.btn-primary')); // save + await testUtils.nextTick(); + + var $foo = form.$('input[type="text"].o_field_char'); + assert.strictEqual($foo.val(), "english value", + "the new translation was not transfered to modified record"); + + await testUtils.fields.editInput($foo, "new english value"); + + await testUtils.dom.click($button); + await testUtils.nextTick(); + + assert.strictEqual($('.modal .o_translation_dialog .translation:first() input').val(), 'new english value', + 'Modified value should be used instead of translation'); + assert.strictEqual($('.modal .o_translation_dialog .translation:last() input').val(), 'valeur français', + 'French translation should be filled'); + + form.destroy(); + + _t.database.multi_lang = multiLang; + }); + + QUnit.test('html field translatable', async function (assert) { + assert.expect(6); + + this.data.partner.fields.foo.translate = true; + + var multiLang = _t.database.multi_lang; + _t.database.multi_lang = true; + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<sheet>' + + '<group>' + + '<field name="foo"/>' + + '</group>' + + '</sheet>' + + '</form>', + res_id: 1, + session: { + user_context: {lang: 'en_US'}, + }, + mockRPC: function (route, args) { + if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { + assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route'); + return Promise.resolve({ + domain: [], + context: { + search_default_name: 'partner,foo', + translation_type: 'char', + translation_show_src: true, + }, + }); + } + if (route === "/web/dataset/call_kw/res.lang/get_installed") { + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); + } + if (args.method === "search_read" && args.model == "ir.translation") { + return Promise.resolve([ + {lang: 'en_US', src: 'first paragraph', value: 'first paragraph', id: 42}, + {lang: 'en_US', src: 'second paragraph', value: 'second paragraph', id: 43}, + {lang: 'fr_BE', src: 'first paragraph', value: 'premier paragraphe', id: 44}, + {lang: 'fr_BE', src: 'second paragraph', value: 'deuxième paragraphe', id: 45}, + ]); + } + if (args.method === "write" && args.model == "ir.translation") { + assert.deepEqual(args.args[1], {value: "first paragraph modified"}, + "Wrong update on translation"); + return Promise.resolve(); + } + return this._super.apply(this, arguments); + }, + }); + await testUtils.form.clickEdit(form); + var $foo = form.$('input[type="text"].o_field_char'); + + // this will not affect the translate_fields effect until the record is + // saved but is set for consistency of the test + await testUtils.fields.editInput($foo, "<p>first paragraph</p><p>second paragraph</p>"); + + var $button = form.$('input[type="text"].o_field_char + .o_field_translate'); + await testUtils.dom.click($button); + await testUtils.nextTick(); + + assert.containsOnce($(document), '.modal', 'a translate modal should be visible'); + assert.containsN($('.modal .o_translation_dialog'), '.translation', 4, + 'four rows should be visible'); + + var $enField = $('.modal .o_translation_dialog .translation:first() input'); + assert.strictEqual($enField.val(), 'first paragraph', + 'first part of english translation should be filled'); + + await testUtils.fields.editInput($enField, "first paragraph modified"); + await testUtils.dom.click($('.modal button.btn-primary')); // save + await testUtils.nextTick(); + + assert.strictEqual($foo.val(), "<p>first paragraph</p><p>second paragraph</p>", + "the new partial translation should not be transfered"); form.destroy(); @@ -2636,7 +2797,7 @@ QUnit.module('basic_fields', { QUnit.module('FieldDateRange'); QUnit.test('Datetime field', async function (assert) { - assert.expect(19); + assert.expect(20); this.data.partner.fields.datetime_end = {string: 'Datetime End', type: 'datetime'}; this.data.partner.records[0].datetime_end = '2017-03-13 00:00:00'; @@ -2685,6 +2846,8 @@ QUnit.module('basic_fields', { "active end date hour should be '5' in date range picker"); assert.strictEqual($('.daterangepicker:first .drp-calendar.right .minuteselect').val(), '30', "active end date minute should be '30' in date range picker"); + assert.containsN($('.daterangepicker:first .drp-calendar.left .minuteselect'), 'option', 12, + "minute selection should contain 12 options (1 for each 5 minutes)"); // Close picker await testUtils.dom.click($('.daterangepicker:first .cancelBtn')); @@ -3548,7 +3711,7 @@ QUnit.module('basic_fields', { View: ListView, model: 'partner', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree multi_edit="1">' + '<field name="datetime"/>' + '</tree>', translateParameters: { // Avoid issues due to localization formats @@ -6440,7 +6603,7 @@ QUnit.module('basic_fields', { }); QUnit.test('Field Color: default widget state', async function (assert) { - assert.expect(3); + assert.expect(4); var form = await createView({ View: FormView, @@ -6465,6 +6628,46 @@ QUnit.module('basic_fields', { await testUtils.dom.click($('.modal .btn:contains("Discard")')); + assert.strictEqual(document.activeElement, form.$('.o_field_color')[0], + "Focus should go back to the color field"); + + form.destroy(); + }); + + QUnit.test('Field Color: behaviour in different views', async function (assert) { + assert.expect(2); + + this.data.partner.records[0].p = [4, 2]; + this.data.partner.records[1].hex_color = '#ff0080'; + + const form = await createView({ + arch: '<form>' + + '<field name="hex_color" widget="color"/>' + + '<field name="p">' + + '<tree editable="top">' + + '<field name="display_name"/>' + + '<field name="hex_color" widget="color"/>' + + '</tree>' + + '</field>' + + '</form>', + data: this.data, + model: 'partner', + res_id: 1, + View: FormView, + }); + + await testUtils.dom.click(form.$('.o_field_color:first()')); + assert.containsNone($(document.body), '.modal', + "Color field in readonly shouldn't be editable"); + + const rowInitialHeight = form.$('.o_data_row:first()').height(); + + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_data_row:first() .o_data_cell:first()')); + + assert.strictEqual(rowInitialHeight, form.$('.o_data_row:first()').height(), + "Color field shouldn't change the color height when edited"); + form.destroy(); }); diff --git a/addons/web/static/tests/fields/field_utils_tests.js b/addons/web/static/tests/fields/field_utils_tests.js index c4281b38c87..e0b9564d13c 100644 --- a/addons/web/static/tests/fields/field_utils_tests.js +++ b/addons/web/static/tests/fields/field_utils_tests.js @@ -353,5 +353,39 @@ QUnit.test('parse datetime', function (assert) { core._t.database.parameters = originalParameters; }); +QUnit.test('parse date without separator', function (assert) { + assert.expect(8); + + var originalParameters = _.clone(core._t.database.parameters); + + _.extend(core._t.database.parameters, {date_format: '%d.%m/%Y'}); + var dateFormat = "DD.MM/YYYY"; + + assert.throws(function () {fieldUtils.parse.date("1197")}, /is not a correct/, "Wrongly formated dates should be invalid"); + assert.throws(function () {fieldUtils.parse.date("0131")}, /is not a correct/, "Wrongly formated dates should be invalid"); + assert.throws(function () {fieldUtils.parse.date("970131")}, /is not a correct/, "Wrongly formated dates should be invalid"); + assert.equal(fieldUtils.parse.date("3101").format(dateFormat), "31.01/" + moment.utc().year()); + assert.equal(fieldUtils.parse.date("31.01").format(dateFormat), "31.01/" + moment.utc().year()); + assert.equal(fieldUtils.parse.date("310197").format(dateFormat), "31.01/1997"); + assert.equal(fieldUtils.parse.date("310117").format(dateFormat), "31.01/2017"); + assert.equal(fieldUtils.parse.date("31011985").format(dateFormat), "31.01/1985"); + + core._t.database.parameters = originalParameters; +}); + +QUnit.test('parse datetime without separator', function (assert) { + assert.expect(3); + + var originalParameters = _.clone(core._t.database.parameters); + + _.extend(core._t.database.parameters, {date_format: '%d.%m/%Y', time_format: '%H:%M/%S'}); + var dateTimeFormat = "DD.MM/YYYY HH:mm/ss"; + + assert.equal(fieldUtils.parse.datetime("3101198508").format(dateTimeFormat), "31.01/1985 08:00/00"); + assert.equal(fieldUtils.parse.datetime("310119850833").format(dateTimeFormat), "31.01/1985 08:33/00"); + assert.equal(fieldUtils.parse.datetime("31/01/1985 08").format(dateTimeFormat), "31.01/1985 08:00/00"); + + core._t.database.parameters = originalParameters; +}); }); }); 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 4d5b5d8aa89..081bd4e3598 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 @@ -557,7 +557,7 @@ QUnit.module('fields', {}, function () { // save the modal and make sure an onchange is triggered await testUtils.dom.click($('.modal .modal-footer .btn-primary').first()); - assert.verifySteps(['read', 'get_formview_id', 'load_views', 'read', 'write', 'onchange', 'read']); + assert.verifySteps(['read', 'get_formview_id', 'load_views', 'read', 'write', 'read', 'onchange']); // save the main record, and check that no extra rpcs are done (record // is not dirty, only a related record was modified) @@ -571,7 +571,7 @@ QUnit.module('fields', {}, function () { const list = await createView({ arch: ` - <tree editable="top"> + <tree multi_edit="1"> <field name="trululu"/> </tree>`, data: this.data, 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 35b4be9f25e..60f3398c88c 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 @@ -8705,6 +8705,42 @@ QUnit.module('fields', {}, function () { form.destroy(); }); + + QUnit.test('editable one2many list with oe_read_only button', async function (assert) { + assert.expect(9); + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: `<form> + <field name="turtles"> + <tree editable="bottom"> + <field name="turtle_foo"/> + <button name="do_it" type="object" class="oe_read_only"/> + </tree> + </field> + </form>`, + res_id: 1, + }); + + assert.containsN(form, '.o_list_view thead th:visible', 2); + assert.containsN(form, '.o_list_view tbody .o_data_row td:visible', 2); + assert.containsN(form, '.o_list_view tfoot td:visible', 2); + assert.containsNone(form, '.o_list_record_remove_header'); + + await testUtils.form.clickEdit(form); + + // should have two visible columns in edit: foo + trash + assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); + assert.containsN(form, '.o_list_view thead th:visible', 2); + assert.containsN(form, '.o_list_view tbody .o_data_row td:visible', 2); + assert.containsN(form, '.o_list_view tfoot td:visible', 2); + assert.containsOnce(form, '.o_list_record_remove_header'); + + form.destroy(); + }); + }); }); }); diff --git a/addons/web/static/tests/fields/relational_fields_mobile_tests.js b/addons/web/static/tests/fields/relational_fields_mobile_tests.js new file mode 100644 index 00000000000..cf46aa2c1cc --- /dev/null +++ b/addons/web/static/tests/fields/relational_fields_mobile_tests.js @@ -0,0 +1,66 @@ +odoo.define("web.relational_fields_mobile_tests", function (require) { +"use strict"; + +const FormView = require("web.FormView"); +const testUtils = require("web.test_utils"); + +QUnit.module("fields", {}, function () { + QUnit.module("relational_fields", { + beforeEach() { + this.data = { + partner: { + fields: { + display_name: { string: "Displayed name", type: "char" }, + p: {string: "one2many field", type: "one2many", relation: "partner", relation_field: "trululu"}, + trululu: {string: "Trululu", type: "many2one", relation: "partner"}, + }, + records: [{ + id: 1, + display_name: "first record", + p: [2, 4], + trululu: 4, + }, { + id: 2, + display_name: "second record", + p: [], + trululu: 1, + }, { + id: 4, + display_name: "aaa", + }], + }, + }; + }, + }, function () { + QUnit.module("FieldOne2Many"); + + QUnit.test("one2many on mobile: display list if present without kanban view", async function (assert) { + assert.expect(2); + + const form = await testUtils.createView({ + View: FormView, + model: "partner", + data: this.data, + arch: ` + <form> + <field name="p"> + <tree> + <field name="display_name"/> + </tree> + </field> + </form> + `, + res_id: 1, + }); + + await testUtils.form.clickEdit(form); + assert.containsOnce(form, ".o_field_x2many_list", + "should display one2many's list"); + assert.containsN(form, ".o_field_x2many_list .o_data_row", 2, + "should display 2 records in one2many's list"); + + form.destroy(); + }); + }); +}); +}); diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 24ce3e892c8..751eb1cb137 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -2600,6 +2600,49 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('interact with reference field changed by onchange', async function (assert) { + assert.expect(2); + + this.data.partner.onchanges = { + bar: function (obj) { + if (!obj.bar) { + obj.reference = 'partner,1'; + } + }, + }; + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: `<form> + <field name="bar"/> + <field name="reference"/> + </form>`, + mockRPC: function (route, args) { + if (args.method === 'create') { + assert.deepEqual(args.args[0], { + bar: false, + reference: 'partner,4', + }); + } + return this._super.apply(this, arguments); + }, + }); + + // trigger the onchange to set a value for the reference field + await testUtils.dom.click(form.$('.o_field_boolean input')); + + assert.strictEqual(form.$('.o_field_widget[name=reference] select').val(), 'partner'); + + // manually update reference field + await testUtils.fields.many2one.searchAndClickItem('reference', {search: 'aaa'}); + + // save + await testUtils.form.clickSave(form); + + form.destroy(); + }); + QUnit.test('default_get and onchange with a reference field', async function (assert) { assert.expect(8); diff --git a/addons/web/static/tests/helpers/test_utils_create.js b/addons/web/static/tests/helpers/test_utils_create.js index 892a946bba0..ee23648a49b 100644 --- a/addons/web/static/tests/helpers/test_utils_create.js +++ b/addons/web/static/tests/helpers/test_utils_create.js @@ -288,6 +288,7 @@ function createControlPanel(params) { } var viewOptions = _.defaults({}, params.viewOptions, { + context: params.context, modelName: params.model, searchMenuTypes: params.searchMenuTypes, viewInfo: params.viewInfo, diff --git a/addons/web/static/tests/helpers/test_utils_dom.js b/addons/web/static/tests/helpers/test_utils_dom.js index 87013bd4696..a4315002cab 100644 --- a/addons/web/static/tests/helpers/test_utils_dom.js +++ b/addons/web/static/tests/helpers/test_utils_dom.js @@ -40,6 +40,7 @@ var concurrency = require('web.concurrency'); * @param {jQuery|HTMLElement} [options.mousedownTarget=undefined] target of the mousedown event * @param {jQuery|HTMLElement} [options.mousemoveTarget=undefined] target of the mousemove event * @param {jQuery|HTMLElement} [options.mouseupTarget=undefined] target of the mouseup event + * @param {jQuery|HTMLElement} [options.ctrlKey=undefined] if the ctrl key should be considered pressed at the time of mouseup * @returns {Promise} */ function dragAndDrop($el, $to, options) { @@ -102,7 +103,8 @@ function dragAndDrop($el, $to, options) { triggerEvent(options.mouseupTarget || el || $el, 'mouseup', { which: 1, pageX: toOffset.left, - pageY: toOffset.top + pageY: toOffset.top, + ctrlKey: options.ctrlKey, }); if (options.withTrailingClick) { triggerEvent(options.mouseupTarget || el || $el, 'click'); diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 86e31103f5a..70ca4c521f4 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -27,6 +27,7 @@ CalendarRenderer.include({ var createCalendarView = testUtils.createCalendarView; +// 2016-12-12 08:00:00 var initialDate = new Date(2016, 11, 12, 8, 0, 0); initialDate = new Date(initialDate.getTime() - initialDate.getTimezoneOffset()*60*1000); @@ -628,7 +629,7 @@ QUnit.module('Views', { QUnit.test('default week start (US)', function (assert) { // if not given any option, default week start is on Sunday - assert.expect(1); + assert.expect(3); var done = assert.async(); createCalendarView({ @@ -646,9 +647,21 @@ QUnit.module('Views', { viewOptions: { initialDate: initialDate, }, + mockRPC: function (route, args) { + if (args.method === 'search_read' && args.model === 'event') { + assert.deepEqual(args.kwargs.domain, [ + ["start","<=","2016-12-17 23:59:59"], + ["stop",">=","2016-12-11 00:00:00"] + ], + 'The domain to search events in should be correct'); + } + return this._super.apply(this, arguments); + } }).then(function (calendar) { assert.strictEqual(calendar.$('.fc-day-header').first().text(), "Sun 11", "The first day of the week should be Sunday"); + assert.strictEqual(calendar.$('.fc-day-header').last().text(), "Sat 17", + "The last day of the week should be Saturday"); calendar.destroy(); done(); }); @@ -656,7 +669,7 @@ QUnit.module('Views', { QUnit.test('European week start', function (assert) { // the week start depends on the locale - assert.expect(1); + assert.expect(3); var done = assert.async(); createCalendarView({ @@ -677,9 +690,21 @@ QUnit.module('Views', { translateParameters: { week_start: 1, }, + mockRPC: function (route, args) { + if (args.method === 'search_read' && args.model === 'event') { + assert.deepEqual(args.kwargs.domain, [ + ["start","<=","2016-12-18 23:59:59"], + ["stop",">=","2016-12-12 00:00:00"] + ], + 'The domain to search events in should be correct'); + } + return this._super.apply(this, arguments); + } }).then(function (calendar) { assert.strictEqual(calendar.$('.fc-day-header').first().text(), "Mon 12", "The first day of the week should be Monday"); + assert.strictEqual(calendar.$('.fc-day-header').last().text(), "Sun 18", + "The last day of the week should be Sunday"); calendar.destroy(); done(); }); @@ -2706,6 +2731,122 @@ QUnit.module('Views', { calendar.destroy(); }); + QUnit.test('default week start (US) month mode', async function (assert) { + // if not given any option, default week start is on Sunday + assert.expect(8); + + // 2019-09-12 08:00:00 + var initDate = new Date(2019, 8, 12, 8, 0, 0); + initDate = new Date(initDate.getTime() - initDate.getTimezoneOffset()*60*1000); + + var calendar = await createCalendarView({ + View: CalendarView, + model: 'event', + data: this.data, + arch: + '<calendar class="o_calendar_test" '+ + 'date_start="start" '+ + 'date_stop="stop" '+ + 'mode="month">'+ + '</calendar>', + archs: archs, + + viewOptions: { + initialDate: initDate, + }, + mockRPC: function (route, args) { + if (args.method === 'search_read' && args.model === 'event') { + assert.deepEqual(args.kwargs.domain, [ + ["start","<=","2019-10-12 23:59:59"], + ["stop",">=","2019-09-01 00:00:00"] + ], + 'The domain to search events in should be correct'); + } + return this._super.apply(this, arguments); + } + }); + + assert.strictEqual(calendar.$('.fc-day-header').first().text(), "Sunday", + "The first day of the week should be Sunday"); + assert.strictEqual(calendar.$('.fc-day-header').last().text(), "Saturday", + "The last day of the week should be Saturday"); + + var $firstDay = calendar.$('.fc-day-top').first(); + + assert.strictEqual($firstDay.find('.fc-week-number').text(), "36", + "The number of the week should be correct"); + assert.strictEqual($firstDay.find('.fc-day-number').text(), "1", + "The first day of the week should be 2019-09-01"); + assert.strictEqual($firstDay.data('date'), "2019-09-01", + "The first day of the week should be 2019-09-01"); + + var $lastDay = calendar.$('.fc-day-top').last(); + assert.strictEqual($lastDay.text(), "12", + "The last day of the week should be 2019-10-12"); + assert.strictEqual($lastDay.data('date'), "2019-10-12", + "The last day of the week should be 2019-10-12"); + + calendar.destroy(); + }); + + QUnit.test('European week start month mode', async function (assert) { + assert.expect(8); + + // 2019-09-12 08:00:00 + var initDate = new Date(2019, 8, 12, 8, 0, 0); + initDate = new Date(initDate.getTime() - initDate.getTimezoneOffset()*60*1000); + + var calendar = await createCalendarView({ + View: CalendarView, + model: 'event', + data: this.data, + arch: + '<calendar class="o_calendar_test" '+ + 'date_start="start" '+ + 'date_stop="stop" '+ + 'mode="month">'+ + '</calendar>', + archs: archs, + + viewOptions: { + initialDate: initDate, + }, + translateParameters: { + week_start: 1, + }, + mockRPC: function (route, args) { + if (args.method === 'search_read' && args.model === 'event') { + assert.deepEqual(args.kwargs.domain, [ + ["start","<=","2019-10-06 23:59:59"], + ["stop",">=","2019-08-26 00:00:00"] + ], + 'The domain to search events in should be correct'); + } + return this._super.apply(this, arguments); + } + }); + + assert.strictEqual(calendar.$('.fc-day-header').first().text(), "Monday", + "The first day of the week should be Monday"); + assert.strictEqual(calendar.$('.fc-day-header').last().text(), "Sunday", + "The last day of the week should be Sunday"); + + var $firstDay = calendar.$('.fc-day-top').first(); + assert.strictEqual($firstDay.find('.fc-week-number').text(), "35", + "The number of the week should be correct"); + assert.strictEqual($firstDay.find('.fc-day-number').text(), "26", + "The first day of the week should be 2019-09-01"); + assert.strictEqual($firstDay.data('date'), "2019-08-26", + "The first day of the week should be 2019-08-26"); + + var $lastDay = calendar.$('.fc-day-top').last(); + assert.strictEqual($lastDay.text(), "6", + "The last day of the week should be 2019-10-06"); + assert.strictEqual($lastDay.data('date'), "2019-10-06", + "The last day of the week should be 2019-10-06"); + + calendar.destroy(); + }); }); }); diff --git a/addons/web/static/tests/views/control_panel_tests.js b/addons/web/static/tests/views/control_panel_tests.js index f51070091b2..d86e1b5428e 100644 --- a/addons/web/static/tests/views/control_panel_tests.js +++ b/addons/web/static/tests/views/control_panel_tests.js @@ -1,16 +1,18 @@ odoo.define('web.control_panel_tests', function (require) { "use strict"; -var ControlPanelView = require('web.ControlPanelView'); -var testUtils = require('web.test_utils'); +const AbstractAction = require('web.AbstractAction'); +const ControlPanelView = require('web.ControlPanelView'); +const core = require('web.core'); +const testUtils = require('web.test_utils'); -var createControlPanel = testUtils.createControlPanel; +const createActionManager = testUtils.createActionManager; +const createControlPanel = testUtils.createControlPanel; -function createControlPanelFactory(arch, fields, params) { - params = params || {}; +function createControlPanelFactory(arch, fields) { arch = arch || "<search></search>"; fields = fields || {}; - var viewInfo = {arch: arch, fields: fields}; + var viewInfo = {arch: arch, fields: fields}; var controlPanelFactory = new ControlPanelView({viewInfo: viewInfo, context: {}}); return controlPanelFactory; } @@ -337,29 +339,6 @@ QUnit.module('Views', { ); }); - QUnit.module('Control Panel Rendering'); - - QUnit.test('invisible filters are not rendered', async function (assert) { - assert.expect(2); - var controlPanel = await createControlPanel({ - model: 'partner', - arch: "<search>" + - "<filter name=\"filterA\" string=\"A\" domain=\"[]\"/>" + - "<filter name=\"filterB\" string=\"B\" invisible=\"1\" domain=\"[]\"/>" + - "</search>", - data: this.data, - searchMenuTypes: ['filter'], - context: { - search_disable_custom_filters: true, - }, - }); - await testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); - assert.containsOnce(controlPanel, '.o_menu_item a:contains("A")'); - assert.containsOnce(controlPanel, '.o_menu_item.d-none a:contains("B")'); - - controlPanel.destroy(); - }); - QUnit.module('Control Panel behaviour'); QUnit.test('remove a facet with backspace', async function (assert) { @@ -397,23 +376,51 @@ QUnit.module('Views', { QUnit.module('Control Panel Rendering'); + QUnit.test('default breadcrumb in abstract action', async function (assert) { + assert.expect(1); + + const ConcreteAction = AbstractAction.extend({ + hasControlPanel: true, + }); + core.action_registry.add('ConcreteAction', ConcreteAction); + + const actionManager = await createActionManager(); + await actionManager.doAction({id: 1, + name: 'A Concrete Action', + tag: 'ConcreteAction', + type: 'ir.actions.client', + }); + + assert.strictEqual(actionManager.$('.breadcrumb').text(), 'A Concrete Action'); + + actionManager.destroy(); + }); + QUnit.test('invisible filters are not rendered', async function (assert) { - assert.expect(2); + assert.expect(5); var controlPanel = await createControlPanel({ model: 'partner', - arch: "<search>" + - "<filter name=\"filterA\" string=\"A\" domain=\"[]\"/>" + - "<filter name=\"filterB\" string=\"B\" invisible=\"1\" domain=\"[]\"/>" + - "</search>", + arch: `<search> + <filter name="filterA" string="A" domain="[]"/> + <filter name="filterB" string="B" invisible="1" domain="[]"/> + </search>`, data: this.data, searchMenuTypes: ['filter'], context: { - search_disable_custom_filters: true, + search_default_filterB: true, }, }); await testUtils.dom.click(controlPanel.$('.o_filters_menu_button')); assert.containsOnce(controlPanel, '.o_menu_item a:contains("A")'); - assert.containsOnce(controlPanel, '.o_menu_item.d-none a:contains("B")'); + assert.containsNone(controlPanel, '.o_menu_item a:contains("B")'); + // default filter should be activated even if invisible + assert.containsOnce(controlPanel, '.o_searchview_facet .o_facet_values:contains(B)'); + + // Triggers an update of the filter menu + await testUtils.dom.click(controlPanel.$('.o_filters_menu .o_menu_item')); + // The displayed filters shhould be the same as before + assert.containsOnce(controlPanel, '.o_menu_item a:contains("A")'); + assert.containsNone(controlPanel, '.o_menu_item a:contains("B")'); controlPanel.destroy(); }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index fcb7df4ff29..03c492245b5 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -2799,6 +2799,42 @@ QUnit.module('Views', { kanban.destroy(); }); + QUnit.test('completely prevent drag and drop if records_draggable set to false', async function (assert) { + assert.expect(6); + + var envIDs = [1, 3, 2, 4]; // the ids that should be in the environment during this test + var kanban = await createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '<kanban class="o_kanban_test" records_draggable="false">' + + '<field name="bar"/>' + + '<templates><t t-name="kanban-box">' + + '<div><field name="foo"/></div>' + + '</t></templates></kanban>', + groupBy: ['product_id'], + }); + + // testing initial state + assert.containsN(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record', 2); + assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 2); + assert.deepEqual(kanban.exportState().resIds, envIDs); + + // attempt to drag&drop a record in another column + var $record = kanban.$('.o_kanban_group:nth-child(1) .o_kanban_record:first'); + var $group = kanban.$('.o_kanban_group:nth-child(2)'); + await testUtils.dom.dragAndDrop($record, $group, {withTrailingClick: true}); + + // should not drag&drop record + assert.containsN(kanban, '.o_kanban_group:nth-child(1) .o_kanban_record', 2, + "First column should still contain 2 records"); + assert.containsN(kanban, '.o_kanban_group:nth-child(2) .o_kanban_record', 2, + "Second column should still contain 2 records"); + assert.deepEqual(kanban.exportState().resIds, envIDs, "Records should not have moved"); + + kanban.destroy(); + }); + QUnit.test('kanban view with default_group_by', async function (assert) { assert.expect(7); this.data.partner.records.product_id = 1; diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 82607c43689..32c868a9bb0 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -98,6 +98,7 @@ QUnit.module('Views', { "ir.translation": { fields: { lang_code: {type: "char"}, + src: {type: "char"}, value: {type: "char"}, res_id: {type: "integer"}, name: {type: "char"}, @@ -1537,7 +1538,7 @@ QUnit.module('Views', { QUnit.test('width of some of the fields should be hardcoded if no data', async function (assert) { const assertions = [ - { field: 'bar', expected: 40, type: 'Boolean' }, + { field: 'bar', expected: 50, type: 'Boolean' }, { field: 'int_field', expected: 74, type: 'Integer' }, { field: 'qux', expected: 92, type: 'Float' }, { field: 'date', expected: 92, type: 'Date' }, @@ -1577,7 +1578,7 @@ QUnit.module('Views', { QUnit.test('width of some fields should be hardcoded if no data, and list initially invisible', async function (assert) { const assertions = [ - { field: 'bar', expected: 40, type: 'Boolean' }, + { field: 'bar', expected: 50, type: 'Boolean' }, { field: 'int_field', expected: 74, type: 'Integer' }, { field: 'qux', expected: 92, type: 'Float' }, { field: 'date', expected: 92, type: 'Date' }, @@ -1635,8 +1636,103 @@ QUnit.module('Views', { form.destroy(); }); + QUnit.test('editable list: overflowing table', async function (assert) { + assert.expect(1); + + this.data.bar = { + fields: { + titi: { string: "Small char", type: "char", sortable: true }, + grosminet: { string: "Beeg char", type: "char", sortable: true }, + }, + records: [ + { + id: 1, + titi: "Tiny text", + grosminet: + // Just want to make sure that the table is overflowed + `Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Donec est massa, gravida eget dapibus ac, eleifend eget libero. + Suspendisse feugiat sed massa eleifend vestibulum. Sed tincidunt + velit sed lacinia lacinia. Nunc in fermentum nunc. Vestibulum ante + ipsum primis in faucibus orci luctus et ultrices posuere cubilia + Curae; Nullam ut nisi a est ornare molestie non vulputate orci. + Nunc pharetra porta semper. Mauris dictum eu nulla a pulvinar. Duis + eleifend odio id ligula congue sollicitudin. Curabitur quis aliquet + nunc, ut aliquet enim. Suspendisse malesuada felis non metus + efficitur aliquet.`, + }, + ], + }; + const list = await createView({ + arch: ` + <tree editable="top"> + <field name="titi"/> + <field name="grosminet" widget="char"/> + </tree>`, + data: this.data, + model: 'bar', + View: ListView, + }); + + assert.strictEqual(list.$('table').width(), list.$('.o_list_view').width(), + "Table should not be stretched by its content"); + + list.destroy(); + }); + + QUnit.test('editable list: overflowing table (3 columns)', async function (assert) { + assert.expect(4); + + const longText = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Donec est massa, gravida eget dapibus ac, eleifend eget libero. + Suspendisse feugiat sed massa eleifend vestibulum. Sed tincidunt + velit sed lacinia lacinia. Nunc in fermentum nunc. Vestibulum ante + ipsum primis in faucibus orci luctus et ultrices posuere cubilia + Curae; Nullam ut nisi a est ornare molestie non vulputate orci. + Nunc pharetra porta semper. Mauris dictum eu nulla a pulvinar. Duis + eleifend odio id ligula congue sollicitudin. Curabitur quis aliquet + nunc, ut aliquet enim. Suspendisse malesuada felis non metus + efficitur aliquet.`; + + this.data.bar = { + fields: { + titi: { string: "Small char", type: "char", sortable: true }, + grosminet1: { string: "Beeg char 1", type: "char", sortable: true }, + grosminet2: { string: "Beeg char 2", type: "char", sortable: true }, + grosminet3: { string: "Beeg char 3", type: "char", sortable: true }, + }, + records: [{ + id: 1, + titi: "Tiny text", + grosminet1: longText, + grosminet2: longText + longText, + grosminet3: longText + longText + longText, + }], + }; + const list = await createView({ + arch: ` + <tree editable="top"> + <field name="titi"/> + <field name="grosminet1" class="large"/> + <field name="grosminet3" class="large"/> + <field name="grosminet2" class="large"/> + </tree>`, + data: this.data, + model: 'bar', + View: ListView, + }); + + assert.strictEqual(list.$('table').width(), list.$('.o_list_view').width()); + const largeCells = list.$('.o_data_cell.large'); + assert.strictEqual(largeCells[0].offsetWidth, largeCells[1].offsetWidth); + assert.strictEqual(largeCells[1].offsetWidth, largeCells[2].offsetWidth); + assert.ok(list.$('.o_data_cell:not(.large)')[0].offsetWidth < largeCells[0].offsetWidth); + + list.destroy(); + }); + QUnit.test('editable list: list view in an initially unselected notebook page', async function (assert) { - assert.expect(9); + assert.expect(8); this.data.foo.records = [{ id: 1, o2m: [1] }]; this.data.bar = { @@ -1745,23 +1841,6 @@ QUnit.module('Views', { "list has been correctly frozen after sorting a column"); form.destroy(); - - // Case 5: resize a column - form = await createView(formOptions); - await testUtils.dom.click(form.$('.nav-item:last-child .nav-link')); - - const resizeHandle = form.$('.o_resize')[0]; - await testUtils.dom.dragAndDrop(resizeHandle, resizeHandle, { - mousemoveTarget: window, - mouseupTarget: window, - }); - [titi, grosminet] = form.$('th'); - assert.ok( - titi.style.width.split('px')[0] > 80 && - grosminet.style.width.split('px')[0] > 700, - "list has been correctly frozen after resizing a column"); - - form.destroy(); }); QUnit.test('editable list: list view hidden by an invisible modifier', async function (assert) { @@ -1907,7 +1986,7 @@ QUnit.module('Views', { QUnit.test('width of some of the fields should be hardcoded if no data (grouped case)', async function (assert) { const assertions = [ - { field: 'bar', expected: 40, type: 'Boolean' }, + { field: 'bar', expected: 50, type: 'Boolean' }, { field: 'int_field', expected: 74, type: 'Integer' }, { field: 'qux', expected: 92, type: 'Float' }, { field: 'date', expected: 92, type: 'Date' }, @@ -2019,6 +2098,34 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('column widths are kept when switching records in edition', async function (assert) { + assert.expect(4); + + const list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: `<tree editable="bottom"> + <field name="m2o"/> + <field name="text"/> + </tree>`, + }); + + const width = list.$('th[data-name="m2o"]')[0].offsetWidth; + + await testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:first')); + + assert.hasClass(list.$('.o_data_row:first'), 'o_selected_row'); + assert.strictEqual(list.$('th[data-name="m2o"]')[0].offsetWidth, width); + + await testUtils.dom.click(list.$('.o_data_row:nth(1) .o_data_cell:first')); + + assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row'); + assert.strictEqual(list.$('th[data-name="m2o"]')[0].offsetWidth, width); + + list.destroy(); + }); + QUnit.test('column widths are re-computed on window resize', async function (assert) { assert.expect(2); @@ -2089,7 +2196,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree multi_edit="1">' + '<field name="datetime"/>' + '<field name="text"/>' + '</tree>', @@ -3544,193 +3651,6 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('export list view to xls with nested groups', async function (assert) { - assert.expect(5); - - let currencies = {}; - _.each(this.data.res_currency.records, function (currency) { - currencies[currency.id] = currency; - }); - - let list = await createView({ - View: ListView, - model: 'foo', - data: this.data, - arch: ` - <tree download="1"> - <field name="foo"/> - <field name="bar"/> - <field name="int_field" sum="Total"/> - <field name="datetime"/> - <field name="amount" widget="monetary"/> - <field name="currency_id" invisible="1"/> - <field name="o2m"/> - <field name="m2o"/> - <field name="m2m"/> - </tree>`, - groupBy: ['foo', 'bar'], - session: { - currencies: currencies, - get_file(args) { - let data = JSON.parse(args.data.data); - assert.strictEqual(args.url, '/web/list/export_xls', - "should call get_file with the correct url"); - assert.deepEqual(data.columns, [ - {field: "foo", string: "Foo"}, - {field: "bar", string: "Bar"}, - {field: "int_field", aggregateValue: 32, string: "int_field"}, - {field: "datetime", string: "Datetime Field"}, - {field: "amount", string: "Monetary field"}, - {field: "o2m", string: "O2M field"}, - {field: "m2o", string: "M2O field"}, - {field: "m2m", string: "M2M field"}, - ], "columns should be in the correct order with the string and aggregate value") - assert.deepEqual(data.groups[0], { - // Group "yop" - isGrouped: true, - count: 1, - aggregateValues: {int_field: 10}, - hideHeader: false, - value: "yop", - data: [{ - // Group "yop > true" - isGrouped: false, - count: 1, - aggregateValues: {int_field: 10}, - hideHeader: false, - value: "true", - data: [{ - // Record "yop > true" - foo: "yop", - bar: "True", - int_field: "10", - datetime: "12/12/2016 10:55:05", - amount: "1200.00€", - m2m: "2 records", - m2o: "Value 1", - o2m: "No records", - }] - }] - }); - assert.deepEqual(data.groups[1], { - // Group "blip" - isGrouped: true, - count: 2, - aggregateValues: {int_field: 5}, - hideHeader: false, - value: "blip", - data: [{ - // Group "blip > true" - isGrouped: false, - count: 1, - aggregateValues: {int_field: 9}, - hideHeader: false, - value: "true", - data: [], - }, { - // Group "blip > false" - isGrouped: false, - count: 1, - aggregateValues: {int_field: -4}, - hideHeader: false, - value: "false", - data: [], - }] - }); - assert.deepEqual(data.groups[2], { - // Group "gnap" - isGrouped: true, - count: 1, - aggregateValues: {int_field: 17}, - hideHeader: false, - value: "gnap", - data: [], - }); - args.complete(); - }, - }, - }); - - // open the first group - await testUtils.dom.click(list.$('.o_group_header:first')); - - // open subgroup - let $openGroup = list.$('tbody:nth(1)'); - await testUtils.dom.click($openGroup.find('.o_group_header:first')); - - // open the second group - await testUtils.dom.click(list.$('.o_group_header:nth(2)')); - - // Download - await testUtils.dom.click(list.$buttons.find('.o_list_download')); - - list.destroy(); - }); - - QUnit.test('export list view to xls', async function (assert) { - assert.expect(3); - - let currencies = {}; - _.each(this.data.res_currency.records, function (currency) { - currencies[currency.id] = currency; - }); - - let list = await createView({ - View: ListView, - model: 'foo', - data: this.data, - arch: ` - <tree> - <field name="foo"/> - <field name="bar"/> - <field name="int_field" sum="Total"/> - <field name="datetime"/> - <field name="amount" widget="monetary"/> - <field name="currency_id" invisible="1"/> - <field name="o2m"/> - <field name="m2o"/> - <field name="m2m"/> - </tree>`, - session: { - currencies: currencies, - get_file(args) { - let data = JSON.parse(args.data.data); - assert.strictEqual(args.url, '/web/list/export_xls', - "should call get_file with the correct url"); - assert.deepEqual(data.columns, [ - {field: "foo", string: "Foo"}, - {field: "bar", string: "Bar"}, - {field: "int_field", aggregateValue: 32, string: "int_field"}, - {field: "datetime", string: "Datetime Field"}, - {field: "amount", string: "Monetary field"}, - {field: "o2m", string: "O2M field"}, - {field: "m2o", string: "M2O field"}, - {field: "m2m", string: "M2M field"}, - ], "columns should be in the correct order with the string and aggregate value") - assert.deepEqual(data.groups[0], { - isGrouped: false, - count: 4, - aggregateValues: {}, - hideHeader: true, - value: "Undefined", - data: [ - {foo: "yop", bar: "True", int_field: "10", datetime: "12/12/2016 10:55:05", amount: "1200.00€", m2m: "2 records", m2o: "Value 1", o2m: "No records"}, - {foo: "blip", bar: "True", int_field: "9", datetime: "", amount: "$500.00", m2m: "3 records", m2o: "Value 2", o2m: "No records"}, - {foo: "gnap", bar: "True", int_field: "17", datetime: "", amount: "$300.00", m2m: "No records", m2o: "Value 1", o2m: "No records"}, - {foo: "blip", bar: "False", int_field: "-4", datetime: "", amount: "$0.00", m2m: "1 record", m2o: "Value 1", o2m: "No records"}, - ], - }); - args.complete(); - }, - }, - }); - - // Download - await testUtils.dom.click(list.$buttons.find('.o_list_download')); - - list.destroy(); - }); - QUnit.test('grouped list on selection field at level 2', async function (assert) { assert.expect(4); @@ -5392,7 +5312,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="top">' + + arch: '<tree multi_edit="1">' + '<field name="foo"/>' + '</tree>', mockRPC: function (route, args) { @@ -5426,7 +5346,7 @@ QUnit.module('Views', { assert.expect(2); const list = await createView({ - arch: `<tree editable="top"><field name="foo"/></tree>`, + arch: `<tree editable="top" multi_edit="1"><field name="foo"/></tree>`, data: this.data, model: 'foo', View: ListView, @@ -5455,7 +5375,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree editable="bottom" multi_edit="1">' + '<field name="foo"/>' + '<field name="int_field"/>' + '</tree>', @@ -5510,7 +5430,7 @@ QUnit.module('Views', { await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')); await testUtils.fields.editInput(list.$('.o_field_widget[name=int_field]'), 666); await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_data_cell:eq(0)')); - assert.ok($('.modal').text().includes('2 selected records'), "the number of records should be correctly displayed"); + assert.ok($('.modal').text().includes('those 2 records'), "the number of records should be correctly displayed"); await testUtils.dom.click($('.modal .btn-primary')); assert.containsNone(list, '.o_data_cell input.o_field_widget', "no field should be editable anymore"); assert.verifySteps(['write', 'read']); @@ -5525,8 +5445,32 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable list view: multi edition and onchanges', async function (assert) { - assert.expect(10); + QUnit.test('create in multi editable list', async function (assert) { + assert.expect(1); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree multi_edit="1">' + + '<field name="foo"/>' + + '<field name="int_field"/>' + + '</tree>', + intercepts: { + switch_view: function (ev) { + assert.strictEqual(ev.data.view_type, 'form'); + }, + }, + }); + + // click on CREATE (should trigger a switch_view) + await testUtils.dom.click($('.o_list_button_add')); + + list.destroy(); + }); + + QUnit.test('editable list view: multi edition cannot call onchanges', async function (assert) { + assert.expect(15); this.data.foo.onchanges = { foo: function (obj) { @@ -5537,7 +5481,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree multi_edit="1">' + '<field name="foo"/>' + '<field name="int_field"/>' + '</tree>', @@ -5550,19 +5494,24 @@ QUnit.module('Views', { }, }); - // edit a single record to validate that onchanges work + assert.verifySteps(['/web/dataset/search_read']); + + // select and edit a single record + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), 'hi'); await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')); - await testUtils.dom.click(list.$('.o_list_button_discard')); - assert.containsOnce(document.body, '.modal'); // discard dialog + assert.containsOnce(document.body, '.modal'); // save dialog await testUtils.dom.click($('.modal .btn-primary')); - assert.verifySteps(['/web/dataset/search_read', 'onchange']); + assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "hi2"); + assert.strictEqual(list.$('.o_data_row:eq(1) .o_data_cell').text(), "blip9"); - // select two records - await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); + assert.verifySteps(['write', 'read']); + + // select the second record (the first one is still selected) + assert.ok(list.$('.o_data_row:eq(0) .o_list_record_selector input').is(':checked')); await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); // edit foo, first row @@ -5588,7 +5537,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree multi_edit="1">' + '<field name="foo" required="1"/>' + '<field name="int_field"/>' + '</tree>', @@ -5636,7 +5585,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree multi_edit="1">' + '<field name="foo"/>' + '<field name="m2o"/>' + '</tree>', @@ -5672,7 +5621,7 @@ QUnit.module('Views', { const list = await createView({ arch: ` - <tree editable="top"> + <tree editable="top" multi_edit="1"> <field name="foo"/> </tree>`, data: this.data, @@ -5710,7 +5659,7 @@ QUnit.module('Views', { const list = await createView({ arch: ` - <tree editable="top"> + <tree multi_edit="1"> <field name="foo"/> </tree>`, data: this.data, @@ -5740,13 +5689,13 @@ QUnit.module('Views', { }); QUnit.test('editable list view: multi edition with readonly modifiers', async function (assert) { - assert.expect(5); + assert.expect(4); var list = await createView({ View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="top">' + + arch: '<tree multi_edit="1">' + '<field name="id"/>' + '<field name="foo"/>' + '<field name="int_field" attrs=\'{"readonly": [("id", ">" , 2)]}\'/>' + @@ -5766,17 +5715,19 @@ QUnit.module('Views', { // edit a field await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')); await testUtils.fields.editInput(list.$('.o_field_widget[name=int_field]'), 666); - assert.ok($('.modal').text().includes('2 valid'), - "the number of records should be correctly displayed (only 2 not readonly)"); - assert.ok($('.modal').text().includes('2 invalid'), - "should display the number of invalid records"); + + const modalText = $('.modal-body').text() + .split(" ").filter(w => w.trim() !== '').join(" ") + .split("\n").join(''); + assert.strictEqual(modalText, + "Among the 4 selected records, 2 are valid for this update. Are you sure you want to " + + "perform the following update on those 2 records ? Field: int_field Update to: 666"); await testUtils.dom.click($('.modal .btn-primary')); assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "1yop666", "the first row should be updated"); assert.strictEqual(list.$('.o_data_row:eq(1) .o_data_cell').text(), "2blip666", "the second row should be updated"); - list.destroy(); }); @@ -5804,6 +5755,100 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('editable list view: multi edition server error handling', async function (assert) { + assert.expect(3); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree multi_edit="1">' + + '<field name="foo" required="1"/>' + + '</tree>', + mockRPC: function (route, args) { + if (args.method === 'write') { + return Promise.reject(); + } + return this._super.apply(this, arguments); + }, + }); + + // select two records + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); + + // edit a line and confirm + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); + await testUtils.fields.editInput(list.$('.o_selected_row .o_field_widget[name=foo]'), "abc"); + await testUtils.dom.click('body'); + await testUtils.dom.click($('.modal .btn-primary')); + // Server error: if there was a crash manager, there would be an open error at this point... + assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), 'yop', + "first cell should have discarded any change"); + assert.strictEqual(list.$('.o_data_row:eq(1) .o_data_cell').text(), 'blip', + "second selected record should not have changed"); + assert.containsNone(list, '.o_data_cell input.o_field_widget', + "no field should be editable anymore"); + + list.destroy(); + }); + + QUnit.test('editable readonly list view: navigation', async function (assert) { + assert.expect(6); + + const list = await createView({ + arch: ` + <tree multi_edit="1"> + <field name="foo"/> + <field name="int_field"/> + </tree>`, + data: this.data, + intercepts: { + switch_view: function (event) { + assert.strictEqual(event.data.res_id, 3, + "'switch_view' event has been triggered"); + }, + }, + model: 'foo', + View: ListView, + }); + + // select 2 records + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); + await testUtils.dom.click(list.$('.o_data_row:eq(3) .o_list_record_selector input')); + + // toggle a row mode + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_data_cell:eq(1)')); + assert.hasClass(list.$('.o_data_row:eq(1)'), 'o_selected_row', + "the second row should be selected"); + + // Keyboard navigation only interracts with selected elements + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input.o_field_widget'), 'enter'); + assert.hasClass(list.$('.o_data_row:eq(3)'), 'o_selected_row', + "the fourth row should be selected"); + + await testUtils.fields.triggerKeydown($(document.activeElement), 'tab'); + await testUtils.fields.triggerKeydown($(document.activeElement), 'tab'); + assert.hasClass(list.$('.o_data_row:eq(1)'), 'o_selected_row', + "the second row should be selected again"); + + await testUtils.fields.triggerKeydown($(document.activeElement), 'tab'); + await testUtils.fields.triggerKeydown($(document.activeElement), 'tab'); + assert.hasClass(list.$('.o_data_row:eq(3)'), 'o_selected_row', + "the fourth row should be selected again"); + + await testUtils.dom.click(list.$('.o_data_row:eq(2) .o_data_cell:eq(0)')); + assert.containsNone(list, '.o_data_cell input.o_field_widget', + "no row should be editable anymore"); + // Clicking on an unselected record while no row is being edited will open the record (switch_view) + await testUtils.dom.click(list.$('.o_data_row:eq(2) .o_data_cell:eq(0)')); + + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); + await testUtils.dom.click(list.$('.o_data_row:eq(3) .o_list_record_selector input')); + + list.destroy(); + }); + QUnit.test('editable list view: multi edition: edit and validate last row', async function (assert) { assert.expect(3); @@ -5811,7 +5856,7 @@ QUnit.module('Views', { View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="bottom">' + + arch: '<tree multi_edit="1">' + '<field name="foo"/>' + '<field name="int_field"/>' + '</tree>', @@ -5839,6 +5884,238 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('editable readonly list view: navigation in grouped list', async function (assert) { + assert.expect(6); + + const list = await createView({ + arch: ` + <tree multi_edit="1"> + <field name="foo"/> + </tree>`, + data: this.data, + groupBy: ['bar'], + intercepts: { + switch_view: function (event) { + assert.strictEqual(event.data.res_id, 3, + "'switch_view' event has been triggered"); + }, + }, + model: 'foo', + View: ListView, + }); + + // Open both groups + await testUtils.dom.click(list.$('.o_group_header:first')); + await testUtils.dom.click(list.$('.o_group_header:last')); + + // select 2 records + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); + await testUtils.dom.click(list.$('.o_data_row:eq(3) .o_list_record_selector input')); + + // toggle a row mode + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_data_cell:eq(0)')); + assert.hasClass(list.$('.o_data_row:eq(1)'), 'o_selected_row', + "the second row should be selected"); + + // Keyboard navigation only interracts with selected elements + await testUtils.fields.triggerKeydown(list.$('tr.o_selected_row input.o_field_widget'), 'enter'); + assert.hasClass(list.$('.o_data_row:eq(3)'), 'o_selected_row', + "the fourth row should be selected"); + + await testUtils.fields.triggerKeydown($(document.activeElement), 'tab'); + assert.hasClass(list.$('.o_data_row:eq(1)'), 'o_selected_row', + "the second row should be selected again"); + + await testUtils.fields.triggerKeydown($(document.activeElement), 'tab'); + assert.hasClass(list.$('.o_data_row:eq(3)'), 'o_selected_row', + "the fourth row should be selected again"); + + await testUtils.dom.click(list.$('.o_data_row:eq(2) .o_data_cell:eq(0)')); + assert.containsNone(list, '.o_data_cell input.o_field_widget', "no row should be editable anymore"); + await testUtils.dom.click(list.$('.o_data_row:eq(2) .o_data_cell:eq(0)')); + + list.destroy(); + }); + + QUnit.test('editable readonly list view: single edition behaves like a multi-edition', async function (assert) { + assert.expect(3); + + const list = await createView({ + arch: ` + <tree multi_edit="1"> + <field name="foo" required="1"/> + </tree>`, + data: this.data, + model: 'foo', + View: ListView, + }); + + // select a record + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); + + // edit a field (invalid input) + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); + await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), ""); + + assert.containsOnce($('body'),'.modal', "should have a modal"); + await testUtils.dom.click($('.modal button.btn')); + + // edit a field + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); + await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), "bar"); + + assert.containsOnce($('body'),'.modal', "should have a modal"); + await testUtils.dom.click($('.modal button.btn.btn-primary')); + + + assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "bar", + "the first row should be updated"); + + list.destroy(); + }); + + QUnit.test('editable readonly list view: multi edition', async function (assert) { + assert.expect(14); + + const list = await createView({ + arch: + `<tree multi_edit="1"> + <field name="foo"/> + <field name="int_field"/> + </tree>`, + data: this.data, + mockRPC: function (route, args) { + assert.step(args.method || route); + if (args.method === 'write') { + assert.deepEqual(args.args, [[1, 2], { int_field: 666 }], + "should write on multi records"); + } else if (args.method === 'read') { + if (args.args[0].length !== 1) { + assert.deepEqual(args.args, [[1, 2], ['foo', 'int_field']], + "should batch the read"); + } + } + return this._super.apply(this, arguments); + }, + model: 'foo', + View: ListView, + }); + + assert.verifySteps(['/web/dataset/search_read']); + + // select two records + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); + + // edit a field + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')); + await testUtils.fields.editInput(list.$('.o_field_widget[name=int_field]'), 666); + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); + + assert.containsOnce(document.body, '.modal', + "modal appears when switching cells"); + + await testUtils.dom.click($('.modal .btn:contains(Cancel)')); + + assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), 'yop10', + "changes have been discarded and row is back to readonly"); + + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')); + await testUtils.fields.editInput(list.$('.o_field_widget[name=int_field]'), 666); + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_data_cell:eq(0)')); + + assert.containsOnce(document.body, '.modal', + "there should be an opened modal"); + assert.ok($('.modal').text().includes('those 2 records'), + "the number of records should be correctly displayed"); + + await testUtils.dom.click($('.modal .btn-primary')); + + assert.verifySteps(['write', 'read']); + assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "yop666", + "the first row should be updated"); + assert.strictEqual(list.$('.o_data_row:eq(1) .o_data_cell').text(), "blip666", + "the second row should be updated"); + assert.containsNone(list, '.o_data_cell input.o_field_widget', + "no field should be editable anymore"); + + list.destroy(); + }); + + QUnit.test('editable list view: m2m tags in grouped list', async function (assert) { + assert.expect(2); + + const list = await createView({ + arch: ` + <tree editable="top" multi_edit="1"> + <field name="bar"/> + <field name="m2m" widget="many2many_tags"/> + </tree>`, + data: this.data, + groupBy: ['bar'], + model: 'foo', + View: ListView, + }); + + // Opens first group + await testUtils.dom.click(list.$('.o_group_header:first')); + + assert.notEqual(list.$('.o_data_row:first').text(), list.$('.o_data_row:last').text(), + "First row and last row should have different values"); + + await testUtils.dom.click(list.$('thead .o_list_record_selector:first input')); + await testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:eq(1)')); + await testUtils.dom.click(list.$('.o_selected_row .o_field_many2manytags .o_delete:first')); + await testUtils.dom.click($('.modal .btn-primary')); + + assert.strictEqual(list.$('.o_data_row:first').text(), list.$('.o_data_row:last').text(), + "All rows should have been correctly updated"); + + list.destroy(); + }); + + QUnit.test('editable list: edit many2one from external link', async function (assert) { + assert.expect(2); + + const list = await createView({ + arch: ` + <tree editable="top" multi_edit="1"> + <field name="m2o"/> + </tree>`, + archs: { + 'bar,false,form': '<form string="Bar"><field name="display_name"/></form>', + }, + data: this.data, + mockRPC: function (route, args) { + if (args.method === 'get_formview_id') { + return Promise.resolve(false); + } + return this._super(route, args); + }, + model: 'foo', + View: ListView, + }); + + await testUtils.dom.click(list.$('thead .o_list_record_selector:first input')); + await testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:eq(0)')); + await testUtils.dom.click(list.$('.o_external_button:first')); + + // Change the M2O value in the Form dialog + await testUtils.fields.editInput($('.modal input:first'), "OOF"); + await testUtils.dom.click($('.modal .btn-primary')); + + assert.strictEqual($('.modal .o_field_widget[name=m2o]').text(), "OOF", + "Value of the m2o should be updated in the confirmation dialog"); + + // Close the confirmation dialog + await testUtils.dom.click($('.modal .btn-primary')); + + assert.strictEqual(list.$('.o_data_cell:first').text(), "OOF", + "Value of the m2o should be updated in the list"); + + list.destroy(); + }); + QUnit.test('list grouped by date:month', async function (assert) { assert.expect(1); @@ -8060,6 +8337,44 @@ QUnit.module('Views', { list.destroy(); delete widgetRegistry.map.test; }); + + QUnit.test('enter edition in editable list with multi_edit = 0', async function (assert) { + assert.expect(1); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree editable="top" multi_edit="0">' + + '<field name="int_field"/>' + + '</tree>', + }); + + // click on int_field cell of first row + await testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:nth(0)')); + assert.strictEqual(document.activeElement.name, "int_field"); + + list.destroy(); + }); + + QUnit.test('enter edition in editable list with multi_edit = 1', async function (assert) { + assert.expect(1); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree editable="top" multi_edit="1">' + + '<field name="int_field"/>' + + '</tree>', + }); + + // click on int_field cell of first row + await testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:nth(0)')); + assert.strictEqual(document.activeElement.name, "int_field"); + + list.destroy(); + }); }); }); diff --git a/addons/web/static/tests/views/search_view_tests.js b/addons/web/static/tests/views/search_view_tests.js index bd75f92811b..43645318f6c 100644 --- a/addons/web/static/tests/views/search_view_tests.js +++ b/addons/web/static/tests/views/search_view_tests.js @@ -372,7 +372,7 @@ QUnit.module('Search View', { assert.expect(7); const unpatchDate = patchDate(2019,6,31,13,43,0); - + var actionManager = await createActionManager({ actions: this.actions, archs: this.archs, @@ -524,7 +524,7 @@ QUnit.module('Search View', { await testUtils.dom.click($('.o_search_options button span.fa-filter')); await testUtils.dom.click($('.o_filters_menu .o_menu_item a').first()); await testUtils.dom.click($('.o_graph_controller .o_control_panel .o_cp_buttons button').eq(1)); - await testUtils.dom.click($('.o_graph_controller .o_group_by_menu .o_menu_item').eq(1)); + await testUtils.dom.click($('.o_graph_controller .o_group_by_menu .o_menu_item').eq(1)); assert.doesNotHaveClass($('.o_graph_controller .o_group_by_menu .o_menu_item > .dropdown-item').eq(1), 'selected', 'groupby should be still unselected'); actionManager.destroy(); @@ -701,6 +701,7 @@ QUnit.module('Search View', { // DateTime case $autocomplete = $('.o_searchview_input'); await stringToEvent($autocomplete, '07/15/1983 00:00:00'); + await testUtils.fields.triggerKey('down', $autocomplete, 'down'); await testUtils.fields.triggerKey('up', $autocomplete, 'enter'); assert.equal($('.o_searchview_facet .o_facet_values').text().trim(), '07/15/1983 00:00:00', @@ -1116,7 +1117,7 @@ QUnit.module('Search View', { // activate Foo a await testUtils.fields.triggerKey('press', $('.o_searchview_input'), 97); await testUtils.fields.triggerKey('up', $('.o_searchview_input'), 'enter'); - + // activate Date Filter with This Month await testUtils.dom.click($('button .fa-filter')); await testUtils.dom.click($('.o_filters_menu .o_menu_item a')); @@ -1604,7 +1605,7 @@ QUnit.module('Search View', { var $filterDropdown = $('.modal .o_filters_menu'); await testUtils.dom.click($filterDropdown.find('.o_add_custom_filter')); - assert.containsN($filterDropdown, '.o_input', 3); + assert.containsN($filterDropdown, '.o_input', 4); // We really are interested in the click event // We do it twice on each input to make sure diff --git a/addons/web/static/tests/widgets/data_export_tests.js b/addons/web/static/tests/widgets/data_export_tests.js index 6d29e8e888d..c9c51219571 100644 --- a/addons/web/static/tests/widgets/data_export_tests.js +++ b/addons/web/static/tests/widgets/data_export_tests.js @@ -14,12 +14,22 @@ QUnit.module('widgets', { partner: { fields: { foo: {string: "Foo", type: "char"}, + bar: {string: "Bar", type: "char"}, }, records: [ { id: 1, foo: "yop", - }, + bar: "bar-blup", + }, { + id: 2, + foo: "yop", + bar: "bar-yop", + }, { + id: 3, + foo: "blup", + bar: "bar-blup", + } ] }, 'ir.exports': { @@ -239,7 +249,7 @@ QUnit.module('widgets', { }, }); // Open the export modal - await testUtils.dom.click(list.$('.o_data_row .o_list_record_selector input')); + await testUtils.dom.click(list.$('thead .o_list_record_selector input')); await testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); await testUtils.dom.click(list.sidebar.$('a:contains(Export)')); @@ -257,6 +267,108 @@ QUnit.module('widgets', { assert.strictEqual($('.modal .o_fields_list li').length, 1, "There should be only one field in list"); list.destroy(); }); + + QUnit.test('Direct export button invisible', async function (assert) { + assert.expect(1) + + let list = await createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: `<tree export_xlsx="0"><field name="foo"/></tree>`, + }); + assert.containsNone(list, '.o_list_export_xlsx') + list.destroy(); + }); + + QUnit.test('Direct export list ', async function (assert) { + assert.expect(2) + + let list = await createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: ` + <tree export_xlsx="1"> + <field name="foo"/> + <field name="bar"/> + </tree>`, + domain: [['bar', '!=', 'glou']], + session: { + get_file(args) { + let data = JSON.parse(args.data.data); + assert.strictEqual(args.url, '/web/export/xlsx', "should call get_file with the correct url"); + assert.deepEqual(data, { + context: {}, + model: 'partner', + domain: [['bar', '!=', 'glou']], + groupby: [], + ids: false, + import_compat: false, + fields: [{ + name: 'foo', + label: 'Foo', + }, { + name: 'bar', + label: 'Bar', + }] + }, "should be called with correct params") + args.complete(); + }, + }, + }); + + // select a record => should not be taken into account + await testUtils.dom.click(list.$('.o_data_row .o_list_record_selector input')[0]); + // Download + await testUtils.dom.click(list.$buttons.find('.o_list_export_xlsx')); + + list.destroy(); + }); + + QUnit.test('Direct export grouped list ', async function (assert) { + assert.expect(2) + + let list = await createView({ + View: ListView, + model: 'partner', + data: this.data, + debug: true, + arch: ` + <tree> + <field name="foo"/> + <field name="bar"/> + </tree>`, + groupBy: ['foo', 'bar'], + domain: [['bar', '!=', 'glou']], + session: { + get_file(args) { + let data = JSON.parse(args.data.data); + assert.strictEqual(args.url, '/web/export/xlsx', "should call get_file with the correct url"); + assert.deepEqual(data, { + context: {}, + model: 'partner', + domain: [['bar', '!=', 'glou']], + groupby: ['foo', 'bar'], + ids: false, + import_compat: false, + fields: [{ + name: 'foo', + label: 'Foo', + }, { + name: 'bar', + label: 'Bar', + }] + }, "should be called with correct params") + args.complete(); + }, + }, + }); + + await testUtils.dom.click(list.$buttons.find('.o_list_export_xlsx')); + + list.destroy(); + }); }); }); |
