diff options
| author | Sébastien Theys <seb@odoo.com> | 2020-09-07 11:06:36 +0000 |
|---|---|---|
| committer | Sébastien Theys <seb@odoo.com> | 2020-09-07 12:28:36 +0000 |
| commit | 0e13ad22267e928a50fdc7d4f045f03b49385aa9 (patch) | |
| tree | 3b7a3684ad2d0c209f911ea47e31c602209fc41b /addons/mail/static/src/utils | |
| parent | 3783654b87851bdeb11e32da78bb5b62865b869a (diff) | |
[FIX] mail: allow tests to await specific events
task-2333915
closes odoo/odoo#57185
X-original-commit: 699106ce65a87f7a24f04e1d8f232b5a6b4e210a
Related: odoo/enterprise#13013
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Diffstat (limited to 'addons/mail/static/src/utils')
| -rw-r--r-- | addons/mail/static/src/utils/test_utils.js | 126 |
1 files changed, 97 insertions, 29 deletions
diff --git a/addons/mail/static/src/utils/test_utils.js b/addons/mail/static/src/utils/test_utils.js index 0513eb37672..be15afe751d 100644 --- a/addons/mail/static/src/utils/test_utils.js +++ b/addons/mail/static/src/utils/test_utils.js @@ -321,6 +321,12 @@ function beforeEach(self) { } function afterEach(self) { + if (self.env) { + self.env.bus.off('hide_home_menu', null); + self.env.bus.off('show_home_menu', null); + self.env.bus.off('will_hide_home_menu', null); + self.env.bus.off('will_show_home_menu', null); + } // The components must be destroyed before the widget, because the // widget might destroy the models before destroying the components, // and the components might still rely on messaging (or other) record(s). @@ -404,6 +410,11 @@ async function createRootComponent(self, Component, { props = {}, target }) { * the View class to use in createView. * @param {Object} [param0.viewOptions] makes only sense when `param0.hasView` * is set: the view options to use in createView. + * @param {Object} [param0.waitUntilEvent] + * @param {String} [param0.waitUntilEvent.eventName] + * @param {String} [param0.waitUntilEvent.message] + * @param {function} [param0.waitUntilEvent.predicate] + * @param {integer} [param0.waitUntilEvent.timeoutDelay] * @param {string} [param0.waitUntilMessagingCondition='initialized'] Determines * the condition of messaging when this function is resolved. * Supported values: ['none', 'created', 'initialized']. @@ -438,6 +449,7 @@ async function start(param0 = {}) { hasTimeControl = false, hasView = false, messagingBeforeCreationDeferred = Promise.resolve(), + waitUntilEvent, waitUntilMessagingCondition = 'initialized', } = param0; if (!['none', 'created', 'initialized'].includes(waitUntilMessagingCondition)) { @@ -594,39 +606,95 @@ async function start(param0 = {}) { () => testEnv.messagingBus.trigger('will_show_home_menu') ); - const result = { env: testEnv, mockServer, widget }; - - messagingBeforeCreationDeferred.then(async () => { - /** - * Some models require session data, like locale text direction - * (depends on fully loaded translation). - */ - await env.session.is_bound; - - testEnv.modelManager = new ModelManager(testEnv); - testEnv.modelManager.start(); - /** - * Create the messaging singleton record. - */ - testEnv.messaging = testEnv.models['mail.messaging'].create(); - testEnv.messaging.start().then(() => - testEnv.messagingInitializedDeferred.resolve() - ); - testEnv.messagingCreatedPromise.resolve(); + /** + * Returns a promise resolved after the expected event is received. + * + * @param {Object} param0 + * @param {string} param0.eventName event to wait + * @param {function} param0.func function which, when called, is expected to + * trigger the event + * @param {string} [param0.message] assertion message + * @param {function} [param0.predicate] predicate called with event data. + * If not provided, only the event name has to match. + * @param {number} [param0.timeoutDelay=5000] how long to wait at most in ms + * @returns {Promise} + */ + const afterEvent = (async ({ eventName, func, message, predicate, timeoutDelay = 5000 }) => { + // Set up the timeout to reject if the event is not triggered. + let timeoutNoEvent; + const timeoutProm = new Promise((resolve, reject) => { + timeoutNoEvent = setTimeout(() => { + let error = message + ? new Error(message) + : new Error(`Timeout: the event ${eventName} was not triggered.`); + console.error(error); + reject(error); + }, timeoutDelay); + }); + // Set up the promise to resolve if the event is triggered. + const eventProm = new Promise(resolve => { + testEnv.messagingBus.on(eventName, null, data => { + if (!predicate || predicate(data)) { + resolve(); + } + }); + }); + // Start the function expected to trigger the event after the + // promise has been registered to not miss any potential event. + const funcRes = func(); + // Make them race (first to resolve/reject wins). + await Promise.race([eventProm, timeoutProm]); + clearTimeout(timeoutNoEvent); + // If the event is triggered before the end of the async function, + // ensure the function finishes its job before returning. + await funcRes; }); - if (waitUntilMessagingCondition === 'created') { - await testEnv.messagingCreatedPromise; - } - if (waitUntilMessagingCondition === 'initialized') { - await testEnv.messagingInitializedDeferred; - } - if (mountCallbacks.length > 0) { - await afterNextRender(async () => { - await Promise.all(mountCallbacks.map(callback => callback({ selector, widget }))); + const result = { + afterEvent, + env: testEnv, + mockServer, + widget, + }; + + const start = async () => { + messagingBeforeCreationDeferred.then(async () => { + /** + * Some models require session data, like locale text direction + * (depends on fully loaded translation). + */ + await env.session.is_bound; + + testEnv.modelManager = new ModelManager(testEnv); + testEnv.modelManager.start(); + /** + * Create the messaging singleton record. + */ + testEnv.messaging = testEnv.models['mail.messaging'].create(); + testEnv.messaging.start().then(() => + testEnv.messagingInitializedDeferred.resolve() + ); + testEnv.messagingCreatedPromise.resolve(); }); + if (waitUntilMessagingCondition === 'created') { + await testEnv.messagingCreatedPromise; + } + if (waitUntilMessagingCondition === 'initialized') { + await testEnv.messagingInitializedDeferred; + } + + if (mountCallbacks.length > 0) { + await afterNextRender(async () => { + await Promise.all(mountCallbacks.map(callback => callback({ selector, widget }))); + }); + } + returnCallbacks.forEach(callback => callback(result)); + }; + if (waitUntilEvent) { + await afterEvent(Object.assign({ func: start }, waitUntilEvent)); + } else { + await start(); } - returnCallbacks.forEach(callback => callback(result)); return result; } |
