diff options
| author | Julien Mougenot <jum@odoo.com> | 2020-02-25 12:52:41 +0000 |
|---|---|---|
| committer | Julien Mougenot <jum@odoo.com> | 2020-03-13 09:37:29 +0000 |
| commit | 384e516218375000461aa3fcb0ac1b9eaa960fb3 (patch) | |
| tree | 89d6faece5edea9a4f182b7a6d2e83cbb6f540ce /addons/web/static/tests | |
| parent | 11ba58a311311b798a68183df18673e562bb97d3 (diff) | |
[REF] web: Update generic files
General cleaning of some helpers and generic files. The idea is that they are
less likely to be entirely re-written in the near future so this is a way to
keep the code clean, readable and compliant to the current standards. Changes include:
- Use of async/await when possible
- Use of const/let instead of var
- Removal of underscore.js
- Use of native JS instead of jQuery when possible
The logic of the affected code remains the same.
Diffstat (limited to 'addons/web/static/tests')
| -rw-r--r-- | addons/web/static/tests/helpers/qunit_asserts.js | 10 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils.js | 254 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils_create.js | 288 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils_dom.js | 194 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils_fields.js | 119 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils_mock.js | 2 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils_modal.js | 48 |
7 files changed, 446 insertions, 469 deletions
diff --git a/addons/web/static/tests/helpers/qunit_asserts.js b/addons/web/static/tests/helpers/qunit_asserts.js index a57154e416e..0f22acbbf78 100644 --- a/addons/web/static/tests/helpers/qunit_asserts.js +++ b/addons/web/static/tests/helpers/qunit_asserts.js @@ -11,7 +11,7 @@ odoo.define('web.qunit_asserts', function (require) { * matches exactly one element. */ - var Widget = require('web.Widget'); + const Widget = require('web.Widget'); //------------------------------------------------------------------------- // Private functions @@ -56,8 +56,8 @@ odoo.define('web.qunit_asserts', function (require) { */ function _checkVisible(w, shouldBeVisible, msg) { if (w instanceof jQuery && w.length !== 1) { - const assertion = shouldHaveClass ? 'hasClass' : 'doesNotHaveClass'; - QUnit.assert.ok(false, `Assertion '${assertion} ${className}' targets ${w.length} elements instead of 1`); + const assertion = shouldBeVisible ? 'isVisible' : 'isNotVisible'; + QUnit.assert.ok(false, `Assertion '${assertion}' targets ${w.length} elements instead of 1`); } const el = w instanceof Widget || w instanceof owl.Component ? w.el : @@ -184,12 +184,12 @@ odoo.define('web.qunit_asserts', function (require) { * @param {string} [msg] */ function hasAttrValue(w, attr, value, msg) { - var $el = w instanceof Widget ? w.$el : + const $el = w instanceof Widget ? w.$el : w instanceof HTMLElement ? $(w) : w; // jquery element if ($el.length !== 1) { - var descr = `hasAttrValue (${attr}: ${value})`; + const descr = `hasAttrValue (${attr}: ${value})`; QUnit.assert.ok(false, `Assertion '${descr}' targets ${$el.length} elements instead of 1` ); diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js index f2b12eaf705..2ff77de4e49 100644 --- a/addons/web/static/tests/helpers/test_utils.js +++ b/addons/web/static/tests/helpers/test_utils.js @@ -1,4 +1,4 @@ -odoo.define('web.test_utils', function (require) { +odoo.define('web.test_utils', async function (require) { "use strict"; /** @@ -10,26 +10,25 @@ odoo.define('web.test_utils', function (require) { * instance of a view, appended in the dom, ready to be tested. */ - var ajax = require('web.ajax'); - var concurrency = require('web.concurrency'); - var core = require('web.core'); - var relationalFields = require('web.relational_fields'); - var session = require('web.session'); - var testUtilsCreate = require('web.test_utils_create'); - var testUtilsDom = require('web.test_utils_dom'); - var testUtilsFields = require('web.test_utils_fields'); - var testUtilsFile = require('web.test_utils_file'); - var testUtilsForm = require('web.test_utils_form'); - var testUtilsGraph = require('web.test_utils_graph'); - var testUtilsKanban = require('web.test_utils_kanban'); - var testUtilsMock = require('web.test_utils_mock'); - var testUtilsModal = require('web.test_utils_modal'); - var testUtilsPivot = require('web.test_utils_pivot'); - var tools = require('web.tools'); + const ajax = require('web.ajax'); + const core = require('web.core'); + const relationalFields = require('web.relational_fields'); + const session = require('web.session'); + const testUtilsCreate = require('web.test_utils_create'); + const testUtilsDom = require('web.test_utils_dom'); + const testUtilsFields = require('web.test_utils_fields'); + const testUtilsFile = require('web.test_utils_file'); + const testUtilsForm = require('web.test_utils_form'); + const testUtilsGraph = require('web.test_utils_graph'); + const testUtilsKanban = require('web.test_utils_kanban'); + const testUtilsMock = require('web.test_utils_mock'); + const testUtilsModal = require('web.test_utils_modal'); + const testUtilsPivot = require('web.test_utils_pivot'); + const tools = require('web.tools'); function deprecated(fn, type) { - var msg = `Helper 'testUtils.${fn.name}' is deprecated. ` + + const msg = `Helper 'testUtils.${fn.name}' is deprecated. ` + `Please use 'testUtils.${type}.${fn.name}' instead.`; return tools.deprecated(fn, msg); } @@ -41,9 +40,9 @@ odoo.define('web.test_utils', function (require) { * @returns {Promise + resolve and reject function} */ function makeTestPromise() { - var resolve; - var reject; - var promise = new Promise(function (_resolve, _reject) { + let resolve; + let reject; + const promise = new Promise(function (_resolve, _reject) { resolve = _resolve; reject = _reject; }); @@ -69,7 +68,7 @@ odoo.define('web.test_utils', function (require) { * @returns {Promise + resolve and reject function} */ function makeTestPromiseWithAssert(assert, str) { - var prom = makeTestPromise(); + const prom = makeTestPromise(); prom.then(() => assert.step('ok ' + str)).catch(function () { }); prom.catch(() => assert.step('ko ' + str)); return prom; @@ -102,118 +101,117 @@ odoo.define('web.test_utils', function (require) { // before starting the qunit test suite. // (session.js is in charge of loading the static xml bundle and we also have // to load xml files that are normally lazy loaded by specific widgets). - return Promise.all([ + await Promise.all([ session.is_bound, ajax.loadXML('/web/static/src/xml/debug.xml', core.qweb), ajax.loadXML('/web/static/src/xml/dialog.xml', core.qweb), ajax.loadXML('/web/static/src/xml/translation_dialog.xml', core.qweb), - ]).then(function () { - setTimeout(function () { - // jquery autocomplete refines the search in a setTimeout() parameterized - // with a delay, so we force this delay to 0 s.t. the dropdown is filtered - // directly on the next tick - relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0; + ]); + setTimeout(function () { + // jquery autocomplete refines the search in a setTimeout() parameterized + // with a delay, so we force this delay to 0 s.t. the dropdown is filtered + // directly on the next tick + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0; - // this is done with the hope that tests are - // only started all together... - QUnit.start(); - }, 0); - return { - mock: { - addMockEnvironment: testUtilsMock.addMockEnvironment, - getMockedOwlEnv: testUtilsMock.getMockedOwlEnv, - intercept: testUtilsMock.intercept, - patch: testUtilsMock.patch, - patchDate: testUtilsMock.patchDate, - unpatch: testUtilsMock.unpatch, - fieldsViewGet: testUtilsMock.fieldsViewGet, - patchSetTimeout: testUtilsMock.patchSetTimeout, - }, - dom: { - triggerKeypressEvent: testUtilsDom.triggerKeypressEvent, - triggerMouseEvent: testUtilsDom.triggerMouseEvent, - triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent, - dragAndDrop: testUtilsDom.dragAndDrop, - openDatepicker: testUtilsDom.openDatepicker, - click: testUtilsDom.click, - clickFirst: testUtilsDom.clickFirst, - clickLast: testUtilsDom.clickLast, - triggerEvents: testUtilsDom.triggerEvents, - triggerEvent: testUtilsDom.triggerEvent, - }, - form: { - clickEdit: testUtilsForm.clickEdit, - clickSave: testUtilsForm.clickSave, - clickCreate: testUtilsForm.clickCreate, - clickDiscard: testUtilsForm.clickDiscard, - reload: testUtilsForm.reload, - }, - graph: { - reload: testUtilsGraph.reload, - }, - kanban: { - reload: testUtilsKanban.reload, - clickCreate: testUtilsKanban.clickCreate, - quickCreate: testUtilsKanban.quickCreate, - toggleGroupSettings: testUtilsKanban.toggleGroupSettings, - toggleRecordDropdown: testUtilsKanban.toggleRecordDropdown, - }, - modal: { - clickButton: testUtilsModal.clickButton, - }, - pivot: { - clickMeasure: testUtilsPivot.clickMeasure, - toggleMeasuresDropdown: testUtilsPivot.toggleMeasuresDropdown, - reload: testUtilsPivot.reload, - }, - fields: { - many2one: { - clickOpenDropdown: testUtilsFields.clickOpenM2ODropdown, - clickHighlightedItem: testUtilsFields.clickM2OHighlightedItem, - clickItem: testUtilsFields.clickM2OItem, - searchAndClickItem: testUtilsFields.searchAndClickM2OItem, - }, - editInput: testUtilsFields.editInput, - editSelect: testUtilsFields.editSelect, - editAndTrigger: testUtilsFields.editAndTrigger, - triggerKey: testUtilsFields.triggerKey, - triggerKeydown: testUtilsFields.triggerKeydown, - triggerKeyup: testUtilsFields.triggerKeyup, - }, - file: { - createFile: testUtilsFile.createFile, - dragoverFile: testUtilsFile.dragoverFile, - dropFile: testUtilsFile.dropFile, - dropFiles: testUtilsFile.dropFiles, + // this is done with the hope that tests are + // only started all together... + QUnit.start(); + }, 0); + return { + mock: { + addMockEnvironment: testUtilsMock.addMockEnvironment, + getMockedOwlEnv: testUtilsMock.getMockedOwlEnv, + intercept: testUtilsMock.intercept, + patch: testUtilsMock.patch, + patchDate: testUtilsMock.patchDate, + unpatch: testUtilsMock.unpatch, + fieldsViewGet: testUtilsMock.fieldsViewGet, + patchSetTimeout: testUtilsMock.patchSetTimeout, + }, + dom: { + triggerKeypressEvent: testUtilsDom.triggerKeypressEvent, + triggerMouseEvent: testUtilsDom.triggerMouseEvent, + triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent, + dragAndDrop: testUtilsDom.dragAndDrop, + openDatepicker: testUtilsDom.openDatepicker, + click: testUtilsDom.click, + clickFirst: testUtilsDom.clickFirst, + clickLast: testUtilsDom.clickLast, + triggerEvents: testUtilsDom.triggerEvents, + triggerEvent: testUtilsDom.triggerEvent, + }, + form: { + clickEdit: testUtilsForm.clickEdit, + clickSave: testUtilsForm.clickSave, + clickCreate: testUtilsForm.clickCreate, + clickDiscard: testUtilsForm.clickDiscard, + reload: testUtilsForm.reload, + }, + graph: { + reload: testUtilsGraph.reload, + }, + kanban: { + reload: testUtilsKanban.reload, + clickCreate: testUtilsKanban.clickCreate, + quickCreate: testUtilsKanban.quickCreate, + toggleGroupSettings: testUtilsKanban.toggleGroupSettings, + toggleRecordDropdown: testUtilsKanban.toggleRecordDropdown, + }, + modal: { + clickButton: testUtilsModal.clickButton, + }, + pivot: { + clickMeasure: testUtilsPivot.clickMeasure, + toggleMeasuresDropdown: testUtilsPivot.toggleMeasuresDropdown, + reload: testUtilsPivot.reload, + }, + fields: { + many2one: { + clickOpenDropdown: testUtilsFields.clickOpenM2ODropdown, + clickHighlightedItem: testUtilsFields.clickM2OHighlightedItem, + clickItem: testUtilsFields.clickM2OItem, + searchAndClickItem: testUtilsFields.searchAndClickM2OItem, }, + editInput: testUtilsFields.editInput, + editSelect: testUtilsFields.editSelect, + editAndTrigger: testUtilsFields.editAndTrigger, + triggerKey: testUtilsFields.triggerKey, + triggerKeydown: testUtilsFields.triggerKeydown, + triggerKeyup: testUtilsFields.triggerKeyup, + }, + file: { + createFile: testUtilsFile.createFile, + dragoverFile: testUtilsFile.dragoverFile, + dropFile: testUtilsFile.dropFile, + dropFiles: testUtilsFile.dropFiles, + }, - createActionManager: testUtilsCreate.createActionManager, - createDebugManager: testUtilsCreate.createDebugManager, - createAsyncView: testUtilsCreate.createView, - createCalendarView: testUtilsCreate.createCalendarView, - createControlPanel: testUtilsCreate.createControlPanel, - createView: testUtilsCreate.createView, - createModel: testUtilsCreate.createModel, - createParent: testUtilsCreate.createParent, - makeTestPromise: makeTestPromise, - makeTestPromiseWithAssert: makeTestPromiseWithAssert, - nextMicrotaskTick: nextMicrotaskTick, - nextTick: nextTick, - prepareTarget: testUtilsCreate.prepareTarget, - returnAfterNextAnimationFrame: testUtilsDom.returnAfterNextAnimationFrame, + createActionManager: testUtilsCreate.createActionManager, + createDebugManager: testUtilsCreate.createDebugManager, + createAsyncView: testUtilsCreate.createView, + createCalendarView: testUtilsCreate.createCalendarView, + createControlPanel: testUtilsCreate.createControlPanel, + createView: testUtilsCreate.createView, + createModel: testUtilsCreate.createModel, + createParent: testUtilsCreate.createParent, + makeTestPromise: makeTestPromise, + makeTestPromiseWithAssert: makeTestPromiseWithAssert, + nextMicrotaskTick: nextMicrotaskTick, + nextTick: nextTick, + prepareTarget: testUtilsCreate.prepareTarget, + returnAfterNextAnimationFrame: testUtilsDom.returnAfterNextAnimationFrame, - // backward-compatibility - addMockEnvironment: deprecated(testUtilsMock.addMockEnvironment, 'mock'), - dragAndDrop: deprecated(testUtilsDom.dragAndDrop, 'dom'), - fieldsViewGet: deprecated(testUtilsMock.fieldsViewGet, 'mock'), - intercept: deprecated(testUtilsMock.intercept, 'mock'), - openDatepicker: deprecated(testUtilsDom.openDatepicker, 'dom'), - patch: deprecated(testUtilsMock.patch, 'mock'), - patchDate: deprecated(testUtilsMock.patchDate, 'mock'), - triggerKeypressEvent: deprecated(testUtilsDom.triggerKeypressEvent, 'dom'), - triggerMouseEvent: deprecated(testUtilsDom.triggerMouseEvent, 'dom'), - triggerPositionalMouseEvent: deprecated(testUtilsDom.triggerPositionalMouseEvent, 'dom'), - unpatch: deprecated(testUtilsMock.unpatch, 'mock'), - }; - }); + // backward-compatibility + addMockEnvironment: deprecated(testUtilsMock.addMockEnvironment, 'mock'), + dragAndDrop: deprecated(testUtilsDom.dragAndDrop, 'dom'), + fieldsViewGet: deprecated(testUtilsMock.fieldsViewGet, 'mock'), + intercept: deprecated(testUtilsMock.intercept, 'mock'), + openDatepicker: deprecated(testUtilsDom.openDatepicker, 'dom'), + patch: deprecated(testUtilsMock.patch, 'mock'), + patchDate: deprecated(testUtilsMock.patchDate, 'mock'), + triggerKeypressEvent: deprecated(testUtilsDom.triggerKeypressEvent, 'dom'), + triggerMouseEvent: deprecated(testUtilsDom.triggerMouseEvent, 'dom'), + triggerPositionalMouseEvent: deprecated(testUtilsDom.triggerPositionalMouseEvent, 'dom'), + unpatch: deprecated(testUtilsMock.unpatch, 'mock'), + }; }); diff --git a/addons/web/static/tests/helpers/test_utils_create.js b/addons/web/static/tests/helpers/test_utils_create.js index 717b845e80e..21bc6cc7850 100644 --- a/addons/web/static/tests/helpers/test_utils_create.js +++ b/addons/web/static/tests/helpers/test_utils_create.js @@ -10,70 +10,68 @@ odoo.define('web.test_utils_create', function (require) { * testUtils file. */ - var ActionManager = require('web.ActionManager'); - var config = require('web.config'); - var ControlPanelView = require('web.ControlPanelView'); - var concurrency = require('web.concurrency'); - var DebugManager = require('web.DebugManager.Backend'); - var dom = require('web.dom'); - var testUtilsMock = require('web.test_utils_mock'); - var Widget = require('web.Widget'); + const ActionManager = require('web.ActionManager'); + const concurrency = require('web.concurrency'); + const config = require('web.config'); + const ControlPanelView = require('web.ControlPanelView'); + const DebugManager = require('web.DebugManager.Backend'); + const dom = require('web.dom'); + const testUtilsMock = require('web.test_utils_mock'); + const Widget = require('web.Widget'); /** - * create and return an instance of ActionManager with all rpcs going through a + * Create and return an instance of ActionManager with all rpcs going through a * mock method using the data, actions and archs objects as sources. * - * @param {Object} [params] + * @param {Object} [params={}] * @param {Object} [params.actions] the actions given to the mock server * @param {Object} [params.archs] this archs given to the mock server * @param {Object} [params.data] the business data given to the mock server * @param {function} [params.mockRPC] - * @returns {ActionManager} + * @returns {Promise<ActionManager>} */ - async function createActionManager(params) { - params = params || {}; + async function createActionManager(params = {}) { const target = prepareTarget(params.debug); - var widget = new Widget(); + const widget = new Widget(); // when 'document' addon is installed, the sidebar does a 'search_read' on // model 'ir_attachment' each time a record is open, so we monkey-patch // 'mockRPC' to mute those RPCs, so that the tests can be written uniformly, // whether or not 'document' is installed - var mockRPC = params.mockRPC; - _.extend(params, { - mockRPC: function (route, args) { + const mockRPC = params.mockRPC; + Object.assign(params, { + async mockRPC(route, args) { if (args.model === 'ir.attachment') { - return Promise.resolve([]); + return []; } if (mockRPC) { return mockRPC.apply(this, arguments); } - return this._super.apply(this, arguments); + return this._super(...arguments); }, }); - testUtilsMock.addMockEnvironment(widget, _.defaults(params, { debounce: false })); + testUtilsMock.addMockEnvironment(widget, Object.assign({ debounce: false }, params)); await widget.prependTo(target); - widget.$el.addClass('o_web_client'); + widget.el.classList.add('o_web_client'); if (config.device.isMobile) { - widget.$el.addClass('o_touch_device'); + widget.el.classList.add('o_touch_device'); } - var userContext = params.context && params.context.user_context || {}; - var actionManager = new ActionManager(widget, userContext); + const userContext = params.context && params.context.user_context || {}; + const actionManager = new ActionManager(widget, userContext); - var originalDestroy = ActionManager.prototype.destroy; + const originalDestroy = ActionManager.prototype.destroy; actionManager.destroy = function () { actionManager.destroy = originalDestroy; widget.destroy(); }; - var fragment = document.createDocumentFragment(); - return actionManager.appendTo(fragment).then(function () { - dom.append(widget.$el, fragment, { - callbacks: [{ widget: actionManager }], - in_DOM: true, - }); - return actionManager; + const fragment = document.createDocumentFragment(); + await actionManager.appendTo(fragment); + dom.append(widget.el, fragment, { + callbacks: [{ widget: actionManager }], + in_DOM: true, }); + return actionManager; } /** @@ -85,21 +83,22 @@ odoo.define('web.test_utils_create', function (require) { * makes the calendar scroll to 6:00 in a setTimeout(0), which might have an * impact according to where we want to trigger positional clicks. * - * @param {Object} params see @createView + * @param {Object} params @see createView * @param {Object} [options] * @param {boolean} [options.positionalClicks=false] * @returns {Promise<CalendarController>} */ async function createCalendarView(params, options) { - var calendar = await createView(params); + const calendar = await createView(params); if (!options || !options.positionalClicks) { return calendar; } - var $view = $('#qunit-fixture').contents(); - $view.prependTo('body'); - var destroy = calendar.destroy; - calendar.destroy = function () { - $view.remove(); + const viewElements = [...document.getElementById('qunit-fixture').children]; + viewElements.forEach(el => document.body.prepend(el)); + + const destroy = calendar.destroy; + calendar.destroy = () => { + viewElements.forEach(el => el.remove()); destroy(); }; await concurrency.delay(0); @@ -107,7 +106,7 @@ odoo.define('web.test_utils_create', function (require) { } /** - * create a controlPanel from various parameters. + * Create a controlPanel from various parameters. * * It returns an instance of ControlPanelController * @@ -138,17 +137,17 @@ odoo.define('web.test_utils_create', function (require) { * * @returns {Promise<ControlPanel>} resolves with an instance of the ControlPanelController */ - function createControlPanel(params) { + async function createControlPanel(params) { params = params || {}; const target = prepareTarget(params.debug); // reproduce the DOM environment of a view control panel - var $webClient = $('<div>').addClass('o_web_client').prependTo(target); - var $actionManager = $('<div>').addClass('o_action_manager').appendTo($webClient); - var $action = $('<div>').addClass('o_action').appendTo($actionManager); + const $webClient = $('<div>').addClass('o_web_client').prependTo(target); + const $actionManager = $('<div>').addClass('o_action_manager').appendTo($webClient); + const $action = $('<div>').addClass('o_action').appendTo($actionManager); - var widget = new Widget(); + const widget = new Widget(); // add mock environment: mock server, session, fieldviewget, ... - var mockServer = testUtilsMock.addMockEnvironment(widget, params); + const mockServer = testUtilsMock.addMockEnvironment(widget, params); if (!params.viewInfo) { try { params.viewInfo = testUtilsMock.fieldsViewGet(mockServer, params); @@ -159,37 +158,35 @@ odoo.define('web.test_utils_create', function (require) { } } - var viewOptions = _.defaults({}, params.viewOptions, { + const viewOptions = Object.assign({ context: params.context, modelName: params.model, searchMenuTypes: params.searchMenuTypes, viewInfo: params.viewInfo, - }); - var controlPanelView = new ControlPanelView(viewOptions); - return controlPanelView.getController(widget).then(function (controlPanel) { - // override the controlPanel's 'destroy' so that it calls 'destroy' on - // the widget instead, as the widget is the parent of the controlPanel - // and the mockServer. - controlPanel.__destroy = controlPanel.destroy; - controlPanel.destroy = function () { - // remove the override to properly destroy the controlPanel and its - // children when it will be called the second time (by its parent) - delete controlPanel.destroy; - widget.destroy(); - $webClient.remove(); - }; + }, params.viewOptions); + const controlPanelView = new ControlPanelView(viewOptions); + const controlPanel = await controlPanelView.getController(widget); + // override the controlPanel's 'destroy' so that it calls 'destroy' on + // the widget instead, as the widget is the parent of the controlPanel + // and the mockServer. + controlPanel.__destroy = controlPanel.destroy; + controlPanel.destroy = function () { + // remove the override to properly destroy the controlPanel and its + // children when it will be called the second time (by its parent) + delete controlPanel.destroy; + widget.destroy(); + $webClient.remove(); + }; - // render the controlPanel in a fragment as it must be able to render - // correctly without being in the DOM - var fragment = document.createDocumentFragment(); - return controlPanel.appendTo(fragment).then(function () { - dom.prepend($action, fragment, { - callbacks: [{ widget: controlPanel }], - in_DOM: true, - }); - return controlPanel; - }); + // render the controlPanel in a fragment as it must be able to render + // correctly without being in the DOM + const fragment = document.createDocumentFragment(); + await controlPanel.appendTo(fragment); + dom.prepend($action, fragment, { + callbacks: [{ widget: controlPanel }], + in_DOM: true, }); + return controlPanel; } /** @@ -197,39 +194,39 @@ odoo.define('web.test_utils_create', function (require) { * mock method, assuming that the user has access rights, and is an admin. * * @param {Object} [params={}] + * @returns {DebugManager} */ - var createDebugManager = function (params) { - params = params || {}; - var mockRPC = params.mockRPC; - _.extend(params, { - mockRPC: function (route, args) { + function createDebugManager(params = {}) { + const mockRPC = params.mockRPC; + Object.assign(params, { + async mockRPC(route, args) { if (args.method === 'check_access_rights') { - return Promise.resolve(true); + return true; } if (args.method === 'xmlid_to_res_id') { - return Promise.resolve(true); + return true; } if (mockRPC) { return mockRPC.apply(this, arguments); } - return this._super.apply(this, arguments); + return this._super(...arguments); }, session: { - user_has_group: function (group) { + async user_has_group(group) { if (group === 'base.group_no_one') { - return Promise.resolve(true); + return true; } - return this._super.apply(this, arguments); + return this._super(...arguments); }, }, }); - var debugManager = new DebugManager(); + const debugManager = new DebugManager(); testUtilsMock.addMockEnvironment(debugManager, params); return debugManager; - }; + } /** - * create a model from given parameters. + * Create a model from given parameters. * * @param {Object} params This object will be given to addMockEnvironment, so * any parameters from that method applies @@ -237,9 +234,9 @@ odoo.define('web.test_utils_create', function (require) { * @returns {Model} */ function createModel(params) { - var widget = new Widget(); + const widget = new Widget(); - var model = new params.Model(widget); + const model = new params.Model(widget); testUtilsMock.addMockEnvironment(widget, params); @@ -256,20 +253,20 @@ odoo.define('web.test_utils_create', function (require) { } /** - * create a widget parent from given parameters. + * Create a widget parent from given parameters. * * @param {Object} params This object will be given to addMockEnvironment, so * any parameters from that method applies * @returns {Widget} */ function createParent(params) { - var widget = new Widget(); + const widget = new Widget(); testUtilsMock.addMockEnvironment(widget, params); return widget; } /** - * create a view from various parameters. Here, a view means a javascript + * Create a view from various parameters. Here, a view means a javascript * instance of an AbstractView class, such as a form view, a list view or a * kanban view. * @@ -291,37 +288,42 @@ odoo.define('web.test_utils_create', function (require) { * Note that this is particularly useful if you want to intercept events going * up in the init process of the view, because there are no other way to do it * after this method returns - * @param {Boolean} [params.doNotDisableAHref=false] will not preventDefault on the A elements of the view if true. + * @param {Boolean} [params.doNotDisableAHref=false] will not preventDefault on the A elements of the view if true. * Default is false. - * @returns {Promise<AbstractController>} resolves with the instance of the view + * @returns {Promise<AbstractController>} the instance of the view */ async function createView(params) { const target = prepareTarget(params.debug); - var widget = new Widget(); + const widget = new Widget(); // reproduce the DOM environment of views - var $webClient = $('<div>').addClass('o_web_client').prependTo(target); - var $actionManager = $('<div>').addClass('o_action_manager').appendTo($webClient); - + const webClient = Object.assign(document.createElement('div'), { + className: 'o_web_client', + }); + const actionManager = Object.assign(document.createElement('div'), { + className: 'o_action_manager', + }); + target.prepend(webClient); + webClient.append(actionManager); // add mock environment: mock server, session, fieldviewget, ... - var mockServer = testUtilsMock.addMockEnvironment(widget, params); - var viewInfo = testUtilsMock.fieldsViewGet(mockServer, params); + const mockServer = testUtilsMock.addMockEnvironment(widget, params); + const viewInfo = testUtilsMock.fieldsViewGet(mockServer, params); // create the view - var View = params.View; - var viewOptions = _.defaults({}, params.viewOptions, { + const View = params.View; + const viewOptions = Object.assign({ modelName: params.model || 'foo', ids: 'res_id' in params ? [params.res_id] : undefined, currentId: 'res_id' in params ? params.res_id : undefined, domain: params.domain || [], context: params.context || {}, hasSidebar: false, - }); + }, params.viewOptions); // patch the View to handle the groupBy given in params, as we can't give it // in init (unlike the domain and context which can be set in the action) testUtilsMock.patch(View, { - _updateMVCParams: function () { - this._super.apply(this, arguments); + _updateMVCParams() { + this._super(...arguments); this.loadParams.groupedBy = params.groupBy || viewOptions.groupBy || []; testUtilsMock.unpatch(View); }, @@ -330,7 +332,7 @@ odoo.define('web.test_utils_create', function (require) { viewOptions.hasSelectors = params.hasSelectors; } - var view; + let view; if (viewInfo.type === 'controlpanel' || viewInfo.type === 'search') { // TODO: probably needs to create an helper just for that view = new params.View({ @@ -347,42 +349,40 @@ odoo.define('web.test_utils_create', function (require) { } if (params.interceptsPropagate) { - _.each(params.interceptsPropagate, function (cb, name) { - testUtilsMock.intercept(widget, name, cb, true); - }); + for (const name in params.interceptsPropagate) { + testUtilsMock.intercept(widget, name, params.interceptsPropagate[name], true); + } } - return view.getController(widget).then(function (view) { - // override the view's 'destroy' so that it calls 'destroy' on the widget - // instead, as the widget is the parent of the view and the mockServer. - view.__destroy = view.destroy; - view.destroy = function () { - // remove the override to properly destroy the view and its children - // when it will be called the second time (by its parent) - delete view.destroy; - widget.destroy(); - $webClient.remove(); - }; + const viewController = await view.getController(widget); + // override the view's 'destroy' so that it calls 'destroy' on the widget + // instead, as the widget is the parent of the view and the mockServer. + viewController.__destroy = viewController.destroy; + viewController.destroy = function () { + // remove the override to properly destroy the viewController and its children + // when it will be called the second time (by its parent) + delete viewController.destroy; + widget.destroy(); + webClient.remove(); + }; - // render the view in a fragment as they must be able to render correctly - // without being in the DOM - var fragment = document.createDocumentFragment(); - return view.appendTo(fragment).then(() => { - dom.prepend($actionManager, fragment, { - callbacks: [{ widget: view }], - in_DOM: true, - }); + // render the viewController in a fragment as they must be able to render correctly + // without being in the DOM + const fragment = document.createDocumentFragment(); + await viewController.appendTo(fragment); + dom.prepend(actionManager, fragment, { + callbacks: [{ widget: viewController }], + in_DOM: true, + }); - if (!params.doNotDisableAHref) { - [...view.el.getElementsByTagName('A')].forEach((elem) => { - elem.addEventListener('click', (ev) => { - ev.preventDefault(); - }); - }); - } - return view; + if (!params.doNotDisableAHref) { + [...viewController.el.getElementsByTagName('A')].forEach(elem => { + elem.addEventListener('click', ev => { + ev.preventDefault(); + }); }); - }); + } + return viewController; } /** @@ -395,17 +395,17 @@ odoo.define('web.test_utils_create', function (require) { */ function prepareTarget(debug = false) { document.body.classList.toggle('debug', debug); - return debug ? document.body : document.querySelector("#qunit-fixture"); + return debug ? document.body : document.getElementById('qunit-fixture'); } return { - createActionManager: createActionManager, - createCalendarView: createCalendarView, - createControlPanel: createControlPanel, - createDebugManager: createDebugManager, - createModel: createModel, - createParent: createParent, - createView: createView, - prepareTarget: prepareTarget, + createActionManager, + createCalendarView, + createControlPanel, + createDebugManager, + createModel, + createParent, + createView, + prepareTarget, }; }); diff --git a/addons/web/static/tests/helpers/test_utils_dom.js b/addons/web/static/tests/helpers/test_utils_dom.js index 3511bb398ce..ceca0d21fc9 100644 --- a/addons/web/static/tests/helpers/test_utils_dom.js +++ b/addons/web/static/tests/helpers/test_utils_dom.js @@ -1,7 +1,7 @@ odoo.define('web.test_utils_dom', function (require) { "use strict"; - var concurrency = require('web.concurrency'); + const concurrency = require('web.concurrency'); /** * DOM Test Utils @@ -13,40 +13,29 @@ odoo.define('web.test_utils_dom', function (require) { */ //------------------------------------------------------------------------- - // Private + // Private functions //------------------------------------------------------------------------- - function keyboardEventBubble(args) { - return Object.assign({}, args, { bubbles: true, keyCode: args.which }); - } - - function mouseEventMapping(args) { - return Object.assign({}, args, { - view: window, - bubbles: true, - cancelable: true, - clientX: args ? args.pageX : undefined, - clientY: args ? args.pageY : undefined, - }); - } - - function mouseEventNoBubble(args) { - return Object.assign({}, args, { - view: window, - bubbles: false, - cancelable: false, - clientX: args ? args.pageX : undefined, - clientY: args ? args.pageY : undefined, - }); - } - - function onlyBubble(args) { - return Object.assign({}, args, { - bubbles: true - }); - } - - const eventTypes = { + // TriggerEvent helpers + const keyboardEventBubble = args => Object.assign({}, args, { bubbles: true, keyCode: args.which }); + const mouseEventMapping = args => Object.assign({}, args, { + bubbles: true, + cancelable: true, + clientX: args ? args.pageX : undefined, + clientY: args ? args.pageY : undefined, + view: window, + }); + const mouseEventNoBubble = args => Object.assign({}, args, { + bubbles: false, + cancelable: false, + clientX: args ? args.pageX : undefined, + clientY: args ? args.pageY : undefined, + view: window, + }); + const noBubble = args => Object.assign({}, args, { bubbles: false }); + const onlyBubble = args => Object.assign({}, args, { bubbles: true }); + // TriggerEvent constructor/args processor mapping + const EVENT_TYPES = { auxclick: { constructor: MouseEvent, processParameters: mouseEventMapping }, click: { constructor: MouseEvent, processParameters: mouseEventMapping }, contextmenu: { constructor: MouseEvent, processParameters: mouseEventMapping }, @@ -60,17 +49,9 @@ odoo.define('web.test_utils_dom', function (require) { mouseover: { constructor: MouseEvent, processParameters: mouseEventMapping }, mouseout: { constructor: MouseEvent, processParameters: mouseEventMapping }, - focus: { - constructor: FocusEvent, processParameters: (args) => { - return Object.assign({}, args, { bubbles: false }); - } - }, + focus: { constructor: FocusEvent, processParameters: noBubble }, focusin: { constructor: FocusEvent, processParameters: onlyBubble }, - blur: { - constructor: FocusEvent, processParameters: (args) => { - return Object.assign({}, args, { bubbles: false }); - } - }, + blur: { constructor: FocusEvent, processParameters: noBubble }, cut: { constructor: ClipboardEvent, processParameters: onlyBubble }, copy: { constructor: ClipboardEvent, processParameters: onlyBubble }, @@ -95,7 +76,10 @@ odoo.define('web.test_utils_dom', function (require) { * @param {Object} node * @returns {boolean} */ - function isEventTarget(node) { + function _isEventTarget(node) { + if (!node) { + throw new Error(`Provided node is ${node}.`); + } if (node instanceof window.top.EventTarget) { return true; } @@ -105,7 +89,7 @@ odoo.define('web.test_utils_dom', function (require) { } //------------------------------------------------------------------------- - // Public + // Public functions //------------------------------------------------------------------------- /** @@ -126,7 +110,7 @@ odoo.define('web.test_utils_dom', function (require) { if (typeof el === 'string') { el = $(el); } - if (isEventTarget(el)) { + if (_isEventTarget(el)) { // EventTarget matches = [el]; } else { @@ -174,8 +158,8 @@ odoo.define('web.test_utils_dom', function (require) { * element event if it is invisible * @returns {Promise} */ - function clickFirst(el, options) { - return click(el, _.extend({}, options, { first: true })); + async function clickFirst(el, options) { + return click(el, Object.assign({}, options, { first: true })); } /** @@ -189,12 +173,12 @@ odoo.define('web.test_utils_dom', function (require) { * element event if it is invisible * @returns {Promise} */ - function clickLast(el, options) { - return click(el, _.extend({}, options, { last: true })); + async function clickLast(el, options) { + return click(el, Object.assign({}, options, { last: true })); } /** - * simulate a drag and drop operation between 2 jquery nodes: $el and $to. + * Simulate a drag and drop operation between 2 jquery nodes: $el and $to. * This is a crude simulation, with only the mousedown, mousemove and mouseup * events, but it is enough to help test drag and drop operations with jqueryUI * sortable. @@ -225,25 +209,25 @@ odoo.define('web.test_utils_dom', function (require) { */ async function dragAndDrop($el, $to, options) { let el = null; - if (isEventTarget($el)) { + if (_isEventTarget($el)) { el = $el; $el = $(el); } - if (isEventTarget($to)) { + if (_isEventTarget($to)) { $to = $($to); } options = options || {}; - var position = options.position || 'center'; - var elementCenter = $el.offset(); - var toOffset = $to.offset(); + const position = options.position || 'center'; + const elementCenter = $el.offset(); + const toOffset = $to.offset(); - if (_.isObject(position)) { + if (typeof position === 'object') { toOffset.top += position.top + 1; toOffset.left += position.left + 1; } else { toOffset.top += $to.outerHeight() / 2; toOffset.left += $to.outerWidth() / 2; - var vertical_offset = (toOffset.top < elementCenter.top) ? -1 : 1; + const vertical_offset = (toOffset.top < elementCenter.top) ? -1 : 1; if (position === 'top') { toOffset.top -= $to.outerHeight() / 2 + vertical_offset; } else if (position === 'bottom') { @@ -257,7 +241,7 @@ odoo.define('web.test_utils_dom', function (require) { if ($to[0].ownerDocument !== document) { // we are in an iframe - var bound = $('iframe')[0].getBoundingClientRect(); + const bound = $('iframe')[0].getBoundingClientRect(); toOffset.left += bound.left; toOffset.top += bound.top; } @@ -301,11 +285,11 @@ odoo.define('web.test_utils_dom', function (require) { } /** - * Opens the datepicker of a given element. + * Open the datepicker of a given element. * * @param {jQuery} $datepickerEl element to which a datepicker is attached */ - function openDatepicker($datepickerEl) { + async function openDatepicker($datepickerEl) { return click($datepickerEl.find('.o_datepicker_input')); } @@ -325,7 +309,7 @@ odoo.define('web.test_utils_dom', function (require) { } /** - * Triggers an event on the specified target. + * Trigger an event on the specified target. * This function will dispatch a native event to an EventTarget or a * jQuery event to a jQuery object. This behaviour can be overridden by the * jquery option. @@ -340,7 +324,7 @@ odoo.define('web.test_utils_dom', function (require) { async function triggerEvent(el, eventType, eventAttrs = {}, fast = false) { let matches; let selectorMsg = ""; - if (isEventTarget(el)) { + if (_isEventTarget(el)) { matches = [el]; } else { matches = [...el]; @@ -353,15 +337,15 @@ odoo.define('web.test_utils_dom', function (require) { const target = matches[0]; let event; - if (!eventTypes[eventType] && !eventTypes[eventType.type]) { + if (!EVENT_TYPES[eventType] && !EVENT_TYPES[eventType.type]) { event = new Event(eventType, Object.assign({}, eventAttrs, { bubbles: true })); } else { if (typeof eventType === "object") { - const { constructor, processParameters } = eventTypes[eventType.type]; + const { constructor, processParameters } = EVENT_TYPES[eventType.type]; const eventParameters = processParameters(eventType); event = new constructor(eventType.type, eventParameters); } else { - const { constructor, processParameters } = eventTypes[eventType]; + const { constructor, processParameters } = EVENT_TYPES[eventType]; event = new constructor(eventType, processParameters(eventAttrs)); } } @@ -370,7 +354,7 @@ odoo.define('web.test_utils_dom', function (require) { } /** - * Triggers multiple events on the specified element. + * Trigger multiple events on the specified element. * * @param {EventTarget|EventTarget[]} el * @param {string[]} events the events you want to trigger @@ -387,8 +371,30 @@ odoo.define('web.test_utils_dom', function (require) { } for (let e = 0; e < events.length; e++) { - await triggerEvent(el, events[e], {}); + await triggerEvent(el, events[e]); + } + } + + /** + * Simulate a keypress event for a given character + * + * @param {string} char the character, or 'ENTER' + * @returns {Promise} + */ + async function triggerKeypressEvent(char) { + let keycode; + if (char === 'Enter') { + keycode = $.ui.keyCode.ENTER; + } else if (char === "Tab") { + keycode = $.ui.keyCode.TAB; + } else { + keycode = char.charCodeAt(0); } + return triggerEvent(document.body, 'keypress', { + key: char, + keyCode: keycode, + which: keycode, + }); } /** @@ -400,7 +406,7 @@ odoo.define('web.test_utils_dom', function (require) { * @param {string} type a mouse event type, such as 'mousedown' or 'mousemove' * @returns {Promise} */ - function triggerMouseEvent($el, type) { + async function triggerMouseEvent($el, type) { const el = $el instanceof jQuery ? $el[0] : $el; if (!el) { throw new Error(`no target found to trigger MouseEvent`); @@ -426,9 +432,9 @@ odoo.define('web.test_utils_dom', function (require) { * @param {string} type a mouse event type, such as 'mousedown' or 'mousemove' * @returns {HTMLElement} */ - function triggerPositionalMouseEvent(x, y, type) { - var ev = document.createEvent("MouseEvent"); - var el = document.elementFromPoint(x, y); + async function triggerPositionalMouseEvent(x, y, type) { + const ev = document.createEvent("MouseEvent"); + const el = document.elementFromPoint(x, y); ev.initMouseEvent( type, true /* bubble */, @@ -442,39 +448,17 @@ odoo.define('web.test_utils_dom', function (require) { return el; } - /** - * simulate a keypress event for a given character - * - * @param {string} char the character, or 'ENTER' - * @returns {Promise} - */ - function triggerKeypressEvent(char) { - var keycode; - if (char === 'Enter') { - keycode = $.ui.keyCode.ENTER; - } else if (char === "Tab") { - keycode = $.ui.keyCode.TAB; - } else { - keycode = char.charCodeAt(0); - } - return triggerEvent(document.body, 'keypress', { - key: char, - keyCode: keycode, - which: keycode, - }); - } - return { - click: click, - clickFirst: clickFirst, - clickLast: clickLast, - dragAndDrop: dragAndDrop, - openDatepicker: openDatepicker, - returnAfterNextAnimationFrame: returnAfterNextAnimationFrame, - triggerEvent: triggerEvent, - triggerEvents: triggerEvents, - triggerKeypressEvent: triggerKeypressEvent, - triggerMouseEvent: triggerMouseEvent, - triggerPositionalMouseEvent: triggerPositionalMouseEvent, + click, + clickFirst, + clickLast, + dragAndDrop, + openDatepicker, + returnAfterNextAnimationFrame, + triggerEvent, + triggerEvents, + triggerKeypressEvent, + triggerMouseEvent, + triggerPositionalMouseEvent, }; }); diff --git a/addons/web/static/tests/helpers/test_utils_fields.js b/addons/web/static/tests/helpers/test_utils_fields.js index 4646fd98d83..060cfc1d437 100644 --- a/addons/web/static/tests/helpers/test_utils_fields.js +++ b/addons/web/static/tests/helpers/test_utils_fields.js @@ -10,8 +10,14 @@ odoo.define('web.test_utils_fields', function (require) { * testUtils file. */ - var concurrency = require('web.concurrency'); - var domUtils = require('web.test_utils_dom'); + const testUtilsDom = require('web.test_utils_dom'); + + const ARROW_KEYS_MAPPING = { + down: 'ArrowDown', + left: 'ArrowLeft', + right: 'ArrowRight', + up: 'ArrowUp', + }; //------------------------------------------------------------------------- // Public functions @@ -23,13 +29,13 @@ odoo.define('web.test_utils_fields', function (require) { * @param {string} fieldName * @param {[string]} selector if set, this will restrict the search for the m2o * input + * @returns {Promise} */ - function clickM2OHighlightedItem(fieldName, selector) { - var m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`; - var $dropdown = $(m2oSelector).autocomplete('widget'); + async function clickM2OHighlightedItem(fieldName, selector) { + const m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`; + const $dropdown = $(m2oSelector).autocomplete('widget'); // clicking on an li (no matter which one), will select the focussed one - $dropdown.find('li:first()').click(); - return concurrency.delay(0); + return testUtilsDom.click($dropdown[0].querySelector('li')); } /** @@ -44,17 +50,15 @@ odoo.define('web.test_utils_fields', function (require) { * @param {string} searchText * @returns {Promise} */ - function clickM2OItem(fieldName, searchText) { - var m2oSelector = `.o_field_many2one[name=${fieldName}] input`; - var $dropdown = $(m2oSelector).autocomplete('widget'); - var $target = $dropdown.find(`li:contains(${searchText})`).first(); + async function clickM2OItem(fieldName, searchText) { + const m2oSelector = `.o_field_many2one[name=${fieldName}] input`; + const $dropdown = $(m2oSelector).autocomplete('widget'); + const $target = $dropdown.find(`li:contains(${searchText})`).first(); if ($target.length !== 1 || !$target.is(':visible')) { - throw new Error('Menu item should be unique and visible'); + throw new Error('Menu item should be visible'); } - $target.mouseenter(); - $target.click(); - - return concurrency.delay(0); + $target.mouseenter(); // This is NOT a mouseenter event. See jquery.js:5516 for more headaches. + return testUtilsDom.click($target); } /** @@ -63,16 +67,16 @@ odoo.define('web.test_utils_fields', function (require) { * @param {string} fieldName * @param {[string]} selector if set, this will restrict the search for the m2o * input - * @returns {HTMLInputElement} the main many2one input + * @returns {Promise<HTMLInputElement>} the main many2one input */ async function clickOpenM2ODropdown(fieldName, selector) { - var m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`; - var matches = document.querySelectorAll(m2oSelector); + const m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`; + const matches = document.querySelectorAll(m2oSelector); if (matches.length !== 1) { throw new Error(`cannot open m2o: selector ${selector} has been found ${matches.length} instead of 1`); } - await domUtils.click(matches[0]); + await testUtilsDom.click(matches[0]); return matches[0]; } @@ -88,7 +92,7 @@ odoo.define('web.test_utils_fields', function (require) { * @param {string[]} events * @returns {Promise} */ - function editAndTrigger(el, value, events) { + async function editAndTrigger(el, value, events) { if (el instanceof jQuery) { if (el.length !== 1) { throw new Error(`target ${el.selector} has length ${el.length} instead of 1`); @@ -97,7 +101,7 @@ odoo.define('web.test_utils_fields', function (require) { } else { el.value = value; } - return domUtils.triggerEvents(el, events); + return testUtilsDom.triggerEvents(el, events); } /** @@ -112,7 +116,7 @@ odoo.define('web.test_utils_fields', function (require) { * @param {string|number} value * @returns {Promise} */ - function editInput(el, value) { + async function editInput(el, value) { return editAndTrigger(el, value, ['input']); } @@ -143,30 +147,22 @@ odoo.define('web.test_utils_fields', function (require) { * testUtils.fields.many2one.searchAndClickM2OItem('partner_id', {search: 'George'}); * * @param {string} fieldName - * @param {[Object]} options - * @param {[string]} options.selector - * @param {[string]} options.search - * @param {[string]} options.item + * @param {[Object]} [options = {}] + * @param {[string]} [options.selector] + * @param {[string]} [options.search] + * @param {[string]} [options.item] * @returns {Promise} */ - function searchAndClickM2OItem(fieldName, options) { - options = options || {}; - - return clickOpenM2ODropdown(fieldName, options.selector).then(function (input) { - var def; - if (options.search) { - input.value = options.search; - input.dispatchEvent(new Event('input')); - def = concurrency.delay(0); - } - return Promise.resolve(def).then(function () { - if (options.item) { - return clickM2OItem(fieldName, options.item); - } else { - return clickM2OHighlightedItem(fieldName, options.selector); - } - }); - }); + async function searchAndClickM2OItem(fieldName, options = {}) { + const input = await clickOpenM2ODropdown(fieldName, options.selector); + if (options.search) { + await editInput(input, options.search); + } + if (options.item) { + return clickM2OItem(fieldName, options.item); + } else { + return clickM2OHighlightedItem(fieldName, options.selector); + } } /** @@ -181,7 +177,15 @@ odoo.define('web.test_utils_fields', function (require) { */ function triggerKey(type, $el, keyCode) { type = 'key' + type; + const params = {}; if (typeof keyCode === 'string') { + // Key (new API) + if (keyCode in ARROW_KEYS_MAPPING) { + params.key = ARROW_KEYS_MAPPING[keyCode]; + } else { + params.key = keyCode[0].toUpperCase() + keyCode.slice(1).toLowerCase(); + } + // KeyCode/which (jQuery) if (keyCode.length > 1) { keyCode = keyCode.toUpperCase(); keyCode = $.ui.keyCode[keyCode]; @@ -189,8 +193,9 @@ odoo.define('web.test_utils_fields', function (require) { keyCode = keyCode.charCodeAt(0); } } - $el.trigger({ type: type, which: keyCode, keyCode: keyCode }); - return concurrency.delay(0); + params.keyCode = keyCode; + params.which = keyCode; + return testUtilsDom.triggerEvent($el, type, params); } /** @@ -216,15 +221,15 @@ odoo.define('web.test_utils_fields', function (require) { } return { - clickM2OHighlightedItem: clickM2OHighlightedItem, - clickM2OItem: clickM2OItem, - clickOpenM2ODropdown: clickOpenM2ODropdown, - editAndTrigger: editAndTrigger, - editInput: editInput, - editSelect: editSelect, - searchAndClickM2OItem: searchAndClickM2OItem, - triggerKey: triggerKey, - triggerKeydown: triggerKeydown, - triggerKeyup: triggerKeyup, + clickM2OHighlightedItem, + clickM2OItem, + clickOpenM2ODropdown, + editAndTrigger, + editInput, + editSelect, + searchAndClickM2OItem, + triggerKey, + triggerKeydown, + triggerKeyup, }; }); diff --git a/addons/web/static/tests/helpers/test_utils_mock.js b/addons/web/static/tests/helpers/test_utils_mock.js index e72dcc0d692..bb2d1ef24a5 100644 --- a/addons/web/static/tests/helpers/test_utils_mock.js +++ b/addons/web/static/tests/helpers/test_utils_mock.js @@ -465,7 +465,7 @@ function intercept(widget, eventName, fn, propagate) { * @param {integer} hours the digits for hours (24h) * @param {integer} minutes * @param {integer} seconds - * @returns {function} a callback to unpatch window.Date. + * @returns {Function} a callback to unpatch window.Date. */ function patchDate(year, month, day, hours, minutes, seconds) { var RealDate = window.Date; diff --git a/addons/web/static/tests/helpers/test_utils_modal.js b/addons/web/static/tests/helpers/test_utils_modal.js index 5587eecfc01..ca3eeef11a7 100644 --- a/addons/web/static/tests/helpers/test_utils_modal.js +++ b/addons/web/static/tests/helpers/test_utils_modal.js @@ -1,36 +1,26 @@ odoo.define('web.test_utils_modal', function (require) { -"use strict"; + "use strict"; -/** - * Modal Test Utils - * - * This module defines various utility functions to help test pivot views. - * - * Note that all methods defined in this module are exported in the main - * testUtils file. - */ + /** + * Modal Test Utils + * + * This module defines various utility functions to help test pivot views. + * + * Note that all methods defined in this module are exported in the main + * testUtils file. + */ -var core = require('web.core'); -var concurrency = require('web.concurrency'); + const { _t } = require('web.core'); + const testUtilsDom = require('web.test_utils_dom'); -/** - * Click on a button in the footer of a modal (which contains a given string). - * Note that this method checks the unicity of the button. - * - * @param {string} text (in english: this method will perform the translation) - */ -function clickButton(text) { - var selector = `.modal-footer button:contains(${core._t(text)})`; - var $button = $(selector); - if ($button.length !== 1) { - throw new Error(`Found ${$button.length} button(s) containing '${text}'`); + /** + * Click on a button in the footer of a modal (which contains a given string). + * + * @param {string} text (in english: this method will perform the translation) + */ + function clickButton(text) { + return testUtilsDom.click($(`.modal-footer button:contains(${_t(text)})`)); } - $button.click(); - return concurrency.delay(0); -} - -return { - clickButton: clickButton, -}; + return { clickButton }; }); |
