Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cucumber JS timeout won't work

Using the protractor-cucumber-framework, I'm trying to click a button a hundred times in one When-step. However, doing this would result in a timeout with the default 5000ms timeout value. I'd rather not change this default using:

var config = function() {
    this.setDefaultTimeout(60*1000);
};

module.exports = config;

This works, but I would rather set the timeout for that single step like so:

this.When(/^I click on the "([^"]*)" button$/, {timeout: 60*1000}, function(text, callback)
{
    // Click the button 100 times
    var button = element(by.partialButtonText('Widget'));
    for(j = 0; j < i; j++) {
        button.click();
    }
    callback();
});

According to the cucumber-js readme this should work, but still results in:

Error: Step timed out after 5000 milliseconds
    at Timer.listOnTimeout (timer.js:92:15)

Any ideas on why this doesn't work?

EDIT: It did work. However, I was using it in the wrong step. Calling click() a hundred times doesn't take so long. It times out on the step after it:

this.Then(/^a new widget is created$/, {timeout: 60 * 1000}, function(callback) {
    // Check if 100 widgets are created
});

Can anyone explain now why the long timeout is necessary in the step after all the calls to click? Is there a more elegant way to have cucumber wait for the buttonclicks to finish?

like image 313
Jusser Avatar asked Mar 28 '16 13:03

Jusser


1 Answers

The reason why the timeout doesn't work for you as expected is that your callback() is fired early, even before the completion of the first click(). This is because the click()s are asynchronous and are added to protractor's controlFlow (they get queued to be fired one after the other), but the callback() is not. There are a couple of options to avoid this.

Option #1

You should make sure that the callback() is only fired once all the promises returned by click()s are fulfilled.

You can collect all the promises returned and call your callback() once all of them are resolved. This is what you would want to do if you did not know of the controlFlow:

this.When(/^I click on the "([^"]*)" button$/, {timeout: 60*1000}, function(text, callback)
{
    // Click the button 100 times
    var button = element(by.partialButtonText('Widget'));
    var promises = [];

    for (var i = 0; i < 101; i++) {
        promises.push(button.click());
    }

    protractor.promise.all(promises).then(callback);
});

Option #2

But things can get much easier if you just stash your callback() onto the controlFlow queue like this:

this.When(/^I click on the "([^"]*)" button$/, {timeout: 60*1000}, function(text, callback)
{
    // Click the button 100 times
    var button = element(by.partialButtonText('Widget'));
    for(var i = 0; i < 101; i++) {
        button.click();
    }

    browser.controlFlow().execute(callback);
});

As you can see, when working with Protractor you should make use of its controlFlow to avoid writing asynchronous (looking) code.

like image 92
The Victor Avatar answered Sep 27 '22 21:09

The Victor