how to click on a table row using capybara & rspec - ruby-on-rails-3

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

Related

Using I.Expect.Text with fluentautomation

Does I.Expect.Text("sometext") require the In method with specified selector?
I ask ask as some tests I've inherited don't have the In method appended, and our tests pass, even when I know that the supplied text does not exist on page.
I have looked at the docs, re http://fluent.stirno.com/docs/#asserts-text - and it doesn't seem clear (to me anyhow) that the In selector HAS to be present. I am assuming that it needs to be present.
I guess what I am asking is that if the In method does need to be appended in order for the assertion to work correctly, how would I fluently state does "sometext" appear in any div or p tag or class on page, or should I be using Ids throughout. I see something useful at https://github.com/stirno/FluentAutomation/issues/133.
So something like the following that could be wrapped into an extension method on AssertSyntaxProvider, however it appears heavy-handed/clunky?
var texts = I.Find("html").Elements.Select(el => el.Item2().Text);
I.Assert.True(() => texts.Any(t => t.Contains("statement1")));
I.Assert.True(() => texts.Any(t => t.Contains("statement2")));
I.Assert.True(() => texts.Any(t => t.Contains("statement3")));
Help appreciated!!
Last thing - this is an awesome Library!!!
Yes, In is required as its the executing block of the chain. Its been awhile since I've been able to focus on expanding FluentAutomation so It hasn't become any nicer to make assertions against multiple elements.
I do have a nice plan for this that I just haven't had time to implement.
As for your specific test, You're close to what I'd use for now:
I.Open("http://fluent.stirno.com");
var elements = I.FindMultiple("*").Children.Select(x => x());
I.Assert.True(() => elements.Any(x => x.Element.Text.Contains("v3.0 Released!")));
v3.1 will have some better methods for dealing with this but this at least uses Sizzle's * selector to get every element (warning: could be real slow so maybe use "div,p,span" or similar if you can limit it).

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

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.

cucumber select multiple checkboxes

I'm working on a Rails 3 application with Cucumber 1.1.1 and I'm trying to select multiple checkboxes in my cucumber scenarios.
Here is my multiple checkboxes in haml:
- for diocese in Diocese.all
= check_box_tag "clergy[diocese_ids][]", diocese.id, #clergy.dioceses.include?(diocese)
= f.label diocese.name
%br
Here is my cucumber step
When /^I check the (\d+)(st|nd|rd|th) of the "([^"]*)" checkboxes$/ do |index, junk, group|
page.all("input[id=\"clergy_diocese_ids_\"]")[index.to_i - 1].check('clergy_diocese_ids_')
end
Here is my cucumber test:
#wip
Scenario: New Clergy
Given I am on the clergies page
When I follow "New Clergy"
And I fill in "Surname" with "Wells"
And I fill in "Given names" with "Robin"
And I check the 1st of the "diocese" checkboxes
And I check the 2nd of the "diocese" checkboxes
And I press "Create Clergy"
Then I should see "Clergy was successfully created."
The error I get when running my test:
And I check the 1st of the "diocese" checkboxes # fea
tures/step_definitions/clergy_steps.rb:37
cannot check field, no checkbox with id, name, or label 'clergy_diocese_ids_' found (C
apybara::ElementNotFound)
./features/step_definitions/clergy_steps.rb:38:in `/^I check the (\d+)(st|nd|rd|th) of
the "([^"]*)" checkboxes$/'
features/clergy_new.feature:17:in `And I check the 1st of the "diocese" checkboxes'
I've tried playing around with the page.all selector but I cannot work it out.
As I understand it, its not the job of Cucumber to select checkboxes and interact with the UI. Cucumber parses the feature files and executes the corresponding step definitions that match. What you put in each step definition block is up to you.
Since your step definition code appears to be using Capybara, I suggest viewing this solution.

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)