From 26fe23201bb495176d14c15c8866e8fc72ca044c Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 28 May 2020 09:55:44 +0000 Subject: [PATCH 1/3] [IMP] web,*: properly define env in the frontend *bus,mail This commit extracts the common basis of the env to use both in the frontend and the backend. The public env now contains most of the features that were previously (only) in the webclient env. Moreover, the PublicRoot no longer uses the ServiceProviderMixin, such that services are only deployed once, in the (public) env. Owl components can now be defined in the website, and rely on a properly built env. Legacy widgets still works as they access services through the PublicRoot (via trigger_up) which redirects those requests to the env. --- .../bus/static/src/js/services/bus_service.js | 34 +++- .../static/src/js/services/mail_manager.js | 18 +- .../src/js/chrome/abstract_web_client.js | 34 ++-- addons/web/static/src/js/common_env.js | 119 +++++++++++++ .../static/src/js/core/abstract_service.js | 29 +++- .../web/static/src/js/core/service_mixins.js | 2 + addons/web/static/src/js/env.js | 157 +----------------- addons/web/static/src/js/public/public_env.js | 23 +-- .../web/static/src/js/public/public_root.js | 87 +++++----- .../src/js/public/public_root_instance.js | 7 + addons/web/views/webclient_templates.xml | 2 + 11 files changed, 274 insertions(+), 238 deletions(-) create mode 100644 addons/web/static/src/js/common_env.js diff --git a/addons/bus/static/src/js/services/bus_service.js b/addons/bus/static/src/js/services/bus_service.js index 7dc932d4575..39b61da51f1 100644 --- a/addons/bus/static/src/js/services/bus_service.js +++ b/addons/bus/static/src/js/services/bus_service.js @@ -4,6 +4,7 @@ odoo.define('bus.BusService', function (require) { var CrossTab = require('bus.CrossTab'); var core = require('web.core'); var ServicesMixin = require('web.ServicesMixin'); +const session = require('web.session'); var BusService = CrossTab.extend(ServicesMixin, { dependencies : ['local_storage'], @@ -11,6 +12,38 @@ var BusService = CrossTab.extend(ServicesMixin, { // properties _audio: null, + /** + * As the BusService doesn't extend AbstractService, we have to replicate + * here what is done in AbstractService + * + * @param {Object} env + */ + init: function (env) { + this.env = env; + this._super(); + }, + + /** + * Replicate the behavior of AbstractService: + * + * Directly calls the requested service, instead of triggering a + * 'call_service' event up, which wouldn't work as services have no parent. + * + * @param {OdooEvent} ev + */ + _trigger_up: function (ev) { + if (ev.name === 'call_service') { + const payload = ev.data; + let args = payload.args || []; + if (payload.service === 'ajax' && payload.method === 'rpc') { + // ajax service uses an extra 'target' argument for rpc + args = args.concat(ev.target); + } + const service = this.env.services[payload.service]; + const result = service[payload.method].apply(service, args); + payload.callback(result); + } + }, /** * This method is necessary in order for this Class to be used to instantiate services * @@ -77,7 +110,6 @@ var BusService = CrossTab.extend(ServicesMixin, { if (!this._audio) { this._audio = new Audio(); var ext = this._audio.canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3"; - var session = this.getSession(); this._audio.src = session.url("/mail/static/src/audio/ting" + ext); } Promise.resolve(this._audio.play()).catch(_.noop); diff --git a/addons/mail/static/src/js/services/mail_manager.js b/addons/mail/static/src/js/services/mail_manager.js index 04cc696d401..f52af585f94 100644 --- a/addons/mail/static/src/js/services/mail_manager.js +++ b/addons/mail/static/src/js/services/mail_manager.js @@ -1004,7 +1004,7 @@ var MailManager = AbstractService.extend({ _t("%d Messages"), this._outOfFocusUnreadMessageCounter ); - this.trigger_up('set_title_part', { + this.env.bus.trigger('set_title_part', { part: '_chat', title: tabTitle }); @@ -1048,12 +1048,14 @@ var MailManager = AbstractService.extend({ * @param {string} viewID */ _redirectToDocument: function (resModel, resID, viewID) { - this.do_action({ - type: 'ir.actions.act_window', - view_mode: 'form', - res_model: resModel, - views: [[viewID || false, 'form']], - res_id: resID, + this.env.bus.trigger('do-action', { + action: { + type: 'ir.actions.act_window', + view_mode: 'form', + res_model: resModel, + views: [[viewID || false, 'form']], + res_id: resID, + }, }); }, /** @@ -1345,7 +1347,7 @@ var MailManager = AbstractService.extend({ */ _onWindowFocus: function () { this._resetOutOfFocusUnreadMessageCounter(); - this.trigger_up('set_title_part', { part: '_chat' }); + this.env.bus.trigger('set_title_part', { part: '_chat' }); }, }); diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js index 0cabba1d23d..95d9868b89a 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -20,7 +20,6 @@ var data_manager = require('web.data_manager'); var dom = require('web.dom'); var KeyboardNavigationMixin = require('web.KeyboardNavigationMixin'); var Loading = require('web.Loading'); -var local_storage = require('web.local_storage'); var RainbowMan = require('web.RainbowMan'); var session = require('web.session'); var utils = require('web.utils'); @@ -103,7 +102,7 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { this.action_mutex = new concurrency.Mutex(); this.set('title_part', {"zopenerp": "Odoo"}); this.env = env; - core.bus.on('legacy_webclient_request', this, this._onLegacyWebclientRequest); + this.env.bus.on('set_title_part', this, this._onSetTitlePart); }, /** * @override @@ -206,6 +205,11 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { set_action_manager: function () { var self = this; this.action_manager = new ActionManager(this, session.user_context); + this.env.bus.on('do-action', this, payload => { + this.action_manager.doAction(payload.action, payload.options) + .then(payload.on_success || (() => {})) + .guardedCatch(payload.on_fail || (() => {})); + }); var fragment = document.createDocumentFragment(); return this.action_manager.appendTo(fragment).then(function () { dom.append(self.$el, fragment, { @@ -439,20 +443,6 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { _onGetScrollPosition: function (ev) { ev.data.callback(this.getScrollPosition()); }, - /** - * Services used to trigger_up some events (e.g. do_action, get_session, - * set_title_part) when the webclient was still instantiating them. Now that - * services have been moved to the env, the webclient is no longer their - * parent and they can thus no longer communicate with the webclient by - * trigger_up. Instead, they trigger those events on the bus, and we end up - * in this handler. Eventually, services should stop triggering events up. - * - * @private - * @param {CustomEvent} ev - */ - _onLegacyWebclientRequest: function (ev) { - this._trigger_up(ev); - }, /** * Loads an action from the database given its ID. * @@ -502,13 +492,13 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { }, /** * @private - * @param {OdooEvent} ev - * @param {string} ev.data.part - * @param {string} [ev.data.title] + * @param {Object} payload + * @param {string} payload.part + * @param {string} [payload.title] */ - _onSetTitlePart: function (ev) { - var part = ev.data.part; - var title = ev.data.title; + _onSetTitlePart: function (payload) { + var part = payload.part; + var title = payload.title; this.set_title_part(part, title); }, /** diff --git a/addons/web/static/src/js/common_env.js b/addons/web/static/src/js/common_env.js new file mode 100644 index 00000000000..0e31b8e3ceb --- /dev/null +++ b/addons/web/static/src/js/common_env.js @@ -0,0 +1,119 @@ +odoo.define("web.commonEnv", function (require) { + "use strict"; + + /** + * This file defines the common environment, which contains everything that + * is needed in the env for both the backend and the frontend (Odoo + * terminology). This module shouldn't be used as is. It should only be + * imported by the module defining the final env to use (in the frontend or + * in the backend). For instance, module 'web.env' imports it, adds stuff to + * it, and exports the final env that is used by the whole webclient + * application. + * + * There should be as much dependencies as possible in the env object. This + * will allow an easier testing of components. See [1] for more information + * on environments. + * + * [1] https://github.com/odoo/owl/blob/master/doc/reference/environment.md#content-of-an-environment + */ + + const { jsonRpc } = require("web.ajax"); + const { device, isDebug } = require("web.config"); + const { bus, serviceRegistry } = require("web.core"); + const rpc = require("web.rpc"); + const session = require("web.session"); + const { _t } = require("web.translation"); + const utils = require("web.utils"); + + + // Build the basic env + const env = { + _t, + bus, + device, + isDebug, + qweb: new owl.QWeb({ translateFn: _t }), + services: { + ajaxJsonRPC() { + return jsonRpc(...arguments); + }, + getCookie() { + return utils.get_cookie(...arguments); + }, + httpRequest(route, params = {}, readMethod = 'json') { + const info = { + method: params.method || 'POST', + }; + if (params.method !== 'GET') { + const formData = new FormData(); + for (const key in params) { + if (key === 'method') { + continue; + } + const value = params[key]; + if (Array.isArray(value) && value.length) { + for (const val of value) { + formData.append(key, val); + } + } else { + formData.append(key, value); + } + } + info.body = formData; + } + return fetch(route, info).then(response => response[readMethod]()); + }, + navigate(url, params) { + window.location = $.param.querystring(url, params); + }, + reloadPage() { + window.location.reload(); + }, + rpc(params, options) { + const query = rpc.buildQuery(params); + return session.rpc(query.route, query.params, options); + }, + setCookie() { + utils.set_cookie(...arguments); + }, + }, + session, + }; + + + // Deploy services in the env (specializations of AbstractService registered + // into the serviceRegistry) + const UndeployedServices = Object.assign({}, serviceRegistry.map); + function _deployServices() { + let done = false; + while (!done) { + // find a service with no missing dependency + const serviceName = Object.keys(UndeployedServices).find(serviceName => { + const Service = UndeployedServices[serviceName]; + return Service.prototype.dependencies.every(depName => { + return env.services[depName]; + }); + }); + if (serviceName) { + const Service = UndeployedServices[serviceName]; + const service = new Service(env); + env.services[serviceName] = service; + delete UndeployedServices[serviceName]; + service.start(); + } else { + done = true; + } + } + } + serviceRegistry.onAdd((serviceName, Service) => { + if (serviceName in env.services || serviceName in UndeployedServices) { + throw new Error(`Service ${serviceName} is already loaded.`); + } + UndeployedServices[serviceName] = Service; + _deployServices(); + }); + _deployServices(); + + + return env; +}); diff --git a/addons/web/static/src/js/core/abstract_service.js b/addons/web/static/src/js/core/abstract_service.js index c907b28f7c8..f9ad859fef2 100644 --- a/addons/web/static/src/js/core/abstract_service.js +++ b/addons/web/static/src/js/core/abstract_service.js @@ -7,14 +7,39 @@ var ServicesMixin = require('web.ServicesMixin'); var AbstractService = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { dependencies: [], - init: function (parent) { + init: function (env) { Mixins.EventDispatcherMixin.init.call(this, arguments); - this.setParent(parent); + this.env = env; }, /** * @abstract */ start: function () {}, + /** + * Directly calls the requested service, instead of triggering a + * 'call_service' event up, which wouldn't work as services have no parent + * + * @param {OdooEvent} ev + */ + _trigger_up: function (ev) { + Mixins.EventDispatcherMixin._trigger_up.apply(this, arguments); + if (ev.is_stopped()) { + return; + } + const payload = ev.data; + if (ev.name === 'call_service') { + let args = payload.args || []; + if (payload.service === 'ajax' && payload.method === 'rpc') { + // ajax service uses an extra 'target' argument for rpc + args = args.concat(ev.target); + } + const service = this.env.services[payload.service]; + const result = service[payload.method].apply(service, args); + payload.callback(result); + } else if (ev.name === 'do_action') { + this.env.bus.trigger('do-action', payload); + } + }, }); return AbstractService; diff --git a/addons/web/static/src/js/core/service_mixins.js b/addons/web/static/src/js/core/service_mixins.js index f191dba5c08..198d00993ed 100644 --- a/addons/web/static/src/js/core/service_mixins.js +++ b/addons/web/static/src/js/core/service_mixins.js @@ -3,6 +3,8 @@ odoo.define('web.ServiceProviderMixin', function (require) { var core = require('web.core'); +// ServiceProviderMixin is deprecated. It is only used by the ProjectTimesheet +// app. As soon as it no longer uses it, we can remove it. var ServiceProviderMixin = { services: {}, // dict containing deployed service instances UndeployedServices: {}, // dict containing classes of undeployed services diff --git a/addons/web/static/src/js/env.js b/addons/web/static/src/js/env.js index c95cbd3045a..7fd7e1defab 100644 --- a/addons/web/static/src/js/env.js +++ b/addons/web/static/src/js/env.js @@ -1,157 +1,16 @@ odoo.define("web.env", function (require) { "use strict"; - const { jsonRpc } = require('web.ajax'); - const { device, isDebug } = require("web.config"); - const { bus, serviceRegistry } = require("web.core"); + /** + * This file defines the env to use in the webclient. + */ + + const commonEnv = require('web.commonEnv'); const dataManager = require('web.data_manager'); const { blockUI, unblockUI } = require("web.framework"); - const rpc = require("web.rpc"); - const session = require("web.session"); - const { _t } = require('web.translation'); - const utils = require("web.utils"); - const qweb = new owl.QWeb({ translateFn: _t }); + const env = Object.assign(commonEnv, { dataManager }); + env.services = Object.assign(env.services, { blockUI, unblockUI }); - function ajaxJsonRPC() { - return jsonRpc(...arguments); - } - - function getCookie() { - return utils.get_cookie(...arguments); - } - - function httpRequest(route, params = {}, readMethod = 'json') { - const info = { - method: params.method || 'POST', - }; - if (params.method !== 'GET') { - const formData = new FormData(); - for (const key in params) { - if (key === 'method') { - continue; - } - const value = params[key]; - if (Array.isArray(value) && value.length) { - for (const val of value) { - formData.append(key, val); - } - } else { - formData.append(key, value); - } - } - info.body = formData; - } - - return fetch(route, info).then(response => response[readMethod]()); - } - - function navigate(url, params) { - window.location = $.param.querystring(url, params); - } - - function performRPC(params, options) { - const query = rpc.buildQuery(params); - return session.rpc(query.route, query.params, options); - } - - function reloadPage() { - window.location.reload(); - } - - function setCookie() { - utils.set_cookie(...arguments); - } - - // ServiceProvider - const services = {}; // dict containing deployed service instances - const UndeployedServices = {}; // dict containing classes of undeployed services - function _deployServices() { - let done = false; - while (!done) { - const serviceName = _.findKey(UndeployedServices, Service => { - // no missing dependency - return !_.some(Service.prototype.dependencies, depName => { - return !services[depName]; - }); - }); - if (serviceName) { - const Service = UndeployedServices[serviceName]; - // we created a patched version of the Service in which the 'trigger_up' - // function directly calls the requested service, instead of triggering - // a 'call_service' event up, which wouldn't work as services have - // no parent - const PatchedService = Service.extend({ - _trigger_up: function (ev) { - this._super(...arguments); - if (!ev.is_stopped() && ev.name === 'call_service') { - const payload = ev.data; - let args = payload.args || []; - if (payload.service === 'ajax' && payload.method === 'rpc') { - // ajax service uses an extra 'target' argument for rpc - args = args.concat(ev.target); - } - const service = services[payload.service]; - const result = service[payload.method].apply(service, args); - payload.callback(result); - } else { - // historically, some services could reach the webclient - // by triggering events up, as the webclient was their - // parent. Since services have been moved to the env, - // this is no longer the case, so we re-trigger those - // events on the bus for now. Eventually, services - // should stop triggering events up (they can still - // communicate with other services through the env). - bus.trigger('legacy_webclient_request', ev); - } - }, - }); - const service = new PatchedService(); - services[serviceName] = service; - delete UndeployedServices[serviceName]; - service.start(); - } else { - done = true; - } - } - } - _.each(serviceRegistry.map, (Service, serviceName) => { - if (serviceName in UndeployedServices) { - throw new Error(`Service ${serviceName} is already loaded.`); - } - UndeployedServices[serviceName] = Service; - }); - serviceRegistry.onAdd((serviceName, Service) => { - if (serviceName in services || serviceName in UndeployedServices) { - throw new Error(`Service ${serviceName} is already loaded.`); - } - UndeployedServices[serviceName] = Service; - _deployServices(); - }); - _deployServices(); - - // There should be as much dependencies as possible in the env object. - // This will allow an easier testing of components. - // See https://github.com/odoo/owl/blob/master/doc/reference/environment.md#content-of-an-environment - // for more information on environments. - return { - _t, - bus, - dataManager, - device, - isDebug, - qweb, - services: Object.assign(services, { - ajaxJsonRPC, - blockUI, - getCookie, - httpRequest, - navigate, - reloadPage, - rpc: performRPC, - setCookie, - unblockUI, - }), - session, - }; + return env; }); diff --git a/addons/web/static/src/js/public/public_env.js b/addons/web/static/src/js/public/public_env.js index 373c7b512b8..581cccd430b 100644 --- a/addons/web/static/src/js/public/public_env.js +++ b/addons/web/static/src/js/public/public_env.js @@ -1,24 +1,11 @@ odoo.define("web.public_env", function (require) { "use strict"; - const { _t } = require("web.core"); - const rpc = require("web.rpc"); + /** + * This file defines the env to use in the public side. + */ - const qweb = new owl.QWeb({ translateFn: _t }); + const commonEnv = require("web.commonEnv"); - function performRPC(params, options) { - const query = rpc.buildQuery(params); - return session.rpc(query.route, query.params, options); - } - - // There should be as much dependencies as possible in the env object. - // This will allow an easier testing of components. - // See https://github.com/odoo/owl/blob/master/doc/reference/environment.md#content-of-an-environment - // for more information on environments. - return { - qweb, - services: { - rpc: performRPC, - }, - }; + return commonEnv; }); diff --git a/addons/web/static/src/js/public/public_root.js b/addons/web/static/src/js/public/public_root.js index 33d442e1d76..f684df2b629 100644 --- a/addons/web/static/src/js/public/public_root.js +++ b/addons/web/static/src/js/public/public_root.js @@ -3,7 +3,7 @@ odoo.define('web.public.root', function (require) { var ajax = require('web.ajax'); var dom = require('web.dom'); -var ServiceProviderMixin = require('web.ServiceProviderMixin'); +const env = require('web.public_env'); var session = require('web.session'); var utils = require('web.utils'); var publicWidget = require('web.public.widget'); @@ -25,16 +25,17 @@ var localeDef = ajax.loadJS('/web/webclient/locale/' + lang.replace('-', '_')); * this Class instance. Its main role will be to retrieve RPC demands from its * children and handle them. */ -var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, { +var PublicRoot = publicWidget.RootWidget.extend({ events: _.extend({}, publicWidget.RootWidget.prototype.events || {}, { 'submit .js_website_submit_form': '_onWebsiteFormSubmit', 'click .js_disable_on_click': '_onDisableOnClick', }), custom_events: _.extend({}, publicWidget.RootWidget.prototype.custom_events || {}, { - 'context_get': '_onContextGet', - 'main_object_request': '_onMainObjectRequest', - 'widgets_start_request': '_onWidgetsStartRequest', - 'widgets_stop_request': '_onWidgetsStopRequest', + call_service: '_onCallService', + context_get: '_onContextGet', + main_object_request: '_onMainObjectRequest', + widgets_start_request: '_onWidgetsStartRequest', + widgets_stop_request: '_onWidgetsStopRequest', }), /** @@ -42,7 +43,7 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, { */ init: function () { this._super.apply(this, arguments); - ServiceProviderMixin.init.call(this); + this.env = env; this.publicWidgets = []; }, /** @@ -90,37 +91,6 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, { // Private //-------------------------------------------------------------------------- - /** - * Automatically adds the global context to RPCs. - * - * @override - */ - _call_service: function (ev) { - if (ev.data.service === 'ajax' && ev.data.method === 'rpc') { - var route = ev.data.args[0]; - if (_.str.startsWith(route, '/web/dataset/call_kw/')) { - var params = ev.data.args[1]; - var options = ev.data.args[2]; - var noContextKeys = undefined; - if (options) { - noContextKeys = options.noContextKeys; - ev.data.args[2] = _.omit(options, 'noContextKeys'); - } - params.kwargs.context = _computeContext.call(this, params.kwargs.context, noContextKeys); - } - } else if (ev.data.service === 'ajax' && ev.data.method === 'loadLibs') { - ev.data.args[1] = _computeContext.call(this, ev.data.args[1]); - } - return ServiceProviderMixin._call_service.apply(this, arguments); - - function _computeContext(context, noContextKeys) { - context = _.extend({}, this._getContext(), context); - if (noContextKeys) { - context = _.omit(context, noContextKeys); - } - return JSON.parse(JSON.stringify(context)); - } - }, /** * Retrieves the global context of the public environment. This is the * context which is automatically added to each RPC. @@ -234,6 +204,47 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, { // Handlers //-------------------------------------------------------------------------- + /** + * Calls the requested service from the env. Automatically adds the global + * context to RPCs. + * + * @private + * @param {OdooEvent} event + */ + _onCallService: function (ev) { + function _computeContext(context, noContextKeys) { + context = _.extend({}, this._getContext(), context); + if (noContextKeys) { + context = _.omit(context, noContextKeys); + } + return JSON.parse(JSON.stringify(context)); + } + + const payload = ev.data; + let args = payload.args || []; + if (payload.service === 'ajax' && payload.method === 'rpc') { + // ajax service uses an extra 'target' argument for rpc + args = args.concat(ev.target); + + var route = args[0]; + if (_.str.startsWith(route, '/web/dataset/call_kw/')) { + var params = args[1]; + var options = args[2]; + var noContextKeys; + if (options) { + noContextKeys = options.noContextKeys; + args[2] = _.omit(options, 'noContextKeys'); + } + params.kwargs.context = _computeContext.call(this, params.kwargs.context, noContextKeys); + } + } else if (payload.service === 'ajax' && payload.method === 'loadLibs') { + args[1] = _computeContext.call(this, args[1]); + } + + const service = this.env.services[payload.service]; + const result = service[payload.method].apply(service, args); + payload.callback(result); + }, /** * Called when someone asked for the global public context. * diff --git a/addons/web/static/src/js/public/public_root_instance.js b/addons/web/static/src/js/public/public_root_instance.js index 988d939bbe1..e368d421fbe 100644 --- a/addons/web/static/src/js/public/public_root_instance.js +++ b/addons/web/static/src/js/public/public_root_instance.js @@ -1,9 +1,16 @@ odoo.define('root.widget', function (require) { 'use strict'; +const env = require('web.public_env'); var lazyloader = require('web.public.lazyloader'); var rootData = require('web.public.root'); +/** + * Configure Owl with the public env + */ +owl.config.mode = env.isDebug() ? "dev" : "prod"; +owl.Component.env = env; + /** * This widget is important, because the tour manager needs a root widget in * order to work. The root widget must be a service provider with the ajax diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index bebc3255dc2..3cb081e7e37 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -167,6 +167,8 @@ + + From dc260c324170a90fa737cd21dd528e39d658d9b6 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 5 Jun 2020 07:20:11 +0000 Subject: [PATCH 2/3] [IMP] web: tests: rework mock helpers This commit reworks the test helpers, following recent changes on services deployed in the Owl env. --- addons/web/static/tests/helpers/test_env.js | 34 +- addons/web/static/tests/helpers/test_utils.js | 2 +- .../static/tests/helpers/test_utils_create.js | 23 +- .../static/tests/helpers/test_utils_mock.js | 710 ++++++++++-------- .../static/tests/owl_compatibility_tests.js | 43 +- .../static/tests/views/view_dialogs_tests.js | 3 +- 6 files changed, 456 insertions(+), 359 deletions(-) diff --git a/addons/web/static/tests/helpers/test_env.js b/addons/web/static/tests/helpers/test_env.js index 5b197451517..5a3256e98fd 100644 --- a/addons/web/static/tests/helpers/test_env.js +++ b/addons/web/static/tests/helpers/test_env.js @@ -1,10 +1,8 @@ odoo.define('web.test_env', async function (require) { "use strict"; - const AbstractStorageService = require('web.AbstractStorageService'); - const Bus = require("web.Bus"); - const RamStorage = require('web.RamStorage'); - const { buildQuery } = require("web.rpc"); + const Bus = require('web.Bus'); + const { buildQuery } = require('web.rpc'); const session = require('web.session'); /** @@ -17,9 +15,6 @@ odoo.define('web.test_env', async function (require) { * @returns {Proxy} */ function makeTestEnvironment(env = {}, providedRPC = null) { - const RamStorageService = AbstractStorageService.extend({ - storage: new RamStorage(), - }); const database = { parameters: { code: "en_US", @@ -31,33 +26,26 @@ odoo.define('web.test_env', async function (require) { time_format: '%H:%M:%S', }, }; - let testEnv = {}; const defaultEnv = { _t: env._t || Object.assign((s => s), { database }), - bus: new Bus(), + bus: env.bus || new Bus(), device: Object.assign({ isMobile: false }, env.device), isDebug: env.isDebug || (() => false), qweb: new owl.QWeb({ templates: session.owlTemplates }), services: Object.assign({ - ajax: { // for legacy subwidgets + ajax: { rpc() { - const prom = testEnv.session.rpc(...arguments); - prom.abort = function () { - throw new Error("Can't abort this request"); - }; - return prom; - }, + return env.session.rpc(...arguments); // Compatibility Legacy Widgets + } }, - getCookie() { }, - httpRequest() { + getCookie() {}, + httpRequest(/* route, params = {}, readMethod = 'json' */) { return Promise.resolve(''); }, rpc(params, options) { const query = buildQuery(params); - return testEnv.session.rpc(query.route, query.params, options); + return env.session.rpc(query.route, query.params, options); }, - local_storage: new RamStorageService(), - session_storage: new RamStorageService(), notification: { notify() { } }, }, env.services), session: Object.assign({ @@ -67,10 +55,10 @@ odoo.define('web.test_env', async function (require) { } throw new Error(`No method to perform RPC`); }, + url: session.url, }, env.session), }; - testEnv = Object.assign(env, defaultEnv); - return testEnv; + return Object.assign(env, defaultEnv); } /** diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js index f6114b84fcd..1a1f38f7f09 100644 --- a/addons/web/static/tests/helpers/test_utils.js +++ b/addons/web/static/tests/helpers/test_utils.js @@ -121,7 +121,7 @@ odoo.define('web.test_utils', async function (require) { return { mock: { addMockEnvironment: testUtilsMock.addMockEnvironment, - getMockedOwlEnv: testUtilsMock.getMockedOwlEnv, + addMockEnvironmentOwl: testUtilsMock.addMockEnvironmentOwl, intercept: testUtilsMock.intercept, patch: testUtilsMock.patch, patchDate: testUtilsMock.patchDate, diff --git a/addons/web/static/tests/helpers/test_utils_create.js b/addons/web/static/tests/helpers/test_utils_create.js index 494a0aa2436..801120bf64b 100644 --- a/addons/web/static/tests/helpers/test_utils_create.js +++ b/addons/web/static/tests/helpers/test_utils_create.js @@ -67,7 +67,6 @@ odoo.define('web.test_utils_create', function (require) { } params.server = mockServer; - Component.env = testUtilsMock.getMockedOwlEnv(params); const userContext = params.context && params.context.user_context || {}; const actionManager = new ActionManager(widget, userContext); @@ -142,7 +141,7 @@ odoo.define('web.test_utils_create', function (require) { if (!(constructor.prototype instanceof Component)) { throw new Error(`Argument "constructor" must be an Owl Component.`); } - const env = Object.assign(testUtilsMock.getMockedOwlEnv(params), params.env); + const cleanUp = await testUtilsMock.addMockEnvironmentOwl(Component, params); class Parent extends Component { constructor() { super(...arguments); @@ -154,7 +153,6 @@ odoo.define('web.test_utils_create', function (require) { } } } - Parent.env = env; Parent.template = xml``; const parent = new Parent(); await parent.mount(prepareTarget(params.debug), { position: 'first-child' }); @@ -162,6 +160,7 @@ odoo.define('web.test_utils_create', function (require) { const originalDestroy = child.destroy; child.destroy = function () { child.destroy = originalDestroy; + cleanUp(); parent.destroy(); }; return child; @@ -241,9 +240,9 @@ 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} + * @returns {Promise} */ - function createDebugManager(params = {}) { + async function createDebugManager(params = {}) { const mockRPC = params.mockRPC; Object.assign(params, { async mockRPC(route, args) { @@ -268,7 +267,7 @@ odoo.define('web.test_utils_create', function (require) { }, }); const debugManager = new DebugManager(); - testUtilsMock.addMockEnvironment(debugManager, params); + await testUtilsMock.addMockEnvironment(debugManager, params); return debugManager; } @@ -280,12 +279,12 @@ odoo.define('web.test_utils_create', function (require) { * @param {Class} params.Model the model class to use * @returns {Model} */ - function createModel(params) { + async function createModel(params) { const widget = new Widget(); const model = new params.Model(widget); - testUtilsMock.addMockEnvironment(widget, params); + await testUtilsMock.addMockEnvironment(widget, params); // override the model's 'destroy' so that it calls 'destroy' on the widget // instead, as the widget is the parent of the model and the mockServer. @@ -304,11 +303,11 @@ odoo.define('web.test_utils_create', function (require) { * * @param {Object} params This object will be given to addMockEnvironment, so * any parameters from that method applies - * @returns {Widget} + * @returns {Promise} */ - function createParent(params) { + async function createParent(params) { const widget = new Widget(); - testUtilsMock.addMockEnvironment(widget, params); + await testUtilsMock.addMockEnvironment(widget, params); return widget; } @@ -358,8 +357,6 @@ odoo.define('web.test_utils_create', function (require) { const viewInfo = testUtilsMock.fieldsViewGet(mockServer, params); params.server = mockServer; - const env = Object.assign(testUtilsMock.getMockedOwlEnv(params)); - Component.env = env; // create the view const View = params.View; diff --git a/addons/web/static/tests/helpers/test_utils_mock.js b/addons/web/static/tests/helpers/test_utils_mock.js index 414a9c0df64..6df49cf59f3 100644 --- a/addons/web/static/tests/helpers/test_utils_mock.js +++ b/addons/web/static/tests/helpers/test_utils_mock.js @@ -10,12 +10,16 @@ odoo.define('web.test_utils_mock', function (require) { * testUtils file. */ +const AbstractStorageService = require('web.AbstractStorageService'); +const AjaxService = require('web.AjaxService'); const basic_fields = require('web.basic_fields'); +const Bus = require('web.Bus'); const config = require('web.config'); const core = require('web.core'); const dom = require('web.dom'); const makeTestEnvironment = require('web.test_env'); const MockServer = require('web.MockServer'); +const RamStorage = require('web.RamStorage'); const session = require('web.session'); const DebouncedField = basic_fields.DebouncedField; @@ -25,6 +29,199 @@ const DebouncedField = basic_fields.DebouncedField; // Private functions //------------------------------------------------------------------------------ +/** + * Returns a mocked environment to be used by OWL components in tests, with + * requested services (+ ajax, local_storage and session_storage) deployed. + * + * @private + * @param {Object} params + * @param {Bus} [params.bus] + * @param {boolean} [params.debug] + * @param {Object} [params.env] + * @param {Bus} [params.env.bus] + * @param {Object} [params.env.dataManager] + * @param {Object} [params.env.services] + * @param {Object[]} [params.favoriteFilters] + * @param {Object} [params.services] + * @param {Object} [params.session] + * @param {MockServer} [mockServer] + * @returns {Promise} env + */ +async function _getMockedOwlEnv(params, mockServer) { + params.env = params.env || {}; + + // build the env + const favoriteFilters = params.favoriteFilters; + const debug = params.debug; + const services = {}; + const env = Object.assign({}, params.env, { + bus: params.bus || params.env.bus || new Bus(), + dataManager: Object.assign({ + load_action: (actionID, context) => { + return mockServer.performRpc('/web/action/load', { + action_id: actionID, + additional_context: context, + }); + }, + load_views: (params, options) => { + return mockServer.performRpc('/web/dataset/call_kw/' + params.model, { + args: [], + kwargs: { + context: params.context, + options: options, + views: params.views_descr, + }, + method: 'load_views', + model: params.model, + }).then(function (views) { + views = _.mapObject(views, viewParams => { + return fieldsViewGet(mockServer, viewParams); + }); + if (favoriteFilters && 'search' in views) { + views.search.favoriteFilters = favoriteFilters; + } + return views; + }); + }, + load_filters: params => { + if (debug) { + console.log('[mock] load_filters', params); + } + return Promise.resolve([]); + }, + }, params.env.dataManager), + services: Object.assign(services, params.env.services), + session: params.session || {}, + }); + + // deploy services into the env + // determine services to instantiate (classes), and already register function services + const servicesToDeploy = {}; + for (const name in params.services || {}) { + const Service = params.services[name]; + if (Service.constructor.name === 'Class') { + servicesToDeploy[name] = Service; + } else { + services[name] = Service; + } + } + // always deploy ajax, local storage and session storage + if (!servicesToDeploy.ajax) { + const MockedAjaxService = AjaxService.extend({ + rpc: mockServer.performRpc.bind(mockServer), + }); + services.ajax = new MockedAjaxService(env); + } + const RamStorageService = AbstractStorageService.extend({ + storage: new RamStorage(), + }); + if (!servicesToDeploy.local_storage) { + services.local_storage = new RamStorageService(env); + } + if (!servicesToDeploy.session_storage) { + services.session_storage = new RamStorageService(env); + } + // deploy other requested services + let done = false; + while (!done) { + const serviceName = Object.keys(servicesToDeploy).find(serviceName => { + const Service = servicesToDeploy[serviceName]; + return Service.prototype.dependencies.every(depName => { + return env.services[depName]; + }); + }); + if (serviceName) { + const Service = servicesToDeploy[serviceName]; + services[serviceName] = new Service(env); + delete servicesToDeploy[serviceName]; + services[serviceName].start(); + } else { + const serviceNames = _.keys(servicesToDeploy); + if (serviceNames.length) { + console.warn("Non loaded services:", serviceNames); + } + done = true; + } + } + // wait for asynchronous services to properly start + await new Promise(setTimeout); + + return env; +} +/** + * This function is used to mock global objects (session, config...) in tests. + * It is necessary for legacy widgets. It returns a cleanUp function to call at + * the end of the test. + * + * The function could be removed as soon as we do not support legacy widgets + * anymore. + * + * @private + * @param {Object} params + * @param {Object} [params.config] if given, it is used to extend the global + * config, + * @param {Object} [params.session] if given, it is used to extend the current, + * real session. + * @param {Object} [params.translateParameters] if given, it will be used to + * extend the core._t.database.parameters object. + * @returns {function} a cleanUp function to restore everything, to call at the + * end of the test + */ +function _mockGlobalObjects(params) { + // patch session + let initialSession = Object.assign({}, session); + session.getTZOffset = function () { + return 0; // by default, but may be overridden in specific tests + }; + if ('session' in params) { + Object.assign(session, params.session); + } + + // patch config + let initialConfig; + if ('config' in params) { + initialConfig = Object.assign({}, config); + initialConfig.device = Object.assign({}, config.device); + if ('device' in params.config) { + Object.assign(config.device, params.config.device); + } + if ('debug' in params.config) { + odoo.debug = params.config.debug; + } + } + + // patch translate params + let initialParameters; + if ('translateParameters' in params) { + initialParameters = Object.assign({}, core._t.database.parameters); + Object.assign(core._t.database.parameters, params.translateParameters); + } + + // build the cleanUp function to restore everything at the end of the test + function cleanUp() { + let key; + if ('session' in params) { + for (key in session) { + delete session[key]; + } + } + Object.assign(session, initialSession); + if ('config' in params) { + for (key in config) { + delete config[key]; + } + _.extend(config, initialConfig); + } + if ('translateParameters' in params) { + for (key in core._t.database.parameters) { + delete core._t.database.parameters[key]; + } + _.extend(core._t.database.parameters, initialParameters); + } + } + + return cleanUp; +} /** * logs all event going through the target widget. * @@ -42,16 +239,58 @@ function _observe(widget) { // Public functions //------------------------------------------------------------------------------ +/** + * performs a fields_view_get, and mocks the postprocessing done by the + * data_manager to return an equivalent structure. + * + * @param {MockServer} server + * @param {Object} params + * @param {string} params.model + * @returns {Object} an object with 3 keys: arch, fields and viewFields + */ +function fieldsViewGet(server, params) { + var fieldsView = server.fieldsViewGet(params); + // mock the structure produced by the DataManager + fieldsView.viewFields = fieldsView.fields; + fieldsView.fields = server.fieldsGet(params.model); + return fieldsView; +} + +/** + * intercepts an event bubbling up the widget hierarchy. The event intercepted + * must be a "custom event", i.e. an event generated by the method 'trigger_up'. + * + * Note that this method really intercepts the event if @propagate is not set. + * It will not be propagated further, and even the handlers on the target will + * not fire. + * + * @param {Widget} widget the target widget (any Odoo widget) + * @param {string} eventName description of the event + * @param {function} fn callback executed when the even is intercepted + * @param {boolean} [propagate=false] + */ +function intercept(widget, eventName, fn, propagate) { + var _trigger_up = widget._trigger_up.bind(widget); + widget._trigger_up = function (event) { + if (event.name === eventName) { + fn(event); + if (!propagate) { return; } + } + _trigger_up(event); + }; +} + /** * Removes the src attribute on images and iframes to prevent not found errors, * and optionally triggers an rpc with the src url as route on a widget. * This method is critical and must be fastest (=> no jQuery, no underscore) * * @param {HTMLElement} el - * @param {[Widget]} widget the widget on which the rpc should be performed + * @param {[function]} rpc */ -function removeSrcAttribute(el, widget) { +function removeSrcAttribute(el, rpc) { var nodes; + el = el.nodeType === 8 ? el.nextSibling : el; if (el.nodeName === 'IMG' || el.nodeName === 'IFRAME') { nodes = [el]; } else { @@ -68,14 +307,135 @@ function removeSrcAttribute(el, widget) { } else { node.setAttribute('src', 'about:blank'); } - if (widget) { - widget._rpc({ route: src }); + if (rpc) { + rpc(src, []); } $(node).trigger('load'); } } } +/** + * Add a mock environment to test Owl Components. This function generates a test + * env and sets it on the given Component. It also has several side effects, + * like patching the global session or config objects. It returns a cleanup + * function to call at the end of the test. + * + * @param {Component} Component + * @param {Object} [params] + * @param {Object} [params.actions] + * @param {Object} [params.archs] + * @param {string} [params.currentDate] + * @param {Object} [params.data] + * @param {boolean} [params.debug] + * @param {function} [params.mockRPC] + * @param {number} [params.fieldDebounce=0] the value of the DEBOUNCE attribute + * of fields + * @param {boolean} [params.debounce=true] if false, patch _.debounce to remove + * its behavior + * @param {boolean} [params.throttle=false] by default, _.throttle is patched to + * remove its behavior, except if this params is set to true + * @param {boolean} [params.mockSRC=false] if true, redirect src GET requests to + * the mockServer + * @param {MockServer} [mockServer] + * @returns {Promise} the cleanup function + */ +async function addMockEnvironmentOwl(Component, params, mockServer) { + params = params || {}; + + // instantiate a mockServer if not provided + if (!mockServer) { + let Server = MockServer; + if (params.mockRPC) { + Server = MockServer.extend({ _performRpc: params.mockRPC }); + } + mockServer = new Server(params.data, { + actions: params.actions, + archs: params.archs, + currentDate: params.currentDate, + debug: params.debug, + }); + } + + // make sure the debounce value for input fields is set to 0 + const initialDebounceValue = DebouncedField.prototype.DEBOUNCE; + DebouncedField.prototype.DEBOUNCE = params.fieldDebounce || 0; + const initialDOMDebounceValue = dom.DEBOUNCE; + dom.DEBOUNCE = 0; + + // patch underscore debounce/throttle functions + const initialDebounce = _.debounce; + if (params.debounce === false) { + _.debounce = function (func) { + return func; + }; + } + // fixme: throttle is inactive by default, should we make it explicit ? + const initialThrottle = _.throttle; + if (!('throttle' in params) || !params.throttle) { + _.throttle = function (func) { + return func; + }; + } + + // make sure images do not trigger a GET on the server + $('body').on('DOMNodeInserted.removeSRC', function (ev) { + let rpc; + if (params.mockSRC) { + rpc = mockServer.performRpc.bind(mockServer); + } + removeSrcAttribute(ev.target, rpc); + }); + + // mock global objects for legacy widgets (session, config...) + const restoreMockedGlobalObjects = _mockGlobalObjects(params); + + // set the test env on owl Component + const env = await _getMockedOwlEnv(params, mockServer); + const originalEnv = Component.env; + Component.env = makeTestEnvironment(env, mockServer.performRpc.bind(mockServer)); + + // while we have a mix between Owl and legacy stuff, some of them triggering + // events on the env.bus (a new Bus instance especially created for the current + // test), the others using core.bus, we have to ensure that events triggered + // on env.bus are also triggered on core.bus (note that outside the testing + // environment, both are the exact same instance of Bus) + const envBusTrigger = env.bus.trigger; + env.bus.trigger = function () { + core.bus.trigger(...arguments); + envBusTrigger.call(env.bus, ...arguments); + }; + + // build the clean up function to call at the end of the test + function cleanUp() { + env.bus.destroy(); + Object.keys(env.services).forEach(function (s) { + var service = env.services[s]; + if (service.destroy && !service.isDestroyed()) { + service.destroy(); + } + }); + + DebouncedField.prototype.DEBOUNCE = initialDebounceValue; + dom.DEBOUNCE = initialDOMDebounceValue; + _.debounce = initialDebounce; + _.throttle = initialThrottle; + + // clear the caches (e.g. data_manager, ModelFieldSelector) at the end + // of each test to avoid collisions + core.bus.trigger('clear_cache'); + + $('body').off('DOMNodeInserted.removeSRC'); + $('.blockUI').remove(); // fixme: move to qunit_config in OdooAfterTestHook? + + restoreMockedGlobalObjects(); + + Component.env = originalEnv; + } + + return cleanUp; +} + /** * Add a mock environment to a widget. This helper function can simulate * various kind of side effects, such as mocking RPCs, changing the session, @@ -100,6 +460,7 @@ function removeSrcAttribute(el, widget) { * used to generate mock answers for every kind of routes supported by odoo * @param {number} [params.debug] if set to true, logs RPCs and uncaught Odoo * events. + * @param {Object} [params.bus] the instance of Bus that will be used (in the env) * @param {function} [params.mockRPC] a function that will be used to override * the _performRpc method from the mock server. It is really useful to add * some custom rpc mocks, or to check some assertions. @@ -130,11 +491,7 @@ function removeSrcAttribute(el, widget) { * other methods on it. */ async function addMockEnvironment(widget, params) { - var Server = MockServer; - params.services = params.services || {}; - if (params.mockRPC) { - Server = MockServer.extend({ _performRpc: params.mockRPC }); - } + // log events triggered up if debug flag is true if (params.debug) { _observe(widget); var separator = window.location.href.indexOf('?') !== -1 ? "&" : "?"; @@ -142,6 +499,11 @@ async function addMockEnvironment(widget, params) { console.log('%c[debug] debug mode activated', 'color: blue; font-weight: bold;', url); } + // instantiate mock server + var Server = MockServer; + if (params.mockRPC) { + Server = MockServer.extend({ _performRpc: params.mockRPC }); + } var mockServer = new Server(params.data, { actions: params.actions, archs: params.archs, @@ -150,310 +512,60 @@ async function addMockEnvironment(widget, params) { widget: widget, }); - // make sure images do not trigger a GET on the server - $('body').on('DOMNodeInserted.removeSRC', function (event) { - removeSrcAttribute(event.target, widget); - }); + // build and set the Owl env on Component + if (!('mockSRC' in params)) { // redirect src rpcs to the mock server + params.mockSRC = true; + } + const cleanUp = await addMockEnvironmentOwl(owl.Component, params, mockServer); + const env = owl.Component.env; - // make sure the debounce value for input fields is set to 0 - var initialDebounceValue = DebouncedField.prototype.DEBOUNCE; - DebouncedField.prototype.DEBOUNCE = params.fieldDebounce || 0; - var initialDOMDebounceValue = dom.DEBOUNCE; - dom.DEBOUNCE = 0; - var initialSession, initialConfig, initialParameters, initialDebounce, initialThrottle; - initialSession = _.extend({}, session); - session.getTZOffset = function () { - return 0; // by default, but may be overridden in specific tests - }; - if ('session' in params) { - _.extend(session, params.session); - } - if ('config' in params) { - initialConfig = _.clone(config); - initialConfig.device = _.clone(config.device); - if ('device' in params.config) { - _.extend(config.device, params.config.device); - } - if ('debug' in params.config) { - odoo.debug = params.config.debug; - } - } - if ('translateParameters' in params) { - initialParameters = _.extend({}, core._t.database.parameters); - _.extend(core._t.database.parameters, params.translateParameters); - } - if (params.debounce === false) { - initialDebounce = _.debounce; - _.debounce = function (func) { - return func; - }; - } - if (!('throttle' in params) || !params.throttle) { - initialThrottle = _.throttle; - _.throttle = function (func) { - return func; - }; - } - - var widgetDestroy = widget.destroy; + // ensure to clean up everything when the widget will be destroyed + const destroy = widget.destroy; widget.destroy = function () { - // clear the caches (e.g. data_manager, ModelFieldSelector) when the - // widget is destroyed, at the end of each test to avoid collisions - core.bus.trigger('clear_cache'); - - Object.keys(services).forEach(function(s) { - var service = services[s]; - if (service && !service.isDestroyed()) - service.destroy(); - }); - - DebouncedField.prototype.DEBOUNCE = initialDebounceValue; - dom.DEBOUNCE = initialDOMDebounceValue; - if (params.debounce === false) { - _.debounce = initialDebounce; - } - if (!('throttle' in params) || !params.throttle) { - _.throttle = initialThrottle; - } - - var key; - if ('session' in params) { - for (key in session) { - delete session[key]; - } - } - _.extend(session, initialSession); - if ('config' in params) { - for (key in config) { - delete config[key]; - } - _.extend(config, initialConfig); - } - if ('translateParameters' in params) { - for (key in core._t.database.parameters) { - delete core._t.database.parameters[key]; - } - _.extend(core._t.database.parameters, initialParameters); - } - - $('body').off('DOMNodeInserted.removeSRC'); - $('body').removeClass('debug'); - $('.blockUI').remove(); - - widgetDestroy.call(this); + cleanUp(); + destroy.call(this, ...arguments); }; - // Dispatch service calls - // Note: some services could call other services at init, - // Which is why we have to init services after that - var services = {}; + // intercept service/data manager calls and redirect them to the env intercept(widget, 'call_service', function (ev) { - var args, result; - if (services[ev.data.service]) { - var service = services[ev.data.service]; - args = (ev.data.args || []); - result = service[ev.data.method].apply(service, args); - } else if (ev.data.service === 'ajax' && ev.data.method === 'rpc') { - // use ajax service that is mocked by the server - var route = ev.data.args[0]; - args = ev.data.args[1]; - result = mockServer.performRpc(route, args); + if (env.services[ev.data.service]) { + var service = env.services[ev.data.service]; + const result = service[ev.data.method].apply(service, ev.data.args || []); + ev.data.callback(result); } - ev.data.callback(result); }); - - intercept(widget, 'load_action', function (event) { - mockServer.performRpc('/web/action/load', { - action_id: event.data.actionID, - additional_context: event.data.context, - }).then(function (action) { - event.data.on_success(action); - }); + intercept(widget, 'load_action', async ev => { + const action = await env.dataManager.load_action(ev.data.actionID, ev.data.context); + ev.data.on_success(action); }); - - intercept(widget, "load_views", function (event) { - mockServer.performRpc('/web/dataset/call_kw/' + event.data.modelName, { - args: [], - kwargs: { - context: event.data.context, - options: event.data.options, - views: event.data.views, - }, - method: 'load_views', - model: event.data.modelName, - }).then(function (views) { - views = _.mapObject(views, function (viewParams) { - return fieldsViewGet(mockServer, viewParams); - }); - if ('search' in views && params.favoriteFilters) { - views.search.favoriteFilters = params.favoriteFilters; - } - event.data.on_success(views); - }); - }); - - intercept(widget, "get_session", function (event) { - event.data.callback(session); - }); - - intercept(widget, "load_filters", function (event) { - if (params.debug) { - console.log('[mock] load_filters', event.data); + intercept(widget, "load_views", async ev => { + const params = { + model: ev.data.modelName, + context: ev.data.context, + views_descr: ev.data.views, + }; + const views = await env.dataManager.load_views(params, ev.data.options); + if ('search' in views && params.favoriteFilters) { + views.search.favoriteFilters = params.favoriteFilters; } - event.data.on_success([]); + ev.data.on_success(views); + }); + intercept(widget, "get_session", ev => { + ev.data.callback(session); + }); + intercept(widget, "load_filters", async ev => { + const filters = await env.dataManager.load_filters(ev.data); + ev.data.on_success(filters); }); - // make sure all Odoo events bubbling up are intercepted - if ('intercepts' in params) { - _.each(params.intercepts, function (cb, name) { - intercept(widget, name, cb); - }); - } - - // Deploy services - var done = false; - var servicesToDeploy = _.clone(params.services); - if (!servicesToDeploy.ajax) { - services.ajax = null; // use mocked ajax from mocked server - } - while (!done) { - var serviceName = _.findKey(servicesToDeploy, function (Service) { - return !_.some(Service.prototype.dependencies, function (depName) { - return !_.has(services, depName); - }); - }); - if (serviceName) { - var Service = servicesToDeploy[serviceName]; - var service = services[serviceName] = new Service(widget); - delete servicesToDeploy[serviceName]; - - intercept(service, "get_session", function (event) { - event.data.callback(session); - }); - service.start(); - } else { - var serviceNames = _.keys(servicesToDeploy); - if (serviceNames.length) { - console.warn("Non loaded services:", serviceNames); - } - done = true; - } - } - - // Wait for asynchronous services to properly start - await new Promise(setTimeout); + // make sure all other Odoo events bubbling up are intercepted + Object.keys(params.intercepts || {}).forEach(function (name) { + intercept(widget, name, params.intercepts[name]); + }); return mockServer; } -/** - * performs a fields_view_get, and mocks the postprocessing done by the - * data_manager to return an equivalent structure. - * - * @param {MockServer} server - * @param {Object} params - * @param {string} params.model - * @returns {Object} an object with 3 keys: arch, fields and viewFields - */ -function fieldsViewGet(server, params) { - var fieldsView = server.fieldsViewGet(params); - // mock the structure produced by the DataManager - fieldsView.viewFields = fieldsView.fields; - fieldsView.fields = server.fieldsGet(params.model); - return fieldsView; -} - -/** - * Returns a mocked environment to be used by OWL components in tests. - * - * @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 {boolean} [params.debug] - * @param {Object} [params.env] - * @param {function} [params.mockRPC] - * @param {function} [params.server] an already instantiated server to avoid - * creating another one. - * @returns {Object} - */ -function getMockedOwlEnv(params = {}) { - let server; - if (params.server) { - server = params.server; - } else { - let Server = MockServer; - if (params.mockRPC) { - Server = MockServer.extend({ _performRpc: params.mockRPC }); - } - server = new Server(params.data, { - actions: params.actions, - archs: params.archs, - debug: params.debug, - }); - } - const env = Object.assign({ - dataManager: { - load_action: (actionID, context) => { - return server.performRpc('/web/action/load', { - kwargs: { - action_id: actionID, - additional_context: context, - }, - }); - }, - load_views: (params, options) => { - return server.performRpc('/web/dataset/call_kw/' + params.model, { - args: [], - kwargs: { - context: params.context, - options: options, - views: params.views_descr, - }, - method: 'load_views', - model: params.model, - }).then(function (views) { - return _.mapObject(views, viewParams => { - return fieldsViewGet(server, viewParams); - }); - }); - }, - load_filters: params => { - if (params.debug) { - console.log('[mock] load_filters', params); - } - return Promise.resolve([]); - }, - }, - session: params.session || {}, - }, params.env); - return makeTestEnvironment(env, server.performRpc.bind(server)); -} - -/** - * intercepts an event bubbling up the widget hierarchy. The event intercepted - * must be a "custom event", i.e. an event generated by the method 'trigger_up'. - * - * Note that this method really intercepts the event if @propagate is not set. - * It will not be propagated further, and even the handlers on the target will - * not fire. - * - * @param {Widget} widget the target widget (any Odoo widget) - * @param {string} eventName description of the event - * @param {function} fn callback executed when the even is intercepted - * @param {boolean} [propagate=false] - */ -function intercept(widget, eventName, fn, propagate) { - var _trigger_up = widget._trigger_up.bind(widget); - widget._trigger_up = function (event) { - if (event.name === eventName) { - fn(event); - if (!propagate) { return; } - } - _trigger_up(event); - }; -} - /** * Patch window.Date so that the time starts its flow from the provided Date. * @@ -644,7 +756,7 @@ function patchSetTimeout() { return { addMockEnvironment: addMockEnvironment, fieldsViewGet: fieldsViewGet, - getMockedOwlEnv: getMockedOwlEnv, + addMockEnvironmentOwl: addMockEnvironmentOwl, intercept: intercept, patchDate: patchDate, patch: patch, diff --git a/addons/web/static/tests/owl_compatibility_tests.js b/addons/web/static/tests/owl_compatibility_tests.js index f49fee5f78a..1cc463d0eeb 100644 --- a/addons/web/static/tests/owl_compatibility_tests.js +++ b/addons/web/static/tests/owl_compatibility_tests.js @@ -5,9 +5,9 @@ odoo.define('web.OwlCompatibilityTests', function (require) { const testUtils = require('web.test_utils'); const Widget = require('web.Widget'); - const getMockedOwlEnv = testUtils.mock.getMockedOwlEnv; const makeTestPromise = testUtils.makeTestPromise; const nextTick = testUtils.nextTick; + const addMockEnvironmentOwl = testUtils.mock.addMockEnvironmentOwl; const { Component, tags, useState } = owl; const { xml } = tags; @@ -448,17 +448,17 @@ odoo.define('web.OwlCompatibilityTests', function (require) { this.MyWidget = MyWidget; } } - Parent.env = getMockedOwlEnv({ - mockRPC: function (route, args) { - assert.step(`${route} ${args.val}`); - return Promise.resolve(); - }, - }); Parent.template = xml`
`; Parent.components = { ComponentAdapter }; + const cleanUp = await addMockEnvironmentOwl(Parent, { + mockRPC: function (route, args) { + assert.step(`${route} ${args.val}`); + return Promise.resolve(); + }, + }); const target = testUtils.prepareTarget(); const parent = new Parent(); @@ -468,6 +468,7 @@ odoo.define('web.OwlCompatibilityTests', function (require) { assert.verifySteps(['some/route 2']); parent.destroy(); + cleanUp(); }); QUnit.test("sub widget that calls a service", async function (assert) { @@ -493,16 +494,14 @@ odoo.define('web.OwlCompatibilityTests', function (require) { this.MyWidget = MyWidget; } } - const env = getMockedOwlEnv(); - env.services.math = { - sqrt: v => Math.sqrt(v), - }; - Parent.env = env; Parent.template = xml`
`; Parent.components = { ComponentAdapter }; + Parent.env.services.math = { + sqrt: v => Math.sqrt(v), + }; const target = testUtils.prepareTarget(); const parent = new Parent(); @@ -525,20 +524,21 @@ odoo.define('web.OwlCompatibilityTests', function (require) { this.MyWidget = MyWidget; } } - Parent.env = getMockedOwlEnv({ - session: { key: 'value' }, - }); Parent.template = xml`
`; Parent.components = { ComponentAdapter }; + const cleanUp = await addMockEnvironmentOwl(Parent, { + session: { key: 'value' }, + }); const target = testUtils.prepareTarget(); const parent = new Parent(); await parent.mount(target); parent.destroy(); + cleanUp(); }); QUnit.test("sub widget that calls load_views", async function (assert) { @@ -555,7 +555,12 @@ odoo.define('web.OwlCompatibilityTests', function (require) { this.MyWidget = MyWidget; } } - Parent.env = getMockedOwlEnv({ + Parent.template = xml` +
+ +
`; + Parent.components = { ComponentAdapter }; + const cleanUp = await addMockEnvironmentOwl(Parent, { mockRPC: function (route, args) { assert.strictEqual(route, '/web/dataset/call_kw/some_model'); assert.deepEqual(args.kwargs.context, { x: 2 }); @@ -563,11 +568,6 @@ odoo.define('web.OwlCompatibilityTests', function (require) { return Promise.resolve(); }, }); - Parent.template = xml` -
- -
`; - Parent.components = { ComponentAdapter }; const target = testUtils.prepareTarget(); const parent = new Parent(); @@ -576,6 +576,7 @@ odoo.define('web.OwlCompatibilityTests', function (require) { assert.strictEqual(parent.el.innerHTML, '
'); parent.destroy(); + cleanUp(); }); QUnit.test("sub widgets in a t-if/t-else", async function (assert) { diff --git a/addons/web/static/tests/views/view_dialogs_tests.js b/addons/web/static/tests/views/view_dialogs_tests.js index 26fc002605a..7d1554dc78d 100644 --- a/addons/web/static/tests/views/view_dialogs_tests.js +++ b/addons/web/static/tests/views/view_dialogs_tests.js @@ -12,8 +12,7 @@ var createView = testUtils.createView; async function createParent(params) { var widget = new Widget(); - params.server = await testUtils.mock.addMockEnvironment(widget, params); - owl.Component.env = testUtils.mock.getMockedOwlEnv(params); + params.server = await testUtils.mock.addMockEnvironment(widget, params); return widget; } From b4ba0fc34082deec8ca6c871ce7a203c0a3a61a4 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 5 Jun 2020 09:18:14 +0000 Subject: [PATCH 3/3] [FIX] *: adapt tests to env rework This commit adapts tests following recent changes on the helpers. The main change is that addMockEnvironment (and all functions using it) are now async, as they need to wait for services to be started. --- addons/bus/static/tests/bus_tests.js | 12 +- .../tests/systray_activity_menu_tests.js | 2 +- .../static/tests/hr_attendance_tests.js | 6 +- .../tests/website_livechat_window_tests.js | 4 +- .../tests/document_thread_window_tests.js | 66 +++++---- .../static/tests/document_viewer_tests.js | 4 +- .../static/tests/mail_status_manager_tests.js | 10 +- .../models/cc_throttle_function_tests.js | 8 +- .../systray/systray_activity_menu_tests.js | 6 +- .../basic_thread_window_tests.js | 30 ++-- .../blank_thread_window_tests.js | 14 +- .../hidden_thread_window_tests.js | 10 +- .../moderation_thread_window_tests.js | 6 +- .../typing_thread_window_tests.js | 6 +- .../static/tests/tools/debug_manager_tests.js | 2 +- .../tests/systray_activity_menu_tests.js | 2 +- ...stock_traceability_report_backend_tests.js | 2 +- .../chrome/keyboard_navigation_mixin_tests.js | 4 +- addons/web/static/tests/chrome/menu_tests.js | 4 +- .../web/static/tests/chrome/systray_tests.js | 3 +- .../static/tests/chrome/user_menu_tests.js | 2 +- .../control_panel/custom_filter_item_tests.js | 31 ++--- addons/web/static/tests/core/dialog_tests.js | 12 +- addons/web/static/tests/core/widget_tests.js | 2 +- .../relational_fields/field_many2one_tests.js | 4 +- .../static/tests/tools/debug_manager_tests.js | 6 +- .../static/tests/views/basic_model_tests.js | 128 +++++++++--------- .../web/static/tests/views/calendar_tests.js | 17 +-- addons/web/static/tests/views/form_tests.js | 17 +-- .../static/tests/views/kanban_model_tests.js | 10 +- addons/web/static/tests/views/kanban_tests.js | 17 +-- addons/web/static/tests/views/list_tests.js | 17 +-- .../tests/widgets/company_switcher_tests.js | 2 +- .../tests/widgets/domain_selector_tests.js | 10 +- .../widgets/model_field_selector_tests.js | 14 +- addons/web_editor/static/tests/test_utils.js | 4 +- .../static/tests/tour_manager_tests.js | 2 +- 37 files changed, 225 insertions(+), 271 deletions(-) diff --git a/addons/bus/static/tests/bus_tests.js b/addons/bus/static/tests/bus_tests.js index 551a145bda9..b13694a7afb 100644 --- a/addons/bus/static/tests/bus_tests.js +++ b/addons/bus/static/tests/bus_tests.js @@ -27,7 +27,7 @@ QUnit.module('Bus', { var pollPromise = testUtils.makeTestPromise(); var parent = new Widget(); - testUtils.mock.addMockEnvironment(parent, { + await testUtils.mock.addMockEnvironment(parent, { data: {}, services: { bus_service: BusService, @@ -101,7 +101,7 @@ QUnit.module('Bus', { var pollPromise = testUtils.makeTestPromise(); var parent = new Widget(); - testUtils.mock.addMockEnvironment(parent, { + await testUtils.mock.addMockEnvironment(parent, { data: {}, services: { bus_service: BusService, @@ -142,7 +142,7 @@ QUnit.module('Bus', { var pollPromiseMaster = testUtils.makeTestPromise(); var parentMaster = new Widget(); - testUtils.mock.addMockEnvironment(parentMaster, { + await testUtils.mock.addMockEnvironment(parentMaster, { data: {}, services: { bus_service: BusService, @@ -173,7 +173,7 @@ QUnit.module('Bus', { // slave await testUtils.nextTick(); var parentSlave = new Widget(); - testUtils.mock.addMockEnvironment(parentSlave, { + await testUtils.mock.addMockEnvironment(parentSlave, { data: {}, services: { bus_service: BusService, @@ -220,7 +220,7 @@ QUnit.module('Bus', { var pollPromiseMaster = testUtils.makeTestPromise(); var parentMaster = new Widget(); - testUtils.mock.addMockEnvironment(parentMaster, { + await testUtils.mock.addMockEnvironment(parentMaster, { data: {}, services: { bus_service: BusService, @@ -252,7 +252,7 @@ QUnit.module('Bus', { await testUtils.nextTick(); var parentSlave = new Widget(); var pollPromiseSlave = testUtils.makeTestPromise(); - testUtils.mock.addMockEnvironment(parentSlave, { + await testUtils.mock.addMockEnvironment(parentSlave, { data: {}, services: { bus_service: BusService, diff --git a/addons/calendar/static/tests/systray_activity_menu_tests.js b/addons/calendar/static/tests/systray_activity_menu_tests.js index 5a25792ff34..02937bae0ed 100644 --- a/addons/calendar/static/tests/systray_activity_menu_tests.js +++ b/addons/calendar/static/tests/systray_activity_menu_tests.js @@ -41,7 +41,7 @@ QUnit.test('activity menu widget:today meetings', async function (assert) { assert.expect(6); var self = this; var activityMenu = new ActivityMenu(); - testUtils.mock.addMockEnvironment(activityMenu, { + await testUtils.mock.addMockEnvironment(activityMenu, { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { diff --git a/addons/hr_attendance/static/tests/hr_attendance_tests.js b/addons/hr_attendance/static/tests/hr_attendance_tests.js index 22de18817ad..1fc06e7474e 100644 --- a/addons/hr_attendance/static/tests/hr_attendance_tests.js +++ b/addons/hr_attendance/static/tests/hr_attendance_tests.js @@ -59,7 +59,7 @@ QUnit.module('HR Attendance', { var $target = $('#qunit-fixture'); var clientAction = new MyAttendances(null, {}); - testUtils.mock.addMockEnvironment(clientAction, { + await testUtils.mock.addMockEnvironment(clientAction, { data: this.data, session: { uid: 1, @@ -80,7 +80,7 @@ QUnit.module('HR Attendance', { var self = this; var rpcCount = 0; var clientAction = new KioskMode(null, {}); - testUtils.mock.addMockEnvironment(clientAction, { + await testUtils.mock.addMockEnvironment(clientAction, { data: this.data, session: { uid: 1, @@ -124,7 +124,7 @@ QUnit.module('HR Attendance', { barcode: barcode, }; var clientAction = new GreetingMessage(null, action); - testUtils.mock.addMockEnvironment(clientAction, { + await testUtils.mock.addMockEnvironment(clientAction, { data: self.data, session: { uid: 1, diff --git a/addons/im_livechat/static/tests/website_livechat_window_tests.js b/addons/im_livechat/static/tests/website_livechat_window_tests.js index e8a296fa095..8907d790cb1 100644 --- a/addons/im_livechat/static/tests/website_livechat_window_tests.js +++ b/addons/im_livechat/static/tests/website_livechat_window_tests.js @@ -45,7 +45,7 @@ QUnit.test('basic rendering', async function (assert) { livechat.setMessages(messages); var chatWindow = new ChatWindow(parent, livechat); - testUtils.mock.addMockEnvironment(parent, {}); + await testUtils.mock.addMockEnvironment(parent, {}); await chatWindow.appendTo($('#qunit-fixture')); chatWindow.render(); @@ -95,7 +95,7 @@ QUnit.test('define several livechat colors', async function (assert) { headerBackgroundColor: 'yellow', titleColor: 'green', }); - testUtils.mock.addMockEnvironment(parent, {}); + await testUtils.mock.addMockEnvironment(parent, {}); await chatWindow.appendTo($('#qunit-fixture')); chatWindow.render(); assert.strictEqual( diff --git a/addons/mail/static/tests/document_thread_window_tests.js b/addons/mail/static/tests/document_thread_window_tests.js index f01ab0925f2..c4a84040433 100644 --- a/addons/mail/static/tests/document_thread_window_tests.js +++ b/addons/mail/static/tests/document_thread_window_tests.js @@ -1,6 +1,8 @@ odoo.define('mail.documentThreadWindowTests', function (require) { "use strict"; +const Bus = require('web.Bus'); +const LocalStorageService = require('web.LocalStorageService'); var mailTestUtils = require('mail.testUtils'); var MessagingMenu = require('mail.systray.MessagingMenu'); @@ -128,20 +130,23 @@ QUnit.test('open a document thread in a thread window', async function (assert) QUnit.test('expand a document thread window', async function (assert) { assert.expect(4); + const bus = new Bus(); + bus.on('do-action', null, function (payload) { + assert.deepEqual(payload.action, { + res_id: 1, + res_model: 'some.res.model', + type: 'ir.actions.act_window', + views: [[false, 'form']], + }, "should open the document in form view"); + }); + var messagingMenu = new MessagingMenu(); await testUtils.mock.addMockEnvironment(messagingMenu, { services: this.services, data: this.data, session: this.session, - intercepts: { - do_action: function (ev) { - assert.deepEqual(ev.data.action, { - res_id: 1, - res_model: 'some.res.model', - type: 'ir.actions.act_window', - views: [[false, 'form']], - }, "should open the document in form view"); - }, + env: { + bus }, }); await messagingMenu.appendTo($('#qunit-fixture')); @@ -177,8 +182,18 @@ QUnit.test('post messages in a document thread window', async function (assert) res_id: 1, }; var messagingMenu = new MessagingMenu(); + const MockedLocalStorageService = LocalStorageService.extend({ + setItem(key, value) { + if (key === 'mail.document_threads_last_message') { + assert.deepEqual(value.messageData, newMessage, + "should write sent message in local storage, to share info with other tabs"); + } + }, + }); await testUtils.mock.addMockEnvironment(messagingMenu, { - services: this.services, + services: Object.assign(this.services, { + local_storage: MockedLocalStorageService, + }), data: this.data, session: this.session, mockRPC: function (route, args) { @@ -198,13 +213,7 @@ QUnit.test('post messages in a document thread window', async function (assert) return this._super.apply(this, arguments); }, }); - testUtils.mock.intercept(messagingMenu, 'call_service', function (ev) { - if (ev.data.service === 'local_storage' && ev.data.method === 'setItem' && - ev.data.args[0] === 'mail.document_threads_last_message') { - assert.deepEqual(ev.data.args[1].messageData, newMessage, - "should write sent message in local storage, to share info with other tabs"); - } - }, true); + await messagingMenu.appendTo($('#qunit-fixture')); // toggle the messaging menu and open the documentThread @@ -229,24 +238,25 @@ QUnit.test('post messages in a document thread window', async function (assert) QUnit.test('open, fold, unfold and close a document thread window', async function (assert) { assert.expect(20); var messagingMenu = new MessagingMenu(); - await testUtils.mock.addMockEnvironment(messagingMenu, { - services: this.services, - data: this.data, - session: this.session, - }); - await testUtils.nextTick(); - testUtils.mock.intercept(messagingMenu, 'call_service', function (ev) { - if (ev.data.service === 'local_storage' && ev.data.method === 'setItem') { - const key = ev.data.args[0]; + const MockedLocalStorageService = LocalStorageService.extend({ + setItem(key, value) { if (key === 'mail.document_threads_state/some.res.model_1') { - const state = ev.data.args[1].state; + const state = value.state; assert.ok(state); assert.ok(state.name); assert.ok(state.windowState); assert.step(`${key}: { name: "${state.name}", windowState: '${state.windowState}' }`); } } - }, true); + }); + await testUtils.mock.addMockEnvironment(messagingMenu, { + services: Object.assign(this.services, { + local_storage: MockedLocalStorageService, + }), + data: this.data, + session: this.session, + }); + await testUtils.nextTick(); await messagingMenu.appendTo($('#qunit-fixture')); // toggle the messaging menu and open the documentThread diff --git a/addons/mail/static/tests/document_viewer_tests.js b/addons/mail/static/tests/document_viewer_tests.js index aa50483b48a..ca6b433d519 100644 --- a/addons/mail/static/tests/document_viewer_tests.js +++ b/addons/mail/static/tests/document_viewer_tests.js @@ -14,7 +14,7 @@ var Widget = require('web.Widget'); * @param {boolean} [params.debug] * @returns {DocumentViewer} */ -var createViewer = function (params) { +var createViewer = async function (params) { var parent = new Widget(); var viewer = new DocumentViewer(parent, params.attachments, params.attachmentID); @@ -32,7 +32,7 @@ var createViewer = function (params) { return Promise.resolve(); } }; - testUtils.mock.addMockEnvironment(parent, { + await testUtils.mock.addMockEnvironment(parent, { mockRPC: function () { if (params.mockRPC) { var _super = this._super; diff --git a/addons/mail/static/tests/mail_status_manager_tests.js b/addons/mail/static/tests/mail_status_manager_tests.js index f21cefdec9e..891cf3aaa68 100644 --- a/addons/mail/static/tests/mail_status_manager_tests.js +++ b/addons/mail/static/tests/mail_status_manager_tests.js @@ -17,9 +17,9 @@ QUnit.module('Status manager', { this.timeoutMock = mailTestUtils.patchMailTimeouts(); }, }); -QUnit.test('simple set im_status', function (assert) { +QUnit.test('simple set im_status', async function (assert) { assert.expect(1); - var parent = testUtils.createParent({ + var parent = await testUtils.createParent({ services: this.services, mockRPC: function (route, args) { if (route === '/mail/init_messaging') { @@ -41,7 +41,7 @@ QUnit.test('simple set im_status', function (assert) { QUnit.test('multi get_im_status', async function (assert) { assert.expect(8); var readCount = 0; - var parent = testUtils.createParent({ + var parent = await testUtils.createParent({ //data: this.data, services: this.services, mockRPC: function (route, args) { @@ -86,7 +86,7 @@ QUnit.test('multi get_im_status', async function (assert) { QUnit.test('update loop', async function (assert) { assert.expect(10); var readCount = 0; - var parent = testUtils.createParent({ + var parent = await testUtils.createParent({ services: this.services, mockRPC: function (route, args) { if (route === '/mail/init_messaging') { @@ -162,7 +162,7 @@ QUnit.test('update status', async function (assert) { } }); var statusWidget = new StatusWidget(); - testUtils.mock.addMockEnvironment(statusWidget, { + await testUtils.mock.addMockEnvironment(statusWidget, { services: this.services, mockRPC: function (route, args) { if (route === '/mail/init_messaging') { diff --git a/addons/mail/static/tests/models/cc_throttle_function_tests.js b/addons/mail/static/tests/models/cc_throttle_function_tests.js index 5ab00ef6048..f760675b1f3 100644 --- a/addons/mail/static/tests/models/cc_throttle_function_tests.js +++ b/addons/mail/static/tests/models/cc_throttle_function_tests.js @@ -31,7 +31,7 @@ QUnit.module('mail', { QUnit.module('model', {}, function () { QUnit.module('CC Throttle Function'); -QUnit.test('cancel()', function (assert) { +QUnit.test('cancel()', async function (assert) { var done = assert.async(); assert.expect(3); @@ -39,7 +39,7 @@ QUnit.test('cancel()', function (assert) { var def = testUtils.makeTestPromise(); var step = 1; - var widget = testUtils.createParent({ + var widget = await testUtils.createParent({ mockRPC: function (route, args) { if (args.method === '__rpc__') { assert.step(args.method + args.args[0]); @@ -87,7 +87,7 @@ QUnit.test('cancel()', function (assert) { }); }); -QUnit.test('clear()', function (assert) { +QUnit.test('clear()', async function (assert) { var done = assert.async(); assert.expect(5); @@ -96,7 +96,7 @@ QUnit.test('clear()', function (assert) { var def2 = testUtils.makeTestPromise(); var step = 1; - var widget = testUtils.createParent({ + var widget = await testUtils.createParent({ mockRPC: function (route, args) { if (args.method === '__rpc__') { assert.step(args.method + args.args[0]); diff --git a/addons/mail/static/tests/systray/systray_activity_menu_tests.js b/addons/mail/static/tests/systray/systray_activity_menu_tests.js index 1cedca2b9c1..23f3500001b 100644 --- a/addons/mail/static/tests/systray/systray_activity_menu_tests.js +++ b/addons/mail/static/tests/systray/systray_activity_menu_tests.js @@ -83,7 +83,7 @@ QUnit.test('activity menu widget: menu with no records', async function (assert) assert.expect(1); var activityMenu = new ActivityMenu(); - testUtils.mock.addMockEnvironment(activityMenu, { + await testUtils.mock.addMockEnvironment(activityMenu, { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { @@ -102,7 +102,7 @@ QUnit.test('activity menu widget: activity menu with 3 records', async function assert.expect(10); var self = this; var activityMenu = new ActivityMenu(); - testUtils.mock.addMockEnvironment(activityMenu, { + await testUtils.mock.addMockEnvironment(activityMenu, { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { @@ -163,7 +163,7 @@ QUnit.test('activity menu widget: activity view icon', async function (assert) { assert.expect(12); var self = this; var activityMenu = new ActivityMenu(); - testUtils.mock.addMockEnvironment(activityMenu, { + await testUtils.mock.addMockEnvironment(activityMenu, { services: this.services, session: this.session, mockRPC: function (route, args) { diff --git a/addons/mail/static/tests/thread_window/basic_thread_window_tests.js b/addons/mail/static/tests/thread_window/basic_thread_window_tests.js index 87e3c536998..61414d2aeaa 100644 --- a/addons/mail/static/tests/thread_window/basic_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/basic_thread_window_tests.js @@ -56,7 +56,7 @@ QUnit.module('Basic', { this.services = mailTestUtils.getMailServices(); this.ORIGINAL_THREAD_WINDOW_APPENDTO = this.services.mail_service.prototype.THREAD_WINDOW_APPENDTO; - this.createParent = function (params) { + this.createParent = async function (params) { var widget = new Widget(); // in non-debug mode, append thread windows in qunit-fixture @@ -68,7 +68,7 @@ QUnit.module('Basic', { self.services.mail_service.prototype.THREAD_WINDOW_APPENDTO = '#qunit-fixture'; } - testUtils.mock.addMockEnvironment(widget, params); + await testUtils.mock.addMockEnvironment(widget, params); return widget; }; }, @@ -81,7 +81,7 @@ QUnit.module('Basic', { QUnit.test('basic rendering thread window', async function (assert) { assert.expect(10); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -128,7 +128,7 @@ QUnit.test('basic rendering thread window', async function (assert) { QUnit.test('close thread window using ESCAPE key', async function (assert) { assert.expect(5); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, mockRPC: function (route, args) { @@ -169,7 +169,7 @@ QUnit.test('close thread window using ESCAPE key', async function (assert) { QUnit.test('thread window\'s input can still be focused when the UI is blocked', async function (assert) { assert.expect(2); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -205,7 +205,7 @@ QUnit.test('thread window\'s input can still be focused when the UI is blocked', QUnit.test('emoji popover should open correctly in thread windows', async function (assert) { assert.expect(1); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -227,7 +227,7 @@ QUnit.test('emoji popover should open correctly in thread windows', async functi QUnit.test('do not increase unread counter when receiving message with myself as author', async function (assert) { assert.expect(4); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, session: { partner_id: 3 } @@ -274,7 +274,7 @@ QUnit.test('do not increment unread counter with focus on thread window', async // order to set the focus on it. assert.expect(2); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, session: { partner_id: 3 } @@ -322,7 +322,7 @@ QUnit.test('do not mark as read the newly open thread window from received messa message_unread_counter: 0, }]; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, session: { partner_id: 3 }, @@ -393,7 +393,7 @@ QUnit.test('show document link of message linked to a document', async function channel_type: "public", }); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, session: { partner_id: 3 }, @@ -510,7 +510,7 @@ QUnit.test('do not auto-focus chat window on receiving new message from new DM', state: 'open', }]; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, session: { partner_id: 3 }, @@ -600,7 +600,7 @@ QUnit.test('no out-of-office status in thread window', async function (assert) { }], }, }; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -650,7 +650,7 @@ QUnit.test('receive 2 new DM messages in quick succession (no chat window initia }], }; - const parent = this.createParent({ + const parent = await this.createParent({ data: this.data, mockRPC(route, args) { if (args.method === 'channel_minimize') { @@ -740,7 +740,7 @@ QUnit.test('non-deletable message attachments', async function (assert) { record_name: "general", res_id: 1, }]; - const parent = this.createParent({ + const parent = await this.createParent({ data: this.data, services: this.services, }); @@ -781,7 +781,7 @@ QUnit.test('join channel from click channel mention', async function (assert) { channel_ids: [1], }); - const parent = this.createParent({ + const parent = await this.createParent({ data: this.data, services: this.services, session: { partner_id: 3 }, diff --git a/addons/mail/static/tests/thread_window/blank_thread_window_tests.js b/addons/mail/static/tests/thread_window/blank_thread_window_tests.js index 4a24a80d4c3..2d295144b62 100644 --- a/addons/mail/static/tests/thread_window/blank_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/blank_thread_window_tests.js @@ -32,7 +32,7 @@ QUnit.module('Blank', { this.services = mailTestUtils.getMailServices(); this.ORIGINAL_THREAD_WINDOW_APPENDTO = this.services.mail_service.prototype.THREAD_WINDOW_APPENDTO; - this.createParent = function (params) { + this.createParent = async function (params) { var widget = new Widget(); // in non-debug mode, append thread windows in qunit-fixture @@ -44,7 +44,7 @@ QUnit.module('Blank', { self.services.mail_service.prototype.THREAD_WINDOW_APPENDTO = '#qunit-fixture'; } - testUtils.mock.addMockEnvironment(widget, params); + await testUtils.mock.addMockEnvironment(widget, params); return widget; }; @@ -90,7 +90,7 @@ QUnit.module('Blank', { QUnit.test('basic rendering blank thread window', async function (assert) { assert.expect(5); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -117,7 +117,7 @@ QUnit.test('basic rendering blank thread window', async function (assert) { QUnit.test('close blank thread window', async function (assert) { assert.expect(1); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -139,7 +139,7 @@ QUnit.test('fold blank thread window', async function (assert) { // (e.g. 400px) assert.expect(3); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -191,7 +191,7 @@ QUnit.test('open new DM chat from blank thread window', async function (assert) records: [], }; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, mockRPC: function (route, args) { @@ -274,7 +274,7 @@ QUnit.test('open already detached DM chat from blank thread window', async funct name: 'DemoUser1', }]; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, mockRPC: function (route, args) { diff --git a/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js b/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js index 078230e276a..36da266128c 100644 --- a/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/hidden_thread_window_tests.js @@ -31,7 +31,7 @@ QUnit.module('Hidden', { this.services = mailTestUtils.getMailServices(); this.ORIGINAL_THREAD_WINDOW_APPENDTO = this.services.mail_service.prototype.THREAD_WINDOW_APPENDTO; - this.createParent = function (params) { + this.createParent = async function (params) { var widget = new Widget(); // in non-debug mode, append thread windows in qunit-fixture @@ -43,7 +43,7 @@ QUnit.module('Hidden', { self.services.mail_service.prototype.THREAD_WINDOW_APPENDTO = '#qunit-fixture'; } - testUtils.mock.addMockEnvironment(widget, params); + await testUtils.mock.addMockEnvironment(widget, params); return widget; }; }, @@ -86,7 +86,7 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver }, }; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -172,7 +172,7 @@ QUnit.test('hidden thread windows dropdown when not enough horizontal space (ver }, }; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); @@ -266,7 +266,7 @@ QUnit.test('receive message from hidden thread window', async function (assert) }, }; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); diff --git a/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js b/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js index 732a5fe7938..db2561fd048 100644 --- a/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/moderation_thread_window_tests.js @@ -62,7 +62,7 @@ QUnit.module('Moderation', { this.services = mailTestUtils.getMailServices(); this.ORIGINAL_THREAD_WINDOW_APPENDTO = this.services.mail_service.prototype.THREAD_WINDOW_APPENDTO; - this.createParent = function (params) { + this.createParent = async function (params) { var widget = new Widget(); // in non-debug mode, append thread windows in qunit-fixture @@ -74,7 +74,7 @@ QUnit.module('Moderation', { self.services.mail_service.prototype.THREAD_WINDOW_APPENDTO = '#qunit-fixture'; } - testUtils.mock.addMockEnvironment(widget, params); + await testUtils.mock.addMockEnvironment(widget, params); return widget; }; }, @@ -110,7 +110,7 @@ QUnit.test('moderator: moderated channel with pending moderation message', async res_id: 1, }]; - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, }); diff --git a/addons/mail/static/tests/thread_window/typing_thread_window_tests.js b/addons/mail/static/tests/thread_window/typing_thread_window_tests.js index be2bd4beec8..53960dc283d 100644 --- a/addons/mail/static/tests/thread_window/typing_thread_window_tests.js +++ b/addons/mail/static/tests/thread_window/typing_thread_window_tests.js @@ -51,7 +51,7 @@ QUnit.module('Typing', { await testUtils.nextMicrotaskTick(); }; - this.createParent = function (params) { + this.createParent = async function (params) { var widget = new Widget(); // in non-debug mode, append thread windows in qunit-fixture @@ -63,7 +63,7 @@ QUnit.module('Typing', { self.services.mail_service.prototype.THREAD_WINDOW_APPENDTO = '#qunit-fixture'; } - testUtils.mock.addMockEnvironment(widget, params); + await testUtils.mock.addMockEnvironment(widget, params); return widget; }; }, @@ -76,7 +76,7 @@ QUnit.module('Typing', { QUnit.test('receive typing notification', async function (assert) { assert.expect(4); - var parent = this.createParent({ + var parent = await this.createParent({ data: this.data, services: this.services, mockRPC: function (route, args) { diff --git a/addons/mail/static/tests/tools/debug_manager_tests.js b/addons/mail/static/tests/tools/debug_manager_tests.js index c21d36572b9..151b89024b6 100644 --- a/addons/mail/static/tests/tools/debug_manager_tests.js +++ b/addons/mail/static/tests/tools/debug_manager_tests.js @@ -10,7 +10,7 @@ QUnit.module('Mail DebugManager', {}, function () { QUnit.test("Manage Messages", async function (assert) { assert.expect(3); - var debugManager = createDebugManager({ + var debugManager = await createDebugManager({ intercepts: { do_action: function (event) { assert.deepEqual(event.data.action, { diff --git a/addons/note/static/tests/systray_activity_menu_tests.js b/addons/note/static/tests/systray_activity_menu_tests.js index 768412df2eb..85ca835e674 100644 --- a/addons/note/static/tests/systray_activity_menu_tests.js +++ b/addons/note/static/tests/systray_activity_menu_tests.js @@ -38,7 +38,7 @@ QUnit.test('note activity menu widget: create note from activity menu', async fu assert.expect(15); var self = this; var activityMenu = new ActivityMenu(); - testUtils.mock.addMockEnvironment(activityMenu, { + await testUtils.mock.addMockEnvironment(activityMenu, { services: this.services, mockRPC: function (route, args) { if (args.method === 'systray_get_activities') { diff --git a/addons/stock/static/tests/stock_traceability_report_backend_tests.js b/addons/stock/static/tests/stock_traceability_report_backend_tests.js index feba8f0f9e5..20a9d809d70 100644 --- a/addons/stock/static/tests/stock_traceability_report_backend_tests.js +++ b/addons/stock/static/tests/stock_traceability_report_backend_tests.js @@ -13,7 +13,7 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) * @returns {Promise} */ async function createStockReportAction(params) { - const parent = testUtils.createParent(params); + const parent = await testUtils.createParent(params); const report = new StockReportGeneric(parent, params.action); const target = testUtils.prepareTarget(params.debug); diff --git a/addons/web/static/tests/chrome/keyboard_navigation_mixin_tests.js b/addons/web/static/tests/chrome/keyboard_navigation_mixin_tests.js index dcf1c23ce39..b0c638dd802 100644 --- a/addons/web/static/tests/chrome/keyboard_navigation_mixin_tests.js +++ b/addons/web/static/tests/chrome/keyboard_navigation_mixin_tests.js @@ -26,7 +26,7 @@ QUnit.module('KeyboardNavigationMixin', function () { return this._super(...arguments); }, }); - var parent = testUtils.createParent({}); + var parent = await testUtils.createParent({}); var w = new KeyboardWidget(parent); await w.appendTo($target); @@ -67,7 +67,7 @@ QUnit.module('KeyboardNavigationMixin', function () { return this._super(...arguments); }, }); - var parent = testUtils.createParent({}); + var parent = await testUtils.createParent({}); var w = new KeyboardWidget(parent); await w.appendTo($target); diff --git a/addons/web/static/tests/chrome/menu_tests.js b/addons/web/static/tests/chrome/menu_tests.js index c7ce40b16c2..a50e2189906 100644 --- a/addons/web/static/tests/chrome/menu_tests.js +++ b/addons/web/static/tests/chrome/menu_tests.js @@ -13,7 +13,7 @@ odoo.define('web.menu_tests', function (require) { QUnit.test('Systray on_attach_callback is called', async function (assert) { assert.expect(4); - const parent = testUtils.createParent({}); + const parent = await testUtils.createParent({}); // Add some widgets to the systray const Widget1 = Widget.extend({ @@ -22,7 +22,7 @@ odoo.define('web.menu_tests', function (require) { const Widget2 = Widget.extend({ on_attach_callback: () => assert.step('on_attach_callback widget2') }); - SystrayMenu.Items = [Widget1, Widget2] + SystrayMenu.Items = [Widget1, Widget2]; testUtils.mock.patch(SystrayMenu, { on_attach_callback: function () { diff --git a/addons/web/static/tests/chrome/systray_tests.js b/addons/web/static/tests/chrome/systray_tests.js index c5238ceff04..941e275f19b 100644 --- a/addons/web/static/tests/chrome/systray_tests.js +++ b/addons/web/static/tests/chrome/systray_tests.js @@ -7,8 +7,7 @@ odoo.define('web.systray_tests', function (require) { QUnit.test('Adding async components to the registry respects the sequence', async function (assert) { assert.expect(2); - var parent = testUtils.createParent({ - }); + var parent = await testUtils.createParent({}); var prom = testUtils.makeTestPromise(); var synchronousFirstWidget = Widget.extend({ diff --git a/addons/web/static/tests/chrome/user_menu_tests.js b/addons/web/static/tests/chrome/user_menu_tests.js index 73c74b0753c..752697a5228 100644 --- a/addons/web/static/tests/chrome/user_menu_tests.js +++ b/addons/web/static/tests/chrome/user_menu_tests.js @@ -13,7 +13,7 @@ QUnit.module('chrome', {}, function () { var parent = new Widget(); - testUtils.mock.addMockEnvironment(parent, {}); + await testUtils.mock.addMockEnvironment(parent, {}); var userMenu = new UserMenu(parent); await userMenu.appendTo($('body')); diff --git a/addons/web/static/tests/control_panel/custom_filter_item_tests.js b/addons/web/static/tests/control_panel/custom_filter_item_tests.js index 52a5aa738a6..7602f56aae0 100644 --- a/addons/web/static/tests/control_panel/custom_filter_item_tests.js +++ b/addons/web/static/tests/control_panel/custom_filter_item_tests.js @@ -6,21 +6,10 @@ odoo.define('web.filter_menu_generator_tests', function (require) { const { Model } = require('web.model'); const pyUtils = require('web.py_utils'); const testUtils = require('web.test_utils'); - const session = require('web.session'); const cpHelpers = testUtils.controlPanel; const { createComponent } = testUtils; - function patchSession(newSession) { - // We have to patch the "legacy" session because field_utils is using it. - // TODO: remove it when the field_utils are re-written. - const initialSession = session; - Object.assign(session, newSession); - return function () { - Object.assign(session, initialSession); - }; - } - QUnit.module('Components', { beforeEach: function () { this.fields = { @@ -203,11 +192,6 @@ odoo.define('web.filter_menu_generator_tests', function (require) { QUnit.test('custom filter datetime with equal operator', async function (assert) { assert.expect(4); - patchSession({ - getTZOffset: function () { - return -240; - }, - }); class MockedControlPanelModel extends Model { createNewFilters(preFilters) { const preFilter = preFilters[0]; @@ -224,6 +208,11 @@ odoo.define('web.filter_menu_generator_tests', function (require) { props: { fields: this.fields, }, + session: { + getTZOffset() { + return -240; + }, + }, env: { controlPanelModel }, }); @@ -243,11 +232,6 @@ odoo.define('web.filter_menu_generator_tests', function (require) { QUnit.test('custom filter datetime between operator', async function (assert) { assert.expect(4); - patchSession({ - getTZOffset: function () { - return -240; - }, - }); class MockedControlPanelModel extends Model { createNewFilters(preFilters) { const preFilter = preFilters[0]; @@ -265,6 +249,11 @@ odoo.define('web.filter_menu_generator_tests', function (require) { props: { fields: this.fields, }, + session: { + getTZOffset() { + return -240; + }, + }, env: { controlPanelModel }, }); diff --git a/addons/web/static/tests/core/dialog_tests.js b/addons/web/static/tests/core/dialog_tests.js index ea3a672322f..c51239a01bb 100644 --- a/addons/web/static/tests/core/dialog_tests.js +++ b/addons/web/static/tests/core/dialog_tests.js @@ -7,10 +7,10 @@ var Widget = require('web.Widget'); var ESCAPE_KEY = $.Event("keyup", { which: 27 }); -function createEmptyParent(debug) { +async function createEmptyParent(debug) { var widget = new Widget(); - testUtils.mock.addMockEnvironment(widget, { + await testUtils.mock.addMockEnvironment(widget, { debug: debug || false, }); return widget; @@ -24,7 +24,7 @@ QUnit.module('core', {}, function () { assert.expect(3); var testPromise = testUtils.makeTestPromiseWithAssert(assert, 'custom callback'); - var parent = createEmptyParent(); + var parent = await createEmptyParent(); new Dialog(parent, { buttons: [ { @@ -54,7 +54,7 @@ QUnit.module('core', {}, function () { assert.expect(3); var testPromise = testUtils.makeTestPromiseWithAssert(assert, 'custom callback'); - var parent = createEmptyParent(); + var parent = await createEmptyParent(); new Dialog(parent, { buttons: [ { @@ -84,7 +84,7 @@ QUnit.module('core', {}, function () { assert.expect(3); var testPromise = testUtils.makeTestPromiseWithAssert(assert, 'confirm callback'); - var parent = createEmptyParent(); + var parent = await createEmptyParent(); var options = { confirm_callback: testPromise.reject, cancel_callback: testPromise.resolve, @@ -107,7 +107,7 @@ QUnit.module('core', {}, function () { assert.expect(3); var testPromise = testUtils.makeTestPromiseWithAssert(assert, 'alert callback'); - var parent = createEmptyParent(); + var parent = await createEmptyParent(); var options = { confirm_callback: testPromise.resolve, }; diff --git a/addons/web/static/tests/core/widget_tests.js b/addons/web/static/tests/core/widget_tests.js index f8283f91c35..1c1c59f1d52 100644 --- a/addons/web/static/tests/core/widget_tests.js +++ b/addons/web/static/tests/core/widget_tests.js @@ -402,7 +402,7 @@ QUnit.module('core', {}, function () { var def; var parent = new Widget(); - testUtils.mock.addMockEnvironment(parent, { + await testUtils.mock.addMockEnvironment(parent, { session: { rpc: function () { def = testUtils.makeTestPromise(); diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js index 856019092fe..98ff6a47925 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js @@ -1060,7 +1060,7 @@ QUnit.module('fields', {}, function () { var fixture = $('#qunit-fixture'); var self = this; - var model = testUtils.createModel({ + var model = await testUtils.createModel({ Model: BasicModel, data: this.data, }); @@ -1083,7 +1083,7 @@ QUnit.module('fields', {}, function () { }); var parent = new StandaloneWidget(model); model.setParent(parent); - testUtils.mock.addMockEnvironment(parent, { + await testUtils.mock.addMockEnvironment(parent, { data: self.data, mockRPC: function (route, args) { assert.step(args.method); diff --git a/addons/web/static/tests/tools/debug_manager_tests.js b/addons/web/static/tests/tools/debug_manager_tests.js index 0f850835c66..77970a1922e 100644 --- a/addons/web/static/tests/tools/debug_manager_tests.js +++ b/addons/web/static/tests/tools/debug_manager_tests.js @@ -11,7 +11,7 @@ QUnit.module('DebugManager', {}, function () { QUnit.test("list: edit view menu item", async function (assert) { assert.expect(3); - var debugManager = createDebugManager(); + var debugManager = await createDebugManager(); await debugManager.appendTo($('#qunit-fixture')); @@ -43,7 +43,7 @@ QUnit.module('DebugManager', {}, function () { QUnit.test("form: Manage Attachments option", async function (assert) { assert.expect(3); - var debugManager = createDebugManager({ + var debugManager = await createDebugManager({ intercepts: { do_action: function (event) { assert.deepEqual(event.data.action, { @@ -126,7 +126,7 @@ QUnit.module('DebugManager', {}, function () { }); // Now the real tested component - var debugManager = createDebugManager({ + var debugManager = await createDebugManager({ data: data, mockRPC: function (route, args) { if (route == "/web/dataset/call_kw/ir.default/set") { diff --git a/addons/web/static/tests/views/basic_model_tests.js b/addons/web/static/tests/views/basic_model_tests.js index 326c8550bb5..3d28a32cf2e 100644 --- a/addons/web/static/tests/views/basic_model_tests.js +++ b/addons/web/static/tests/views/basic_model_tests.js @@ -136,7 +136,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo']; this.params.context = { active_field: 2 }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -171,7 +171,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = 99; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -190,7 +190,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo', 'qux']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -211,7 +211,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.records[1].qux = 1; this.params.fieldNames = ['qux']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -238,7 +238,7 @@ odoo.define('web.basic_model_tests', function (require) { var rpcCount = 0; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -277,7 +277,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo', 'bar']; this.params.context = { hello: 'world' }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -317,7 +317,7 @@ odoo.define('web.basic_model_tests', function (require) { var rpcCount = 0; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -354,7 +354,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -381,7 +381,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.records[1].product_ids = [37]; this.params.fieldNames = ['product_ids']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -461,7 +461,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['product_id']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -495,7 +495,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo', 'product_id']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -524,7 +524,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo', 'product_id']; this.params.res_id = 1; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -553,7 +553,7 @@ odoo.define('web.basic_model_tests', function (require) { }, }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -583,7 +583,7 @@ odoo.define('web.basic_model_tests', function (require) { }, }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -615,7 +615,7 @@ odoo.define('web.basic_model_tests', function (require) { }, }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -641,7 +641,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.context = { active_field: 2 }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -671,7 +671,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.groupedBy = []; this.params.res_id = undefined; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -698,7 +698,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.type = 'record'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -732,7 +732,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.type = 'record'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -784,7 +784,7 @@ odoo.define('web.basic_model_tests', function (require) { }; this.params.viewType = 'form'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -822,7 +822,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.type = 'record'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -848,7 +848,7 @@ odoo.define('web.basic_model_tests', function (require) { fields.product_id.default = 37; fields.other_product_id.default = 41; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -880,7 +880,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -902,7 +902,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['category']; this.params.res_id = 1; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -927,7 +927,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['foo']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -951,7 +951,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['product_id']; var self = this; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -981,7 +981,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; var filterEnabled = false; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1046,7 +1046,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.groupedBy = ['date:month']; this.params.res_id = undefined; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1072,7 +1072,7 @@ odoo.define('web.basic_model_tests', function (require) { }; this.data.partner.records[0].selection = 'a'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1108,7 +1108,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.context = { active_field: 2 }; var id; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1146,7 +1146,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['product_ids']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1183,7 +1183,7 @@ odoo.define('web.basic_model_tests', function (require) { QUnit.test('create commands on a one2many', async function (assert) { assert.expect(3); - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1260,7 +1260,7 @@ odoo.define('web.basic_model_tests', function (require) { type: 'record', }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1297,7 +1297,7 @@ odoo.define('web.basic_model_tests', function (require) { QUnit.test('dates are properly loaded and parsed (record)', async function (assert) { assert.expect(2); - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1328,7 +1328,7 @@ odoo.define('web.basic_model_tests', function (require) { QUnit.test('dates are properly loaded and parsed (list)', async function (assert) { assert.expect(2); - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1355,7 +1355,7 @@ odoo.define('web.basic_model_tests', function (require) { QUnit.test('dates are properly loaded and parsed (default_get)', async function (assert) { assert.expect(1); - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1379,7 +1379,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.fields.category.default = [12, 14]; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1412,7 +1412,7 @@ odoo.define('web.basic_model_tests', function (require) { }; this.data.partner.fields.category.type = 'many2one'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1464,7 +1464,7 @@ odoo.define('web.basic_model_tests', function (require) { string: "O2M", type: 'one2many', relation: 'partner', default: [[6, 0, [1]]], }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1510,7 +1510,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.fields.product_id.context = "{'hello': 'world', 'test': foo}"; this.data.partner.fields.product_id.domain = "[['hello', 'like', 'world'], ['test', 'like', foo]]"; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1538,7 +1538,7 @@ odoo.define('web.basic_model_tests', function (require) { model.destroy(); // Try again with xml override of field domain and context - model = createModel({ + model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1585,7 +1585,7 @@ odoo.define('web.basic_model_tests', function (require) { } }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1650,7 +1650,7 @@ odoo.define('web.basic_model_tests', function (require) { } }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1699,7 +1699,7 @@ odoo.define('web.basic_model_tests', function (require) { QUnit.test('default_get with one2many values', async function (assert) { assert.expect(1); - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1738,7 +1738,7 @@ odoo.define('web.basic_model_tests', function (require) { assert.expect(3); var rpcCount = 0; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1773,7 +1773,7 @@ odoo.define('web.basic_model_tests', function (require) { assert.expect(5); var rpcCount = 0; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1812,7 +1812,7 @@ odoo.define('web.basic_model_tests', function (require) { assert.expect(5); var rpcCount = 0; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1857,7 +1857,7 @@ odoo.define('web.basic_model_tests', function (require) { assert.expect(4); var rpcCount = 0; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1888,7 +1888,7 @@ odoo.define('web.basic_model_tests', function (require) { assert.expect(2); let rpcCount = 0; - const model = createModel({ + const model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1916,7 +1916,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.fields.product_id.context = "{'id': id, 'active_id': active_id, 'active_ids': active_ids, 'active_model': active_model}"; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -1949,7 +1949,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = ['category']; this.params.res_id = 1; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -1991,7 +1991,7 @@ odoo.define('web.basic_model_tests', function (require) { viewType: 'form', }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2011,7 +2011,7 @@ odoo.define('web.basic_model_tests', function (require) { assert.expect(2); this.data.partner.fields.foo.default = 'default'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2042,7 +2042,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.fieldNames = Object.keys(this.data.partner.fields); this.params.res_id = 1; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2095,7 +2095,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.viewType = 'form'; this.params.res_id = 1; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2133,7 +2133,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.groupedBy = []; this.params.res_id = undefined; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -2164,7 +2164,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.type = 'record'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route, args) { @@ -2196,7 +2196,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.type = 'record'; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2215,7 +2215,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = undefined; this.params.orderedBy = [{ name: 'display_name', asc: true }]; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, mockRPC: function (route) { @@ -2269,7 +2269,7 @@ odoo.define('web.basic_model_tests', function (require) { this.params.res_id = 1; this.params.fieldNames = ['foobool', 'foobool2']; this.params.fields = this.data.partner.fields; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2291,7 +2291,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.records[1].product_ids = [37, 38]; this.params.fieldNames = ['product_ids']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2341,7 +2341,7 @@ odoo.define('web.basic_model_tests', function (require) { this.data.partner.records[1].product_ids = [37, 38]; this.params.fieldNames = ['product_ids']; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2401,7 +2401,7 @@ odoo.define('web.basic_model_tests', function (require) { }, }; - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); @@ -2448,7 +2448,7 @@ odoo.define('web.basic_model_tests', function (require) { QUnit.test('identify correct active field', async function(assert) { assert.expect(4); - var model = createModel({ + var model = await createModel({ Model: BasicModel, data: this.data, }); diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 7b69e8ce167..ba8c1f35dfd 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -2541,7 +2541,7 @@ QUnit.module('Views', { }); QUnit.test('check if the view destroys all widgets and instances', async function (assert) { - assert.expect(1); + assert.expect(2); var instanceNumber = 0; testUtils.mock.patch(mixins.ParentedMixin, { @@ -2579,21 +2579,10 @@ QUnit.module('Views', { }; var calendar = await createCalendarView(params); - calendar.destroy(); - - var initialInstanceNumber = instanceNumber; - instanceNumber = 0; - - calendar = await createCalendarView(params); - - // call destroy function of controller to ensure that it correctly destroys everything - calendar.__destroy(); - - // + 1 (parent) - assert.strictEqual(instanceNumber, initialInstanceNumber + 1, - "every widget must be destroyed exept the parent"); + assert.ok(instanceNumber > 0); calendar.destroy(); + assert.strictEqual(instanceNumber, 0); testUtils.mock.unpatch(mixins.ParentedMixin); }); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 33bc3de7347..56fba6515bc 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -7706,7 +7706,7 @@ QUnit.module('Views', { }); QUnit.test('check if the view destroys all widgets and instances', async function (assert) { - assert.expect(1); + assert.expect(2); var instanceNumber = 0; await testUtils.mock.patch(mixins.ParentedMixin, { @@ -7766,21 +7766,10 @@ QUnit.module('Views', { }; var form = await createView(params); - form.destroy(); - - var initialInstanceNumber = instanceNumber; - instanceNumber = 0; - - form = await createView(params); - - // call destroy function of controller to ensure that it correctly destroys everything - form.__destroy(); - - // + 4 (parent) - assert.strictEqual(instanceNumber, initialInstanceNumber + 1, - "every widget must be destroyed exept the parent"); + assert.ok(instanceNumber > 0); form.destroy(); + assert.strictEqual(instanceNumber, 0); await testUtils.mock.unpatch(mixins.ParentedMixin); }); diff --git a/addons/web/static/tests/views/kanban_model_tests.js b/addons/web/static/tests/views/kanban_model_tests.js index 793a5749b46..2598f2bd676 100644 --- a/addons/web/static/tests/views/kanban_model_tests.js +++ b/addons/web/static/tests/views/kanban_model_tests.js @@ -67,7 +67,7 @@ QUnit.module('Views', { assert.expect(22); var calledRoutes = {}; - var model = createModel({ + var model = await createModel({ Model: KanbanModel, data: this.data, mockRPC: function (route) { @@ -132,7 +132,7 @@ QUnit.module('Views', { var done = assert.async(); assert.expect(4); - var model = createModel({ + var model = await createModel({ Model: KanbanModel, data: this.data, mockRPC: function (route, args) { @@ -173,7 +173,7 @@ QUnit.module('Views', { var done = assert.async(); assert.expect(2); - var model = createModel({ + var model = await createModel({ Model: KanbanModel, data: this.data, mockRPC: function (route, args) { @@ -210,7 +210,7 @@ QUnit.module('Views', { this.data.partner.records.push({id: 3, foo: 'aaa', product_id: 37}); var nbReseq = 0; - var model = createModel({ + var model = await createModel({ Model: KanbanModel, data: this.data, mockRPC: function (route, args) { @@ -270,7 +270,7 @@ QUnit.module('Views', { assert.expect(8); var self = this; - var model = createModel({ + var model = await createModel({ Model: KanbanModel, data: this.data, }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 22c29168585..225826c8515 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -5420,7 +5420,7 @@ QUnit.module('Views', { }); QUnit.test('check if the view destroys all widgets and instances', async function (assert) { - assert.expect(1); + assert.expect(2); var instanceNumber = 0; testUtils.mock.patch(mixins.ParentedMixin, { @@ -5457,21 +5457,10 @@ QUnit.module('Views', { }; var kanban = await createView(params); - kanban.destroy(); - - var initialInstanceNumber = instanceNumber; - instanceNumber = 0; - - kanban = await createView(params); - - // call destroy function of controller to ensure that it correctly destroys everything - kanban.__destroy(); - - // + 1 (parent) - assert.strictEqual(instanceNumber, initialInstanceNumber + 1, - "every widget must be destroyed exept the parent"); + assert.ok(instanceNumber > 0); kanban.destroy(); + assert.strictEqual(instanceNumber, 0); testUtils.mock.unpatch(mixins.ParentedMixin); }); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index d69aef904c1..67c1a7962a0 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -7409,7 +7409,7 @@ QUnit.module('Views', { }); QUnit.test('check if the view destroys all widgets and instances', async function (assert) { - assert.expect(1); + assert.expect(2); var instanceNumber = 0; testUtils.mock.patch(mixins.ParentedMixin, { @@ -7446,21 +7446,10 @@ QUnit.module('Views', { }; var list = await createView(params); - list.destroy(); - - var initialInstanceNumber = instanceNumber; - instanceNumber = 0; - - list = await createView(params); - - // call destroy function of controller to ensure that it correctly destroys everything - list.__destroy(); - - // + 1 (parent) - assert.strictEqual(instanceNumber, initialInstanceNumber + 1, - "every widget must be destroyed exept the parent"); + assert.ok(instanceNumber > 0); list.destroy(); + assert.strictEqual(instanceNumber, 0); testUtils.mock.unpatch(mixins.ParentedMixin); }); diff --git a/addons/web/static/tests/widgets/company_switcher_tests.js b/addons/web/static/tests/widgets/company_switcher_tests.js index 6b3ead230ca..fe0fec5e6c2 100644 --- a/addons/web/static/tests/widgets/company_switcher_tests.js +++ b/addons/web/static/tests/widgets/company_switcher_tests.js @@ -9,7 +9,7 @@ async function createSwitchCompanyMenu(params) { params = params || {}; var target = params.debug ? document.body : $('#qunit-fixture'); var menu = new SwitchCompanyMenu(); - testUtils.mock.addMockEnvironment(menu, params); + await testUtils.mock.addMockEnvironment(menu, params); await menu.appendTo(target) return menu } diff --git a/addons/web/static/tests/widgets/domain_selector_tests.js b/addons/web/static/tests/widgets/domain_selector_tests.js index 8c246fd8399..a587117f95a 100644 --- a/addons/web/static/tests/widgets/domain_selector_tests.js +++ b/addons/web/static/tests/widgets/domain_selector_tests.js @@ -60,7 +60,7 @@ QUnit.module('DomainSelector', { readonly: false, debugMode: true, }); - testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); await domainSelector.appendTo($target); // As we gave an empty domain, there should be a visible button to add @@ -161,7 +161,7 @@ QUnit.module('DomainSelector', { var domainSelector = new DomainSelector(null, "partner", [["nice_datetime", "=", "2017-03-27 15:42:00"]], { readonly: false, }); - testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); await domainSelector.appendTo($target); // Check that there is a datepicker to choose the date @@ -189,7 +189,7 @@ QUnit.module('DomainSelector', { debugMode: true, readonly: false, }); - testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); await domainSelector.appendTo($target); await testUtils.fields.editAndTrigger(domainSelector.$('.o_domain_leaf_value_input'), @@ -210,7 +210,7 @@ QUnit.module('DomainSelector', { debugMode: true, readonly: false, }); - testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); await domainSelector.appendTo($target); assert.strictEqual(domainSelector.$el.text(), "This domain is not supported.", @@ -230,7 +230,7 @@ QUnit.module('DomainSelector', { debugMode: true, default: [["foo","=","kikou"]], }); - testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(domainSelector, {data: this.data}); await domainSelector.appendTo($target); // Clicking on the button should add a visible field selector in the diff --git a/addons/web/static/tests/widgets/model_field_selector_tests.js b/addons/web/static/tests/widgets/model_field_selector_tests.js index 75580e09df3..69bb1eb9314 100644 --- a/addons/web/static/tests/widgets/model_field_selector_tests.js +++ b/addons/web/static/tests/widgets/model_field_selector_tests.js @@ -59,7 +59,7 @@ QUnit.module('ModelFieldSelector', { readonly: false, debugMode: true, }); - testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); await fieldSelector.appendTo($target); var $value = fieldSelector.$("> .o_field_selector_value"); @@ -182,7 +182,7 @@ QUnit.module('ModelFieldSelector', { readonly: false, debugMode: true, }); - testUtils.addMockEnvironment(fieldSelector, {data: this.data}); + await testUtils.addMockEnvironment(fieldSelector, {data: this.data}); await fieldSelector.appendTo($target); // Focusing the field selector input should open a field selector popover @@ -215,7 +215,7 @@ QUnit.module('ModelFieldSelector', { return field.type === 'many2one'; }, }); - testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); await fieldSelector.appendTo($target); fieldSelector.$el.trigger('focusin'); @@ -237,7 +237,7 @@ QUnit.module('ModelFieldSelector', { var fieldSelector = new ModelFieldSelector(null, "partner", [], { readonly: false, }); - testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); await fieldSelector.appendTo($target); fieldSelector.$el.trigger('focusin'); @@ -269,7 +269,7 @@ QUnit.module('ModelFieldSelector', { readonly: false, showSearchInput: false, }); - testUtils.mock.addMockEnvironment(fieldSelector, { data: this.data }); + await testUtils.mock.addMockEnvironment(fieldSelector, { data: this.data }); await fieldSelector.appendTo($target); fieldSelector.$el.trigger('focusin'); @@ -291,7 +291,7 @@ QUnit.module('ModelFieldSelector', { readonly: false, showSearchInput: false, }); - testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); await fieldSelector.appendTo($target); var $fieldName = fieldSelector.$('.o_field_selector_chain_part'); @@ -311,7 +311,7 @@ QUnit.module('ModelFieldSelector', { readonly: false, showSearchInput: false, }); - testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); + await testUtils.mock.addMockEnvironment(fieldSelector, {data: this.data}); await fieldSelector.appendTo($target); var $fieldName = fieldSelector.$('.o_field_selector_chain_part'); diff --git a/addons/web_editor/static/tests/test_utils.js b/addons/web_editor/static/tests/test_utils.js index 8b28e292438..9cd3249f953 100644 --- a/addons/web_editor/static/tests/test_utils.js +++ b/addons/web_editor/static/tests/test_utils.js @@ -293,12 +293,12 @@ function wysiwygData(data) { * * @param {object} params */ -function createWysiwyg(params) { +async function createWysiwyg(params) { patch(); params.data = wysiwygData(params.data); var parent = new Widget(); - testUtils.mock.addMockEnvironment(parent, params); + await testUtils.mock.addMockEnvironment(parent, params); var wysiwygOptions = _.extend({}, params.wysiwygOptions, { recordInfo: { diff --git a/addons/web_tour/static/tests/tour_manager_tests.js b/addons/web_tour/static/tests/tour_manager_tests.js index 79ce4c0ceb5..14802f9086f 100644 --- a/addons/web_tour/static/tests/tour_manager_tests.js +++ b/addons/web_tour/static/tests/tour_manager_tests.js @@ -20,7 +20,7 @@ odoo.define('web_tour.tour_manager_tests', async function (require) { * @param {Object[]} params.tours { {string} name, {Object} option, {Object[]} steps } */ async function createTourManager({ consumed_tours, debug, template, tours }) { - const parent = testUtils.createParent({ debug }); + const parent = await testUtils.createParent({ debug }); const tourManager = new TourManager(parent, consumed_tours); tourManager.running_step_delay = 0; for (const { name, options, steps } of tours) {