Stripe checkout fill in form in Capybara-webkit - webkit

I'm using capybara-webkit driver for my JS enabled tests. However when I try to fill in the form fields for the stripe checkout iframe, I'm not able to do it using the capybara fill_in helper in both the drivers. Selenium driver provides methods that facilitates in achieving this task though.
4.times {page.driver.browser.find_element(:id, 'card_number').send_keys('4242')}
page.driver.browser.find_element(:id, 'cc-exp').send_keys '5'
page.driver.browser.find_element(:id, 'cc-exp').send_keys '18'
page.driver.browser.find_element(:id, 'cc-csc').send_keys '123'
page.driver.browser.find_element(:id,'billing-zip').send_keys '600004'
If I use the fill_in helper, I'm not able to input the details fully. For example for a 16 digit card number, the input field gets filled only with 4 digits & in the date field I'm able to input only the month and not the year.
I want to know if there are helpers in the webkit driver that would enable me to fill in forms in the Stripe checkout form. Any heads up on this would be great! Thanks in advance.

I was having similar problems with Selenium that both
find(:css, "input[id$='card_number']").set("4242 4242 4242 4242")
fill_in('card_number', with: "4242 4242 4242 4242")
stopped working. I had find(:css, ...) earlier, which still worked a few months ago, but I guess changes in checkout.js made it so that it didn't work any more. With the help of Capybara cannot fill Stripe Checkout.js fields I managed to get it working. It's not a pretty (or really behavior-driven) solution, but it does the job:
stripe_iframe = all('iframe[name=stripe_checkout_app]').last
Capybara.within_frame stripe_iframe do
page.execute_script(%Q{ $('input#card_number').val('4242 4242 4242 4242'); })
page.execute_script(%Q{ $('input#cc-exp').val('12/16'); })
#rest of the Stripe-capybara
end
I am not sure if it works with Webkit though.

It looks like Stripe has changed the IDs on the checkout elements. Many have dynamic IDs that aren't matched by some of the other examples here.
What worked for me is matching elements by the placeholder text. Here's a working snippet as of 12/06/16:
stripe_card_number = '4242424242424242'
within_frame 'stripe_checkout_app' do
find_field('Card number').send_keys(stripe_card_number)
find_field('MM / YY').send_keys "01#{DateTime.now.year + 1}"
find_field('CVC').send_keys '123'
find('button[type="submit"]').click
end
page.has_content?('Success!', wait: 30)

For completeness, here's working code.
Capybara.within_frame stripe_iframe do
page.find_field('Email').set 'foo#bar.com'
page.find_field('Card number').set '4242 4242 4242 4242'
page.find_field('MM / YY').set '12/42'
page.find_field('CVC').set '123'
find('button[type="submit"]').click
end
You can always find the latest working version in
https://github.com/dblock/slack-gamebot/blob/master/spec/integration/update_cc_spec.rb

I had exactly the same issue, and found that the following solution works with capybara-webkit as well as with selenium-chrome:
page.find_field('Email').set "test#gmail.com"
page.find_field('Card number').set ('4242424242424242')
The "Email" and "Card number" here are the placeholders. The reason it works this way and doesn't with find('#email') is because there's no element with id='email' on the page. Capybara correctly can't find this element, it doesn't exist many times. The reason there's no element is because sometimes stripe's iframe is rendered without proper id, like so:
element-with-dynamic-id
find_field searches by id, name, or placeholder, and in this case, placeholder is the only attribute that doesn't change in both situations and that can be found by capybara (for ex. "type" doesn't change either, but I couldn't find the way to make capybara search by type and also the test would be much less readable then).
Why does Stripe's iframe sometimes miss these correct ids and replaces them with dynamic ids? No idea.

If you're using Stripe from an iframe (perhaps https://www.paymentiframe.com/?), you should be able to change the scope of your tests to the frame using Capybara's within_frame method:
within_frame('stripe-iframe') do
fill_in 'card_number', :with => '4242'
end
If you give your iframe a name (stripe-iframe in that example) then that should do it.
Hope that's of help!

I made it similar to #humpah, but without evil JS code.
form_iframe = all('iframe.wysihtml5-sandbox').last
within_frame form_iframe do
page.find('body').set('SomeContent')
end

