Question

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: 'example@example.com'
      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: 'example@example.com')
  • 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('example@example.com')
  • 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: 'example@example.com'
    ...
  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 }
Was it helpful?

Solution

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: 'test@example.com' # 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

OTHER TIPS

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.

Just throwing out an idea here, since I've been stuck on these little idiosyncratic problems and know how frustrating they can be....Once I was just trying to get Capybara to click a "Close" button on a Twitter Bootstrap modal dialog box. It turns out that to get it to work, I had to enclose the Bootstrap modal html code in a "form" tag and then Capybara was able to find it. I wonder if something like that is your issue?

Capybara fill_in uses css ids not classes. Example

<input autofocus="autofocus" class="center" id="user_login" name="user[login]" placeholder="Username/Email" type="text" >

You may also have to use <input> instead of <div>

Try with Poltergeist, using the Window Switching methods

https://github.com/jonleighton/poltergeist#window-switching

stripe = page.driver.window_handles.last

page.within_window stripe do
  fill_in "Email", :with => "test@test.com"

  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

I found the solution in this spanish blog: numerica latina.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top