summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/utils
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-09-07 11:06:36 +0000
committerSébastien Theys <seb@odoo.com>2020-09-07 12:28:36 +0000
commit0e13ad22267e928a50fdc7d4f045f03b49385aa9 (patch)
tree3b7a3684ad2d0c209f911ea47e31c602209fc41b /addons/mail/static/src/utils
parent3783654b87851bdeb11e32da78bb5b62865b869a (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.js126
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;
}