Make sure you:
Wrap your query method in a Capybara.within_frame block
Use the placeholder value in Stripe's input fields to fill in the field.
Example that is currently working for me:
Capybara.within_frame first("iframe") do
fill_in "Card number", with: "4242424242424242"
fill_in "MM / YY", with: "12 / #{ (Time.now.year + 1).to_s[-2..-1] }"
fill_in "CVC", with: "123"
fill_in "ZIP", with: "12345"
end

Related

Copy-paste using Capybara?

I would love to do something like this:
div = find '#some-div'
copy_to_clipboard(div)
input = find '#my-input'
paste_from_clipboard(input)
I do not want to simulate this with send_keys and using Ctrl+C and Ctrl+V; I want this to work cross-browser (especially on mobile).
Does this API exist?
The most simple way I've found:
element.send_keys [:control, 'c']
element.send_keys [:control, 'v']
There is no Capybara copy/paste API - If all you want to do is copy the visible text into an input then you could do
div_text = find('#some-div').text()
find('#my-input').set(div_text)
If that's not correct for what you want, then you could use #execute_script to create a selection range like
var range = document.createRange();
range.setStart( <start node>, <start node character offset> );
range.setEnd( <end node>, <end node character offset> );
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
then find your target element and set it's value to window.getSelection().toString(). Note that's not really emulating what a user would do, so if you are actually using this for testing an app I would still recommend using the ctrl/cmd-c/v after setting the selection range for browsers that support it since it emulates user behavior better.
It's an old one, however you don't need to use capybara however the workaround would to use this incredibly simple gem:
https://github.com/janlelis/clipboard
There is no API to do it.
You can get element from one browser
div = page.find('#some-div')
Then you can pass it to another browser
fill_in '#some-other-div' with => div
You can read more about capybara here: https://github.com/jnicklas/capybara

Selenium webElement.clear() doesn't work with phantomJS but yes with Firefox

I am trying to use PhantomJS as a browser to make a selenium test, but I have a problem with an input.
This input is a calendar (is not type date, is text but modified by JavaScript).
First of all, I have this using FirefoxDriver and works perfectly:
webElement.Clear();
webElement.SendKeys(date);
but, using PhantomJSDriver jumps an exception which says this:
{"errorMessage":"Element must be user-editable in order to clear it.","request":{"headers":{"Accept":"application/json, image/png","Connection":"Close","Content-Length":"0","Content-Type":"application/json;charset=utf-8","Host":"localhost:53291"},"httpVersion":"1.1","method":"POST","post":"","url":"/clear","urlParsed":{"anchor":"","query":"","file":"clear","directory":"/","path":"/clear","relative":"/clear","port":"","host":"","password":"","user":"","userInfo":"","authority":"","protocol":"","source":"/clear","queryKey":{},"chunks":["clear"]},"urlOriginal":"/session/a1e4cdb0-b901-11e4-befc-c3b28b46e8f3/element/%3Awdc%3A1424437741971/clear"}}
is very extraneous because in Firefox works perfectly,
what can I do to send the data?
Also I tried to sen this data without clear it, but the default value is 20/02/2015 and when i send something just add after the current text: 20/02/201530/03/2015 and doesn't work.
attribute .text doesn't has setter, the same for getAttribute().
thanks for all,
Ivan.
#edit1: html
<input type="text" aria-haspopup="true" title="" value="20/02/2015" name="id0" id="id0" class="bg datetime hasDatepicker">
Finally i found how to do this, probably is not the best way, but in my case works.
I send the data by JavaScript with javascriptExecutor
((IJavaScriptExecutor)_driver).ExecuteScript("document.getElementById('id0').value='" + date+ "'");

Deleting content from text field with Capybara

