Expose a generic wait loop method that takes a method that returns a promise
:+1:
What's a block?
In this case, it's a predicate (Boolean-returning) function that is polled until it returns true.
A block is essentially a function in javascript. It's a reference to an executable item.
Even more simply, it's a callback.
So why not call it a callback?
It's not strictly a callback as it's not for notification, but people understand the notion of a callback. It's really a predicate function.
I guess the block term comes from another language (probably ruby in this case) that uses them for waiting logic.
@samccone not sure if I'm in the same wavelength as you here, but I've seen this done in ruby selenium tests before.
Yes a callback is the correct javascript term here. @tomhicks you are spot on, thanks for the response :)
for example
this.waitUntil(function() {
return x.read('zap');
}, function(result){
return result.length > 4 //can return a promise also
},
<optional error message>,
<optional global timeout override in ms>)
or perhaps another syntax would be
this.waitUntil({
evaluator: function(){}
exitCondition: function(){}
timeout: <int>
errorMessage: <string or func>
})
the result of the method would be a promise so it is thenable
MyWidget = this.Widget.extend({
root: '#whatever',
isReady: function () {
return !this.el.hasClass('fetching-data'); // whatever the real API is
}
});
// In a step
var instance = new MyWidget();
return instance.find().then(function (resolvedValue) {
return this.waitUntil(instance.isReady.bind(instance), {
timeout: 10000,
someOtherOption: 'blah'
}).then(function () {
return resolvedValue;
});
}.bind(this))
So rather than an evaluation function that returns a result that is then checked for exit-worthiness by another function, we just pass a predicate that answers the 'can I stop waiting now?' question.
waitUntil could look something like this:
waitUntil = function (predicate, options) {
deferred = new Deferred();
// assuming predicate does not return a promise
everyXMillisecondsUntilTimeout(function checkPredicate () {
if (predicate()) {
deferred.resolve();
});
}, function timeoutElapsed () {
deferred.reject(new Error(options.errorMessage));
});
return deferred.promise;
}
There's some issues in passing on the previously-resolved value after waitUntil has resolved in the way I've written it. I'm sure there's a more elegant way of expressing this.
@samccone is there any particular reason to do it as you suggested, with evaluator and an exitCondition functions? I think we can get roughly the same behaviour with a simple predicate.
I would also be good to be able to do the waiting inside interaction methods:
var MyWidget = this.Widget.extend({
save: function () {
return this.click('save')
.then(function (saveClickResolution) {
return Something.waitUntil(this.hasSaveFinished.bind(this));
}.bind(this))
.then(function () { return saveClickResolution; })
}
});
I'm pretty sure I'm being heavy-handed in the promise resolution, but the idea is the save method returns only when the saving has completely finished, with "completely finished" being defined by MyWidget::hasSaveFinished()
Hang on a sec, isn't this available already via widget.getDriver()? http://selenium.googlecode.com/git/docs/api/javascript/source/lib/webdriver/webdriver.js.src.html#l588
Been working through a few failures lately and my current thinking is to add something like
foo.isVisible().until(true, <timeout>).then(...)
foo.isVisible().until(function(){return <Promise>}).then(...)
foo.isVisible().untilDoesNotThow( <timeout>).then(...)
what do you think @tomhicks