How to write integration tests for Stripe checkout on Rails?

djoll picture djoll · Jun 6, 2014 · Viewed 7k times · Source

I've hit the wall trying to write an integration test for Stripe's checkout.js [ https://checkout.stripe.com/checkout.js ] for my Rails 3.2 app.

Stripe checkout works correctly for me when manually tested (using Stripe's testing keys), but I cannot get Capybara to detect and fill_in the email field in the Stripe checkout iframe modal.

I am using poltergeist for headless javascript, though have also tested this with capybara-webkit and even selenium with the same problem.

What I am trying to test is the complete subscription sign-up flow, to show that a new user can create a subscriber account after entering their payment details in Stripe - but I cannot get past the Stripe checkout pop-up.

Here is my before .. do:

describe "show Stripe checkout", :js => true do
  before do
    visit pricing_path
    click_button 'plan-illuminated'
    stripe_iframe = all('iframe[name=stripe_checkout_app]').last
    Capybara.within_frame stripe_iframe do        
      fill_in "email", :with => "[email protected]"
      fill_in "billing-name", :with => "Mr Name"
      fill_in "billing-street", :with => "test Street"
      fill_in "billing-zip", :with => 10000
      fill_in "billing-city", :with => "Berlin"
      click_button "Payment Info"
    end
  end
  it { should have_selector('button', text: "Subscribe") }
end

Which errors with:

Failure/Error: Capybara.within_frame stripe_iframe do
 Capybara::Poltergeist::TimeoutError:
   Timed out waiting for response to {"name":"push_frame","args":[null]}

If I swap out the attempt to choose the correct iframe (suggested here: Capybara trouble filling in JS modal ) like so:

# stripe_iframe = all('iframe[name=stripe_checkout_app]').last
# Capybara.within_frame stripe_iframe do  
Capybara.within_frame 'stripe_checkout_app' do

I still get the similar:

Capybara::Poltergeist::TimeoutError:
   Timed out waiting for response to {"name":"push_frame","args":["stripe_checkout_app"]}

It appears that whichever javascript testing gem I use, rspec/capybara cannot find the Stripe checkout iframe. When I check with Selenium I see the Choose this Plan button pressed and the Checkout pop-up, but the spec times out looking for the email field to fill in.

Any ideas?

I've already tried:

  • Various ways of choosing or finding the email field.
  • Updating all my gems.
  • Using StripeMock before this (not that it should be involved, right?).
  • Running the same tests against Stripe's own site, which give the same errors:

Testing with:

  visit "https://stripe.com/docs/checkout"
  click_button 'Pay with Card'
  stripe_iframe = all('iframe[name=stripe_checkout_app]').last
  Capybara.within_frame stripe_iframe do
    fill_in 'Email', with: '[email protected]'
    sleep 3
  end

Depending which method I use to select the iframe I receive the same errors. Using just Capybara.within_frame 'stripe_checkout_app' do:

 Failure/Error: Capybara.within_frame stripe_iframe do
 Capybara::Poltergeist::TimeoutError:
   Timed out waiting for response to {"name":"push_frame","args":[null]}

or using Selenium with stripe_iframe = all('iframe[name=stripe_checkout_app]').last:

 Failure/Error: Unable to find matching line from backtrace
 SystemStackError:
   stack level too deep

or even just:

 Failure/Error: fill_in 'Email', with: '[email protected]'
 Capybara::ElementNotFound:
   cannot fill in, no text field, text area or password field with id, name, or label 'Email' found

...depending on which testing javascript gem I am using.

Any help or wisdom is greatly appreciated!

Answer

james picture james · Oct 21, 2014

I could not get any of the solutions here so far to work for me, and then reading this post: https://gist.github.com/nruth/b2500074749e9f56e0b7 I realized that the key was to add a delay to the test to give the Stripe enough time to 1) load the checkout window and 2) process the token.

For that reason the only code that I could get to work was this (feel free to play with timing) :

SELENIUM

describe "test stripe" do, js: true, driver: :selenium do

  before do
    ... # fill in order form or whatever
    click_button "checkout_with_stripe"
    sleep(2) # allows stripe_checkout_app frame to load
    stripe_iframe = all('iframe[name=stripe_checkout_app]').last
    Capybara.within_frame stripe_iframe do
      page.execute_script(%Q{ $('input#email').val('[email protected]'); })
        page.execute_script(%Q{ $('input#card_number').val('4242424242424242'); })
        page.execute_script(%Q{ $('input#cc-exp').val('08/44'); })
        page.execute_script(%Q{ $('input#cc-csc').val('999'); })
        page.execute_script(%Q{ $('#submitButton').click(); })
      sleep(3) # allows stripe_checkout_app to submit
    end
  end

  it "should successfully process the request" do
     ... # test should pass
  end 
end

For Capybara-webkit, the sleep trick didn't work nor sadly did @Ryan's solution, and I got too tired of trying to figure this out so I just stopped, but hopefully someone else will get it because I'd rather use webkit for speed reasons! (I was using capybara-webkit 1.3.0)

In case it helps, here are my relevant versions:

selenium-webdriver 2.35.1
rspec-rails 2.14.2
capybara 2.1.0
stripe 1.16.0 da216fd
rails 4.1.1
ruby 2.1.2