Deleting content from text field with Capybara - automation

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.

Related

Keyboard shortcuts on Flutter web

I'm adding keyboard shortcuts to a Flutter web application.
I have a form within a custom FocusableActionDetector where the shortcut has form like this:
SingleActivator(LogicalKeyboardKey.digit2)
and action is like:
CustomActivateIntent: CallbackAction<CustomActivateIntent>(
onInvoke: (intent) { provider.value = "2"; },)
In the same form I have a couple of numeric TextFormFields. To permit writing the character "2" I have to put these text fields inside some new FocusableActionDetector, otherwise the previous detector catches the command and the text field loses the "2" character, and this is already quite weird... Moreover, after writing in any of the text fields the form focus detector doesn't work anymore.
I think this could be related to the focus system, which is yet not that clear to me.
Can anyone help find a clean solution?
I found a workaround: the FocusableActionDetector is now preceded by an if statement. The code looks like the following:
// I extract the form to a widget to make it clearer
var searchWidget = SearchWidget();
child: textEditingInProgress
? searchWidget
: FocusableActionDetector(
child: searchWidget,
...,
),
The textEditingInProgress bool is a field in a provider and is controlled by the FocusNodes belonging to the TextControllers.
Still this is not a perfect solution, in particular I'd like to understand why the previous approach was not working.

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

Capybara, selecting 1st option from dropdown?

I've done a search and most of the related google results have returned just in general selecting an element from a dropdown. However the ID's in this case for the elements in the dropdown are dynamically generated unfortunately.
This is for a base test case, so I basically just need to select for example the first one. The text is also the same for the elements in the dropdown (not sure if that helps).
Is there such an example of this?
Im using cucumber with caybara(using selenium driver) integration
You can find the first option element and then use the select_option method to select it.
For example, if the select list has an id "select_id", you can do:
first('#select_id option').select_option
As #TomWalpole mentions, this will not wait for the element to appear. It would be safer to do one of the following:
first('#select_id option', minimum: 1).select_option
or
find('#select_id option:first-of-type').select_option
Alternatively you can get the first element text then select it by select function:
first_element = find("#id_of_dropdown > option:nth-child(1)").text
select(first_element, :from => "id_of_dropdown")
After two days of searching and reading, this article was amongst one of a few that was helpful. Hopefully, this can help someone else!
I created a few methods like so, excuse the naming..I changed it.
def some_dropdown(id, text)
dropdown = find(id).click
dropdown.first('option', text: text).select_option
end
def select_form
within 'content#id' do
some_dropdown('#id', text)
click_link_or_button 'Submit'
end
end
I also referenced this.
I've tried to select an option from a modal dropdown. After trying all listed methods, and many other from other threads - I totally gave up and instead of using clicks or select_option just used keyboard keys
find(:select, "funding").send_keys :enter, :down, :enter
In case it still complains - try:
find(:select, "funding", visible: false).send_keys :enter, :down, :enter
Worked like a charm, selecting first option from a dropdown.

Stripe checkout fill in form in Capybara-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

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