benjarwar
benjarwar

Reputation: 1804

Executing callback from a CucumberJS with Selenium step definition

I'm trying out CucumberJS with Selenium and PhantomJS. I've successfully built a World object using this StackOverflow answer as a guide.

So now I'm testing out some basic step definitions, but having some confusion about how to execute the callback at the end of the step. This works great:

module.exports = function () {
  this.World = require("../support/world.js").World;

  this.Given(/^I am visiting Google$/, function (callback) {
    this.driver.get('http://www.google.com')
        .then(function() {
          callback();
        });
  });
};

The driver hits Google.com and the callback isn't fired until after the requested document is loaded. But I find this syntax to be a little wordy, so I thought maybe I could just pass callback straight to the then() after my first promise, like so:

module.exports = function () {
  this.World = require("../support/world.js").World;

  this.Given(/^I am visiting Google$/, function (callback) {
    this.driver.get('http://www.google.com')
        .then(callback);
  });
};

This, however fails, and seems to console.log the callback. Here's the output:

  Scenario: Googling           # features/theGoogle.feature:6
    Given I am visiting Google # features/theGoogle.feature:7
      [object Object]


    (::) failed steps (::)

    [object Object]

What's going on here? I was expecting that callback could simply be passed to the then() function and executed after the promise is fulfilled. Why would wrapping it in an anonymous function make it work?

Upvotes: 1

Views: 2499

Answers (1)

floribon
floribon

Reputation: 19183

What is happening is that callback is being called with the arguments of driver.get().then.

In other words, here is what happens:

this.driver.get('http://www.google.com')
  .then(function(result) {
    callback(result);
  });

The problem is that cucumber's callback will consider a fail if the callback is called with something as its first parameter, since it is supposed to be an error as in callback(new Error('Something went wrong')).

To me this was enough to ban the use of callbacks completely. Selenium is fully oriented promise, and you should keep with promise only if you want your life to be easier. This is perfect since cucumber.js accepts promises to be returned instead of a callback, so here is the best way to do things:

// Be sure to omit the last parameter of the function, usually "callback"
this.Given(/^I am visiting Google$/, function () {
  return this.driver.get('http://www.google.com');
});

The step will fail if the promise is eventually rejected, or keep on to the next step if the promise is fullfiled. But in both cases, cucumber will wait for the last promise, so all you need to do is always return the very last promise of any step, since Selenium will only resolve/reject the last promise after the previous ones have been resolved. Everything looks much nicer doesn't it?

Upvotes: 1

Related Questions