I'm working on a rails project that uses an acceptance test setup consisting of Cucumber and Poltergeist. The project is relying on some third party JavaScript, that uses the bind() method internally.

Issue

Since PhantomJS is running an older version of WebKit, it doesn't support bind() be default, so when running a test that touches the third party code, Cucumber outputs the following error:

TypeError: 'undefined' is not a function (near '...}.bind(self);...')

Solution

To fix it, I needed to inject an implementation of bind() into PhantomJS before running the suite - luckily Poltergeist provides an easy way to do this.

Create a the folder vendor/poltergeist/ and add a file called bind.js with the definition of bind().

In vendor/poltergeist/bind.js:

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

Add the file to the :extensions property when initializing Poltergeist.

In features/support/poltergeist.rb:

require 'capybara/poltergeist'

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, {extensions: ["vendor/poltergeist/bind.js"]})
end

Capybara.javascript_driver = :poltergeist

Now the file will be loaded before running any tests in PhantomJS, making bind() a valid function and thus keeping the third party library.