Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test Stripe.js using poltergeist and Capybara?

I've been going nuts trying to write an automated test for my user sign up page. Users will be charged a recurring subscription via Stripe. They input their basic details (email, password, etc) and their credit card details on the same form, then the following flow happens:

  1. (On the client-side) stripe.js makes an AJAX request to Stripe's servers, which (assuming everything is valid) returns a credit card token.
  2. My javascript fills in a hidden input in the HTML form with the credit card token, and submits the form to my Rails server.
  3. (Now on the server-side): I validate the user's basic details. If they're invalid, return (because there's no point charging them via Stripe if e.g. their email address is invalid so they can't create an account anyway.)
  4. If they're valid, attempt to create a Stripe::Customer object, add the right subscription and charge them using Stripe's ruby gem etc.

All of this works perfectly fine... except I can't figure out how to test it. Testing step #4 is easy enough as it takes place on the server-side so I can mock out the Stripe calls with a gem like VCR.

Step #1 is what's giving me trouble. I've tried to test this using both puffing-billy and the stripe-ruby-mock gem, but nothing works. Here's my own javascript (simplified):

    var stripeResponseHandler = function (status, response) {
      console.log("response handler called");
      if (response.error) {
        // show the errors on the form
      } else {
        // insert the token into the form so it gets submitted to the server
        $("#credit_card_token").val(response.id);

        // Now submit the form.
        $form.get(0).submit();
      }
    }


    $form.submit(function (event) {
      // Disable the submit button to prevent repeated clicks
      $submitBtn.prop("disabled", true);
      event.preventDefault();

      console.log("creating token...");
      Stripe.createToken(
        // Get the credit card details from the form
        // and input them here.
      }, stripeResponseHandler);

      // Prevent the form from submitting the normal way.
      return false;
    });

Just to reiterate, this all works fine when I test it manually. But my automated tests fail:

 Failure/Error: expect{submit_form}.to change{User.count}.by(1)
   expected result to have changed by 1, but was changed by 0

When I try to use the gem puffing-billy, it seems to be caching stripe.js itself (which is loaded from Stripe's own servers at js.stripe.com, not served from my own app, as Stripe don't support this.), but the call initiated by Stripe.createToken isn't being cached. In fact, when I log into my Stripe server logs, it doesn't seem that the call is even been made (or at least Stripe isn't receiving it.)

Note those console.log statements in my JS above. When I run my test suite, the line "creating token..." gets printed, but "response handler called." doesn't. Looks like the response handler is never being called.

I've left out some details because this question is already very long, but can add more on request. What am I doing wrong here? How can I test my sign up page?

UPDATE See [my comment on this Github issue] on stripe-ruby-mock for more info on what I've tried and failed.

like image 312
GMA Avatar asked Apr 25 '15 05:04

GMA


2 Answers

If I understand correctly...

Capybara won't know about your ajax requests. You should be able to stub out AJAX requests with Sinatra. Have it return a fixtures much the same as VCR.

Here's an article on it.

https://robots.thoughtbot.com/using-capybara-to-test-javascript-that-makes-http

You need to boot the Sinatra app in Capybara and then match the URLs in your ajax calls.

Something like:

class FakeContinousIntegration < Sinatra::Base
  def self.boot
    instance = new
    Capybara::Server.new(instance).tap { |server| server.boot }
  end

  get '/some/ajax'
    # send ajax back to capybara
  end
end

When you boot the server, it will return the address and port which you can write to a config that your js can use.

@server = App.boot

Then I use the address and port to config the JS app

def write_js_config
  config['api'] = "http://#{@server.host}:#{@server.port}"
  config.to_json
end

In spec_helper.rb send in the config to the js so your script points to your sinatra app. Mine compiles with gulp. So I just build the config into to is before the tests run:

system('gulp build --env capybara')
like image 186
Rimian Avatar answered Nov 06 '22 19:11

Rimian


I've had tests which worked on manual fail in Capybara/poltergeist due to timeout. In my case, the solution was to wait for all AJAX requests to finish. Reference

Not sure whether Stripe.js uses JQuery internally, try checking for a condition set by stripeResponseHandler.

like image 43
fylooi Avatar answered Nov 06 '22 19:11

fylooi