/**
* @module leadfoot/helpers/pollUntil
*/
var util = require('../lib/util');
/**
* A {@link module:leadfoot/Command} helper that polls for a value within the client environment until the value exists
* or a timeout is reached.
*
* @param {Function|string} poller
* The poller function to execute on an interval. The function should return `null` or `undefined` if there is not a
* result. If the poller function throws, polling will halt.
*
* @param {Array.<any>=} args
* An array of arguments to pass to the poller function when it is invoked. Only values that can be serialised to JSON,
* plus {@link module:leadfoot/Element} objects, can be specified as arguments.
*
* @param {number=} timeout
* The maximum amount of time to wait for a successful result, in milliseconds. If not specified, the current
* `executeAsync` maximum timeout for the session will be used.
*
* @param {number=} pollInterval
* The amount of time to wait between calls to the poller function, in milliseconds. If not specified, defaults to 67ms.
*
* @returns {function(): Promise.<any>}
* A {@link module:leadfoot/Command#then} callback function that, when called, returns a promise that resolves to the
* value returned by the poller function on success and rejects on failure.
*
* @example
* var Command = require('leadfoot/Command');
* var pollUntil = require('leadfoot/helpers/pollUntil');
*
* new Command(session)
* .get('http://example.com')
* .then(pollUntil('return document.getElementById("a");', 1000))
* .then(function (elementA) {
* // element was found
* }, function (error) {
* // element was not found
* });
*
* @example
* var Command = require('leadfoot/Command');
* var pollUntil = require('leadfoot/helpers/pollUntil');
*
* new Command(session)
* .get('http://example.com')
* .then(pollUntil(function (value) {
* var element = document.getElementById('a');
* return element && element.value === value ? true : null;
* }, [ 'foo' ], 1000))
* .then(function () {
* // value was set to 'foo'
* }, function (error) {
* // value was never set
* });
*/
module.exports = function (poller, args, timeout, pollInterval) {
if (typeof args === 'number') {
pollInterval = timeout;
timeout = args;
args = [];
}
args = args || [];
pollInterval = pollInterval || 67;
return function () {
var session = this.session;
var originalTimeout;
return session.getExecuteAsyncTimeout().then(function () {
function storeResult(result) {
resultOrError = result;
}
if (!isNaN(timeout)) {
originalTimeout = arguments[0];
}
else {
timeout = arguments[0];
}
var resultOrError;
return session.setExecuteAsyncTimeout(timeout)
.then(function () {
/* jshint maxlen:140 */
return session.executeAsync(/* istanbul ignore next */ function (poller, args, timeout, pollInterval, done) {
/* jshint evil:true */
poller = new Function(poller);
var endTime = Number(new Date()) + timeout;
(function poll() {
var result = poller.apply(this, args);
/*jshint evil:true */
if (result != null) {
done(result);
}
else if (Number(new Date()) < endTime) {
setTimeout(poll, pollInterval);
}
else {
done(null);
}
})();
}, [ util.toExecuteString(poller), args, timeout, pollInterval ]);
})
.then(storeResult, storeResult)
.finally(function () {
function finish() {
if (resultOrError instanceof Error) {
throw resultOrError;
}
if (resultOrError === null) {
var error = new Error('Polling timed out with no result');
error.name = 'ScriptTimeout';
throw error;
}
return resultOrError;
}
if (!isNaN(originalTimeout)) {
return session.setExecuteAsyncTimeout(originalTimeout).then(finish);
}
return finish();
});
});
};
};