Rspec Capybara find field not functioning - ruby-on-rails

I'm running Rails 4 using capybara and rspec (OS is Ubuntu 13.10). I'm having a problem -- when I run rspec my specs work, including those that use capybara's fill_in methods. However in one spec I need to use capybara's find_field method, and it is not functioning at all. It gives me the following error:
Failure/Error: page.find_field('From:').set(Date.today - 1.month)
Capybara::ElementNotFound:
Unable to find field "From:"
Now, I have inserted a "puts page.html" line immediately before the page.find_field... line and the html it prints includes the following lines:
<div class="date-range-picker">
<span class="from-date"><label for="from_date">From:</label> <input id="from_date" name="from_date" type="date" value="2013-12-23" /></span>
<span class="to-date"><label for="to_date">To:</label> <input id="to_date" name="to_date" type="date" value="2013-12-30" /></span>
</div>
So the element is there, but not being picked up by the find_field method. Any ideas?

OK, after much meandering through Capybara's source files I found the problem. It seems that the #find_field method doesn't work properly when using Capybara-webkit. The method only failed on examples that had the js: true argument, so that should have been the first clue. Anyway it seems that the cause of this is some method naming conflict between capybara and capybara-webkit, but I didn't analyze it too closely and so I can't be sure.
I changed the find_field('from_date') to find('#from_date') and everything works now. It also worked when changing to the :rack_test driver, but since I need webkit that's what I'll stick too. Is this issue documented anywhere??

As for capybara documentation:
"Find a form field on the page. The field can be found by its name, id or label text."
so use this code instead:
page.find_field('form_date').set(Date.today - 1.month)
So you are selecting the field by it's id.

I placed your HTML fragment within an HTML page and was able to successfully do a find_field('From:') on it, which suggests the problem lies within the containing HTML. My "idea" would be to strip down the page until the find_field succeeds as a means of isolating the offending content.

I found this on another site and it slightly worked for me:
Add this to "support/select_from_chosen.rb":
module SelectFromChosen
# select_from_chosen('Option', from: 'id_of_field')
def select_from_chosen(item_text, options)
field = find_field(options[:from], :visible => false)
find("##{field[:id]}_chosen").click
find("##{field[:id]}_chosen ul.chosen-results li", :text => item_text).click
end
end
RSpec.configure do |config|
config.include SelectFromChosen, type: :feature
end
I was able to do:
select_from_chosen('Blue', from: 'car_colors')
find('#submit-button').click
expect(page).to have_content 'Blue'
select_from_chosen('Red', from: 'car_colors')
find('#submit-button').click
expect(page).to have_content 'Red'
What I was actually trying to do was validate that the options in the chosen results where available for some users and not for others so I wound up using:
find('#car_colors_chosen').click
expect(page).to have_content 'Blue'
expect(page).not_to have_content 'Gold'
Just wanted to add this incase it helped someone else,

Related

Capybara testing with RSpec in Ruby

