Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capybara trouble filling in JS modal

Let me start by confirming that this isn't a duplicate (in that the answers posted there didn't fix my problem). This post is essentially my exact problem: Capybara can't find the form fields in the Stripe modal to fill them in.

Here's my Capybara spec:

describe 'checkout', type: :feature, js: true do
  it 'checks out correctly' do
    visit '/'
    page.should have_content 'Amount: $20.00'
    page.find('#button-two').click_button 'Pay with Card'
    Capybara.within_frame 'stripe_checkout_app' do
      fill_in 'Email', with: '[email protected]'
      fill_in 'Name',  with: 'Nick Cox'
      fill_in 'Street', with: '123 Anywhere St.'
      fill_in 'ZIP', with: '98117'
      fill_in 'City', with: 'Seattle'
      click_button 'Payment Info'
      fill_in 'Card number', with: '4242424242424242' #test card number
      fill_in 'MM/YY', with: '02/22'
      fill_in 'CVC', with: '222'
      click_button 'Pay'
    end
    page.should have_content 'Thanks'
  end
end

The text in that spec is my most recent attempt. The Capybara error is Unable to find field "Email".

What I've tried

  • This most recent attempt at filling in the field by placeholder text as suggested here
  • Finding the email field by the name attribute (e.g., fill_in 'email', with: '[email protected]')
  • With and without the type: :feature and js: true hashes in the describe
  • With and without the within_frame call on Capybara
  • Trying to find the input by css (e.g., page.find('.emailInput input').set('[email protected]')
  • Adding a within block: within('.panel') do around the form inputs (fails with Unable to find css ".panel")
  • Adding a sleep 3 before the previous step in case it's looking for that div too early (fails with Unable to find css ".panel")
  • Adding a call to find (and also page.find) and passing the form manipulation as a block (with and without a within block nested in the find block):

Capybara.within_frame 'stripe_checkout_app' do
  page.find('.panel') do
    fill_in 'Email', with: '[email protected]'
    ...
  end
end

I've also tried this with poltergeist.

What's maddening about this is that I've changed the Capybara driver from poltergeist back to selenium/Firefox and I can physically see the driver opening the page, clicking the button and bringing up the modal.

Any thoughts on how to get Capybara to interact with this form?

Edit

I also tried sticking a binding.pry in there and debugging with the pry gem. The page's HTML at the point that the modal loads is a few elements, and then script tags that dynamically insert JS into the head of the iframe. See this screenshot of the DOM at the point where the binding.pry is hit (after the modal window opens with the Stripe form):

enter image description here

So that <div class="emailInput"> is clearly in there, but page.has_selector?('.emailInput') returns false in the pry console.

Here is a GitHub gist of what happens when I print page.html from the pry binding as the next line after Capybara.within_frame 'stripe_checkout_app' do. That is, this is the markup from Capybara's perspective, and this varies wildly from the DOM as shown in the screenshot of the DOM of the actual form when it appears on screen, shown above. As you can see, there are no inputs or anything to work with in the markup that Capybara sees.

Update

Here is the markup for the form that contains the JS that executes the modal. (Markup is in haml).

  %form{ action: "/charge?amount=1400", method: 'post', class: 'payment', id: 'fourteen' }
    %article
      %label.amount
        %span Amount: $14.00

    .stripejs#button-one
      %script{ src: 'https://checkout.stripe.com/checkout.js', class:'stripe-button', :'data-key' => settings.publishable_key, :'data-name' => 'Slow Coffee', :'data-description' => '2 8oz. bags (2/month)', :'data-shipping-address' => true }
like image 850
nickcoxdotme Avatar asked Mar 11 '14 20:03

nickcoxdotme


2 Answers

I can't reproduce this issue against Stripe's demo checkout page.

require "capybara"

sess = Capybara::Session.new(:selenium)
sess.visit("https://stripe.com/docs/checkout")
sess.click_button('Pay with Card')
sess.within_frame('stripe_checkout_app') do
  sess.fill_in 'Email', with: '[email protected]' # it's visible that field becomes filled in
  sleep 3 # just to inspect that manually
end

The issue described by you is because there are two iframes with name stripe_checkout_app. within_frame finds the first of them that doesn't contain billing fields. To find second iframe you can do e.g.:

stripe_iframe = all('iframe[name=stripe_checkout_app]').last
within_frame stripe_iframe do
  # your code here
end
like image 103
Andrei Botalov Avatar answered Nov 20 '22 14:11

Andrei Botalov


Don't have 50 rep to comment but, have you tried adding some wait time on capybara so the modal has time to load completely, I came across a similar problem using testcafe and giving it 2-3 seconds wait time did the trick.

like image 23
gimiarn1801 Avatar answered Nov 20 '22 14:11

gimiarn1801