I'm writing a script that fills out text fields with Capybara, but prior to filling out the fields, I want to ensure that the fields are empty and that text is not autofilled. Basically, I'm looking for the opposite of
(Object) fill_in(locator, options = {}) #empty_content_of? delete?
found here: http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions. Advice?
For me only this solution worked:
fill_in('Foo', with: 'bar', fill_options: { clear: :backspace })
I have Vue.js on frontend.
After struggling with this, I asked a coworker and the solution was to use the following:
fill_in(locator, with: "")
So, for example:
fill_in "Name", with: ""
This makes perfect sense and is probably intuitive to many, but I was stumped and couldn't find an answer on SO so I thought I would post about it in case it helps anyone.
you can use the native selenium bindings to clear an input field without filling in an empty string
element = find('locator')
element.native.clear
I prefer this option rather than fill_in.
Also if you think about it fill in is limited to find your locator by label or name so if it doesn't have a label or name you still have to use find
A solution that works for me and has always been reliable:
field = find('locator')
field.value.length.times { field.send_keys [:backspace] }
In Capybara terms of mimicking a user's behaviour, this also seems a correct way to me of doing that.
For React you've got to do more than that. fill_in field, with: '' does native.clear. Which doesn't play nicely with React. As doesn't fill_in field, with: 'some text'. Since it does arguments[0].value = '' before typing text.
I've run into the issues with react-datetime. What I've settled with is:
def fill_in_react_datetime n, options
with = options[:with] == '' ? '' : format_date(options[:with])
fill_in n, with: with, fill_options: {clear: [[:control, 'a'], :delete]}
end
Building on the accepted answers, you can optionally pass with: nil:
e.g.
fill_in("search-input", with: nil, fill_options: { clear: :backspace })
I prefernil over an empty string ("") simply for explicitness. I am explicitly not setting the value and clearing with backspace. Makes for an understandable test.

how to click on a table row using capybara & rspec

I am writitng request spec for my rails app with capybara. In my code I have something like:
%table
%tbody
%tr{"on_click" => "location.href='some_link'"}
%td="Some attribute"
%td="Some attribute"
%td="Some attribute"
%td="Some attribute"
This way I make the entire row clickable. I want to write a request spec with capybara for this feature but I don't know how. Can anyone help me on this ?
Thank you
May be you should first get to know the testing in rails. Check this out! http://railscasts.com/episodes/275-how-i-test it is really helpful. You can give your tr a class (say) .tr and do
page.find(:css, ".tr").click()
I hope this works, worked in my case!
I found this works without requiring a class:
page.find(:xpath, "//table/tbody/tr").click
I believe your row-click requires JavaScript, so you'll need to add :js => true to your test's header. Setting up testing with JavaScript is a challenge. I found these resources helpful:
ASCIIcast #257 Request Specs and Capybara - see info on database-cleaner
Capybara readme - see “Transactions and Database Setup"
Here is a more complete test example:
# Note that opening page by clicking on row requires JavaScript
describe "when user clicks on first row", :js => true do
let(:first_account_listed) { Account.order(:name).first }
before { page.find(:xpath, "//table/tbody/tr").click }
it { should have_selector('title', text: 'Account Details') }
end
In Capybara 3+ you can go with a more elegant way by using the :table_row selector and match against a td value like:
page.find(:table_row, ["Some attribute"]).click
If you have table headers defined you can also pass a Hash instead of an Array that will match the cell against its corresponding table header like this:
page.find(:table_row, { "Header" => "Cell value" }).click
To dive into how this actually works, here's a link to the latest Capybara selector definition: https://github.com/teamcapybara/capybara/blob/master/lib/capybara/selector/definition/table_row.rb

Testing focus with Capybara

I have a simple link_to_function in my view template
<%= link_to_function "add new category", "$('#category_name').focus()" %>
and I want to test this with capybara using request specs. Basically the spec should look something like this
it "focuses category form when I click 'add new category'" do
visit new_article_path
click_link "add new category"
# unfortunately there's nothing like 'has_focus?'
find_field("category_name").should have_focus
end
the problem is, I wasn't able to find anything, that would check if the element has focus.
The only thing I did find was this
page.evaluate_script('document.focus')[:id]
which however isn't supported by the capybara-wekbit driver, which I'm using to avoid opening browser for each test run.
I just used the following code (with phantomjs driver, but I believe that it works with webkit also):
page.evaluate_script("document.activeElement.id") == "some_id"
P.S. One year question without an answer. Should they give me a badge? :)
You should use the :focus selector, e.g:
page.should have_selector('#category_name:focus')
With the Selenium driver you can get the focused element:
page.driver.browser.switch_to.active_element
Then you can do what you like with it
page.driver.browser.switch_to.active_element.send_keys "some text"
Note that it returns a Selenium::WebDriver::Element whereas find returns a Capybara::Node::Element so be careful when comparing them
expect(page.driver.browser.switch_to.active_element).to eql(find('#some-element').native)