on my index page I have this div:
<div class="banner">
<h1 class="glow-header">Galaxy Far, Far Away? Quick Trip to Mars?<br>
Pianeta has you covered.</h1>
<div>
In my testfile this works:
RSpec.describe 'home features' do
it 'displays the name of the app and links to the index-all planets page' do
visit root_path
expect(page).to have_content('Space is full of surprises.')
click_link('Go Beyond')
expect(current_path).to eq('/planets')
expect(page).to have_content('Galaxy Far, Far Away?')
end
end
But I would like it to be working with the h1 included.
I did this:
expect(page).to have_content('<h1 class="glow-header">Galaxy Far, Far Away? Quick Trip to Mars?<br>
Pianeta has you covered.</h1>')
end
But the test failed. What did I do wrong ?
The #has_content?/#has_text? method only checks the text content of the page. It does not look at the HTML tags.
If you want to check for content within a specific HTML element there is a #within method that takes a block and will scope the Capybara lookups within it to be within the matched element. The element referenced by #within must exist or Capybara will raise an exception.
page.within('h1.glow-header') do
expect(page).to have_content('Galaxy Far, Far Away?')
end
If you don't want to deal with scoping using within for a single expectation you could do
expect(page).to have_css('h1.glow-header', text: 'Galaxy Far, Far Away?')
If you've already got a reference to the header you could also do something like
header = find('h1.glow-header')
...
expect(header).to have_text('Galaxy Far, Far Away?')
Additionally you should not be doing expect(current_path).to eq('/planets'). Using RSpecs eq matcher with Capybara will lead to flaky tests as soon as you move to using an asynchronous (JS supporting) driver, because it prevents Capybaras auto waiting/retrying behaviors. Instead you should use the Capybara provided matcher
expect(page).to have_current_path('/planets')

Capybara: fill_in: unable to find field

Code: https://github.com/jmopr/job-hunter/blob/master/scraper.rb
So i'm running a scraper on indeed.com but it seems to be unable to find the field q, which is the first field on the left. I'm 100% certain that the code is correct but i'm not sure why it isn't able to view the form. save_and_open_page CONFIRMS that it was able to view indeed.com successfully... yet is unable to locate the form.
Code:
def perform_search
# For indeed0
save_and_open_page
fill_in 'q', :with => #skillset
fill_in 'l', :with => #region
find('#fj').click
sleep(1)
end
Indeed.com HTML:
<input class="input_text" maxlength="512" size="31" aria-labelledby="what_label_top hidden_colon what_label_bot" name="q" autocomplete="off" id="what">
Required portions/redirects of the page are loaded from 'indeed.com' rather than 'www.indeed.com' - Change your capybara-webkit configuration to allow indeed.com.
Additionally, if you want to know what the driver is actually seeing, you're generally better off using save_and_open_screenshot rather that save_and_open_page (The latter will show you the html structure but not how capybara-webkit has rendered it)

How to click the form commit button in Capybara

With Rails, rspec and capybara, I'm trying to test your typical ERB generated form:
<form action="/pages/1" id="edit_page_1" method="post">
<input id="page_title" name="page[title]" type="text">
<input name="commit" type="submit" value="Update Page">
</form>
I run two kinds of feature specs, those that are the same no matter what the language, and those that are I18N specific (for internationalization testing).
The problem is there is no clear way to click that submit button with capybara, unless I'm missing the obvious. I would expect simply click('commit') to do the trick.
Using click_button('Update Page') works but is obviously language specific and can't be used with both the New and Edit templates even though they render the same form template.
Adding an id to the submit button works, but I strongly dislike changing the code exclusively because the test requires it.
Using a css or xml matcher both looks ugly (a user would never know/care about accessing an element that way) and it is overkill.
In the end a macro was the answer I needed as apparently there is nothing native in capybara.
# spec/support/form_helpers.rb
module FormHelpers
def submit_form
find('input[name="commit"]').click
end
end
This gets included in spec_helper.rb
RSpec.configure do |config|
config.include FormHelpers, :type => :feature
...etc...
I used :type => :feature so it gets included only in the integration tests.
In the integration tests you can use it like this:
scenario 'pages can be created' do
visit new_page_path
fill_in 'page_title', with: 'A Tale of Two Cities'
submit_form # Clicks the commit button regardless of id or text
expect(page).to have_content 'The page was created'
...etc..
end
Of course submit_form can also be used inside within blocks and with :js => true.
I usually do:
within 'form#edit_page_1' do
find('input[name="page[title]"]').set "Some Value"
find('input[name="commit"]').click
end
Its tied to the html but to its semantic attributes, so I feel like its fine.
Actually I never use the magical finders.
Btw I dont understand your comment: (a user would never know/care about accessing an element that way).
Integration specs are for you, it mimics a user for sure but its just a matter of giving proper instructions.
Try to use:
find('input[name="commit"]').click
It helps

Capybara and Rspec: correct way to use within() and have_selector() together?

I use rspec 2.6.0 and Capybara 1.1.1 for acceptance testing.
With a view like the following:
<tr >
<td>Team 3 Name</td>
<td>true</td>
<td>Show</td>
<td>Edit</td>
<td>Deactivate</td>
</tr>
<tr >
<td>Team 4 Name</td>
<td>true</td>
<td>Show</td>
<td>Edit</td>
<td>Deactivate</td>
</tr>
I want to write an acceptance test that states: "Team 3 does NOT have the 'Deactivate' link." I expect the following to fail:
within('tr', :text => 'Team 3 Name') do |ref|
page.should_not have_selector('a', :text => 'Deactivate')
end
But it passes. To further test what is going on, I wrote the absurd:
lock = false
within('tr', :text => 'Team 3 Name') do |ref|
page.should have_selector('a', :text => 'Deactivate')
page.should_not have_selector('a', :text => 'Deactivate')
lock = true
end
lock.should be_true
Which passes as well.
I am assuming from this that the scope the have_selector() call is using is not limited by the within() block, but I am not sure why this is. The capybara documentation uses this pattern and does not seem to mention any gotchas.
What is the correct way to use within to limit the scope of my select?
Thank you.
/Salernost
Still learning Capybara myself, but have you tried have_link instead of have_selector? Also I don't think you need |ref|. For example:
lock = false
within('tr', :text => 'Team 3 Name') do # omit |ref|
page.should have_link('Deactivate')
page.should_not have_link('Deactivate')
lock = true
end
lock.should be_true
Update October 13, 2012
Having come a little further with Capybara, I see several potential issues here:
within may silently ignore the text field. You'll notice that the examples only show CSS or XPath finders without additional arguments.
If within does use text, it may not work here because you are asking it to look at the <tr>, but the text is in the <td>.
It's quite possible that the page subject still targets the entire page even if you are in a within block. The within examples are mostly about using fill_in or click. The exception is the example under Beware the XPath // trap.
As for creating a within block, you can either give your table rows unique ids and search for them using CSS, or you may be able to write a specific XPath targeting the first matching row.
The problem with the latter is that you want use the within on the <tr>, but the text you are using for your targeting is inside a <td> subelement. So for example, this XPath should find the table cell containing the text Team 3 Name but then you are only working within that first cell, not the whole row.
within(:xpath, "//tr/td[normalize-space(text())='Team 3 Name'") do
There are ways to "back up" to a parent element using XPath but I don't know how to do it and I've read that it's not good practice. I think your best bet here might be to just generate ids so your rows start like this:
<tr id="team_3">
then target them with a simple
within("tr#team_3")
I would also recommend Mark Berry's final approach he mentioned of adding id's to each of your table elements.
<tr id="team_3">
then target with
within("tr#team_3")
Capybara has given me issues when selecting by xpath in that it doesn't seem to work consistently, especially with CI services.
I also want to note on the same answer this section:
It's quite possible that the page subject still targets the entire page even if you are in a within block. The within examples are mostly about using fill_in or click. The exception is the example under Beware the XPath // trap.
This may have been the case in an older version, but in the current version of Capybara, calling page inside of a within block only inspects the part of the page targeted. So, using Mark's above example:
within("tr#team_3") do
expect(page).to have_content 'Team 3 Name'
# => true
expect(page).to have_content 'Team 4 Name'
# => false
end
have_selector seems to ignore :text and :content options. I had to use something like this instead:
within 'a' do
page.should have_content 'Deactivate'
end
The solution is to not use within method:
expect(page).to have_css('tr#team_3') do
without_tag('a', text: 'Deactivate')
end

Capybara, finding within a css element

I'm working with Ruby on Rails 3, Cucumber, and Capybara
I've been searching for quite some time, and I can't figure out how to find a specific page element within a css tag. In my case, I need to make sure that a name is found inside of a table, and not in the "Welcome [Name]".
I tried something like:
within('table') do
page.body.index("[Name]")
end
And I have a table with id='table'.
But I'd like to know how to do this for any css element, such as:
within('h2') do
page.body.should have_content ('stuff')
end
I think my problem has to do with page.body, but I'm not sure how to contain it to a particular css tag.
Thanks in advance!
Capybara's within matcher only matches the first result, so if you have multiple h2 tags, it'll only look in the first one.
Instead, try have_css with the :text option.
page.should have_css("#table", :text => "[Name]")
page.should have_css('h2', :text => 'stuff')
To find a specific element:
page.find('#table').should have_text('stuff')
I guess all answers should work but now Capybara doesn't use should anymore it uses expect
expect(page).to have_css("#table", :text => "[Name]")
expect(page).to have_css('h2', :text => 'stuff')
I've done stuff like this:
page.all(:css, 'button.btn-primary.days.active').size.should == 1
To check if there are any elements that contain a specific set of classes.
I didn't need to look for a particular text value of the element though.
I just wanted to ensure the existence of the element, and how many of them there were.

Resources