summaryrefslogtreecommitdiff
path: root/addons/web/static
diff options
context:
space:
mode:
authorJulien Mougenot <jum@odoo.com>2020-02-25 12:52:41 +0000
committerJulien Mougenot <jum@odoo.com>2020-03-13 09:37:29 +0000
commit384e516218375000461aa3fcb0ac1b9eaa960fb3 (patch)
tree89d6faece5edea9a4f182b7a6d2e83cbb6f540ce /addons/web/static
parent11ba58a311311b798a68183df18673e562bb97d3 (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')
-rw-r--r--addons/web/static/src/js/chrome/action_mixin.js8
-rw-r--r--addons/web/static/src/js/core/custom_hooks.js10
-rw-r--r--addons/web/static/src/js/main.js1
-rw-r--r--addons/web/static/tests/helpers/qunit_asserts.js10
-rw-r--r--addons/web/static/tests/helpers/test_utils.js254
-rw-r--r--addons/web/static/tests/helpers/test_utils_create.js288
-rw-r--r--addons/web/static/tests/helpers/test_utils_dom.js194
-rw-r--r--addons/web/static/tests/helpers/test_utils_fields.js119
-rw-r--r--addons/web/static/tests/helpers/test_utils_mock.js2
-rw-r--r--addons/web/static/tests/helpers/test_utils_modal.js48
10 files changed, 456 insertions, 478 deletions
diff --git a/addons/web/static/src/js/chrome/action_mixin.js b/addons/web/static/src/js/chrome/action_mixin.js
index 1ca87c15ef3..a704e5736a0 100644
--- a/addons/web/static/src/js/chrome/action_mixin.js
+++ b/addons/web/static/src/js/chrome/action_mixin.js
@@ -20,9 +20,9 @@ odoo.define('web.ActionMixin', function (require) {
* @module web.ActionMixin
*/
- var core = require('web.core');
+ const core = require('web.core');
- var ActionMixin = {
+ const ActionMixin = {
template: 'Action',
/**
@@ -79,7 +79,7 @@ odoo.define('web.ActionMixin', function (require) {
renderElement: function () {
this._super.apply(this, arguments);
if (this.contentTemplate) {
- var content = core.qweb.render(this.contentTemplate, { widget: this });
+ const content = core.qweb.render(this.contentTemplate, { widget: this });
this.$('.o_content').append(content);
}
},
@@ -223,7 +223,7 @@ odoo.define('web.ActionMixin', function (require) {
*/
_onGetControllerQueryParams: function (ev) {
ev.stopPropagation();
- var state = this.getOwnedQueryParams();
+ const state = this.getOwnedQueryParams();
ev.data.callback(state || {});
},
};
diff --git a/addons/web/static/src/js/core/custom_hooks.js b/addons/web/static/src/js/core/custom_hooks.js
index c196dd7f900..69750066a8c 100644
--- a/addons/web/static/src/js/core/custom_hooks.js
+++ b/addons/web/static/src/js/core/custom_hooks.js
@@ -14,18 +14,18 @@ odoo.define('web.custom_hooks', function () {
*/
function useFocusOnUpdate() {
const component = Component.current;
- component.__willFocus = null;
+ let willFocus = null;
function _focusSelector() {
- if (component.__willFocus) {
- const target = component.el.querySelector(component.__willFocus);
+ if (willFocus) {
+ const target = component.el.querySelector(willFocus);
if (target) {
target.focus();
if (['INPUT', 'TEXTAREA'].includes(target.tagName)) {
target.selectionStart = target.selectionEnd = target.value.length;
}
}
- component.__willFocus = null;
+ willFocus = null;
}
}
@@ -33,7 +33,7 @@ odoo.define('web.custom_hooks', function () {
onPatched(_focusSelector);
return function focusOnUpdate(selector = '[autofocus]') {
- component.__willFocus = selector;
+ willFocus = selector;
};
}
diff --git a/addons/web/static/src/js/main.js b/addons/web/static/src/js/main.js
index 35f00254ba8..49b90228ba2 100644
--- a/addons/web/static/src/js/main.js
+++ b/addons/web/static/src/js/main.js
@@ -1,4 +1,5 @@
odoo.define('web.web_client', function (require) {
+ "use strict";
const env = require('web.env');
const session = require("web.session");
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 };
});