diff options
Diffstat (limited to 'testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm')
-rw-r--r-- | testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm b/testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm new file mode 100644 index 000000000..27da0de22 --- /dev/null +++ b/testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm @@ -0,0 +1,120 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This module implements a number of utility functions that can be loaded + * into content scope. + * + * All asynchronous helper methods should return promises, rather than being + * callback based. + */ + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "ContentTaskUtils", +]; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/Timer.jsm"); + +this.ContentTaskUtils = { + /** + * Will poll a condition function until it returns true. + * + * @param condition + * A condition function that must return true or false. If the + * condition ever throws, this is also treated as a false. + * @param interval + * The time interval to poll the condition function. Defaults + * to 100ms. + * @param attempts + * The number of times to poll before giving up and rejecting + * if the condition has not yet returned true. Defaults to 50 + * (~5 seconds for 100ms intervals) + * @return Promise + * Resolves when condition is true. + * Rejects if timeout is exceeded or condition ever throws. + */ + waitForCondition(condition, msg, interval=100, maxTries=50) { + return new Promise((resolve, reject) => { + let tries = 0; + let intervalID = setInterval(() => { + if (tries >= maxTries) { + clearInterval(intervalID); + msg += ` - timed out after ${maxTries} tries.`; + reject(msg); + return; + } + + let conditionPassed = false; + try { + conditionPassed = condition(); + } catch(e) { + msg += ` - threw exception: ${e}`; + clearInterval(intervalID); + reject(msg); + return; + } + + if (conditionPassed) { + clearInterval(intervalID); + resolve(); + } + tries++; + }, interval); + }); + }, + + /** + * Waits for an event to be fired on a specified element. + * + * Usage: + * let promiseEvent = ContentTasKUtils.waitForEvent(element, "eventName"); + * // Do some processing here that will cause the event to be fired + * // ... + * // Now yield until the Promise is fulfilled + * let receivedEvent = yield promiseEvent; + * + * @param {Element} subject + * The element that should receive the event. + * @param {string} eventName + * Name of the event to listen to. + * @param {bool} capture [optional] + * True to use a capturing listener. + * @param {function} checkFn [optional] + * Called with the Event object as argument, should return true if the + * event is the expected one, or false if it should be ignored and + * listening should continue. If not specified, the first event with + * the specified name resolves the returned promise. + * + * @note Because this function is intended for testing, any error in checkFn + * will cause the returned promise to be rejected instead of waiting for + * the next event, since this is probably a bug in the test. + * + * @returns {Promise} + * @resolves The Event object. + */ + waitForEvent(subject, eventName, capture, checkFn, wantsUntrusted = false) { + return new Promise((resolve, reject) => { + subject.addEventListener(eventName, function listener(event) { + try { + if (checkFn && !checkFn(event)) { + return; + } + subject.removeEventListener(eventName, listener, capture); + resolve(event); + } catch (ex) { + try { + subject.removeEventListener(eventName, listener, capture); + } catch (ex2) { + // Maybe the provided object does not support removeEventListener. + } + reject(ex); + } + }, capture, wantsUntrusted); + }); + }, +}; |