19

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 }
Community
  • 1
  • 1
nickcoxdotme
  • 6,567
  • 9
  • 46
  • 72
  • in this article, there are some tips for how to debug capybara tests http://nofail.de/2013/10/debugging-rails-applications-in-development/ – phoet Mar 12 '14 at 00:52
  • can you show me your code for model popup form, which you want to fill ? – Sachin Singh Mar 15 '14 at 13:56
  • The form HTML is dynamically generated by Stripe's JS, but you can view a screenshot of the full markup of the form [right here](http://www.flickr.com/photos/79751190@N05/13174005044/). – nickcoxdotme Mar 15 '14 at 19:38
  • Assuming you're trying to fill in ... have you tried fill_in('email') [note the lower case]; also you may want to specify id="email" –  Mar 17 '14 at 04:45
  • 2
    nick, I have a very similar / identical problem, though cannot even get your solution working for me now. You wouldn't happen to have any other suggestions? Thanks! http://stackoverflow.com/questions/24081305/how-to-write-integration-tests-for-stripe-checkout-on-rails – djoll Jun 06 '14 at 12:05
  • I haven't seen that timeout. Which version of Capybara are you using? – nickcoxdotme Jun 06 '14 at 20:03

5 Answers5

6

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
Andrei Botalov
  • 20,686
  • 11
  • 89
  • 123
  • [Here](https://github.com/thenickcox/slow_coffee_checkout/blob/master/spec/features/app_spec.rb) is the link to the spec on GitHub. Feel free to checkout the repo and run the spec. – nickcoxdotme Mar 20 '14 at 20:33
  • BTW, I think the bounty only has an hour left. Sorry if it closes before you provide a working answer. If that's the case, I'll see if I can start another and award it to you. – nickcoxdotme Mar 20 '14 at 20:34
  • Thanks! Just commented on the issue. – nickcoxdotme Mar 20 '14 at 22:38
  • Also, I just tried your code to repro, and you're absolutely right; that field is filled in! – nickcoxdotme Mar 20 '14 at 22:41
  • I noticed that this instantiates Capybara::Session. I don't see that in the docs anywhere. Is my config wrong in my [spec_helper.rb](https://github.com/thenickcox/slow_coffee_checkout/blob/master/spec/spec_helper.rb)? – nickcoxdotme Mar 20 '14 at 22:45
  • I also tried instantiating Capybara::Session in that same way in my test, and now I get `Unable to locate frame: stripe_checkout_app`, although it's clearly on the page, and the same example works on the Stripe site. – nickcoxdotme Mar 20 '14 at 22:52
  • @nickcoxdotme You don't need to instantiate session manually. It becomes available in your spec when you `require 'capybara/rspec'` – Andrei Botalov Mar 21 '14 at 06:04
  • @nickcoxdotme Session becomes available in your specs by including `Capybara::DSL` - https://github.com/jnicklas/capybara/blob/master/lib/capybara/rspec.rb#L8 – Andrei Botalov Mar 21 '14 at 06:12
5

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.

gimiarn1801
  • 131
  • 2
  • 7
  • Thanks for at least trying. See #7 of things I tried with a `sleep 3`. Unless you tried it another way? – nickcoxdotme Mar 14 '14 at 15:49
  • 1
    did you try `page.find('.checkout')` ? – gimiarn1801 Mar 14 '14 at 16:29
  • Just did. `Unable to find css ".checkout"`. I tried it after the `sleep 3` also. I noticed that Capybara has an `execute_script` method, but I wasn't able to get that to do anything. It's just baffling to me how different the page markup is. (See the gist of the HTML.) – nickcoxdotme Mar 15 '14 at 01:47
  • @gimiarn1801 Here's some rep to help you get to 50. – ggPeti Mar 15 '14 at 20:33
0

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?

  • That's an interesting thought. See haml markup, above. Would you suggest wrapping the `script` tag in a div and doing something like `find('div.script-container') do`? – nickcoxdotme Mar 15 '14 at 19:32
  • Ah, edited the response to say put the code that is responsible for generating the modal in a "form" tag. SOverflow hid the "
    " part of my answer.
    –  Mar 16 '14 at 22:57
  • Hm…So you can see in my haml that the `script` tag is in a `form` tag. Is that similar to what your situation was? What was it that generated the modal for you? – nickcoxdotme Mar 17 '14 at 17:23
  • My modal code was static HTML & was similar to what is under the section "the hack" in this post: http://www.binarywebpark.com/javascript-testing-with-cucumber-capybara-poltergeist-phantomjs-and-the-twitter-bootstrap-modal-dialog-box/ Can you use static HTML for the modal and see what happens? Have you tried changing Capybara.default_wait_time to something longer than 2 sec? –  Mar 18 '14 at 04:49
0

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>

hunterboerner
  • 1,264
  • 1
  • 11
  • 25
-1

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.

parov
  • 1,077
  • 4
  • 12
  • 22