pioneer icon indicating copy to clipboard operation
pioneer copied to clipboard

Expose a generic wait loop method that takes a method that returns a promise

Open samccone opened this issue 11 years ago • 11 comments

samccone avatar Sep 04 '14 15:09 samccone

:+1:

tomhicks avatar Oct 13 '14 20:10 tomhicks

What's a block?

wkf avatar Oct 15 '14 20:10 wkf

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.

tomhicks avatar Oct 15 '14 20:10 tomhicks

So why not call it a callback?

wkf avatar Oct 15 '14 20:10 wkf

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.

tomhicks avatar Oct 15 '14 20:10 tomhicks

Yes a callback is the correct javascript term here. @tomhicks you are spot on, thanks for the response :)

samccone avatar Oct 15 '14 20:10 samccone

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

samccone avatar Oct 15 '14 21:10 samccone

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.

tomhicks avatar Oct 15 '14 21:10 tomhicks

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()

tomhicks avatar Oct 15 '14 21:10 tomhicks

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

tomhicks avatar Oct 15 '14 23:10 tomhicks

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

samccone avatar Oct 22 '14 19:10 samccone