Capybara, checking HTML element by ID and Class - capybara

Two questions from a beginner.
Q1- Is it possible to assert the existence of an HTML node by ID and class?
For example, to see if the following element exists:
<div class="drawer" id="first"....>
I've seen you can use something like:
page.should have_css('div.drawer')
page.should have_css('div#first')
but can we somehow query for the existence of both parameters, I've tried the following and didn't work:
page.should have_selector("div", :class => "drawer", :id => "first")
Q2- Is it possible to add 2 selectors to the 'within' capybara method, ie, I've seen you can limit the scope by doing:
within("//div[#id='first']") do
but can we filter that DIV by adding id='first' and class='drawer' somehow?
Many thanks!

You can combine the selectors.
For your first question, the following checks for a div with id "first" and class "drawer":
page.should have_css('div#first.drawer')
For your second question, the within block can use the same css-selector as above:
within('div#first.drawer') do
Or if you really prefer xpath, you can do:
within("//div[#id='first' and #class='drawer']") do
A good reference for css-selectors: http://www.w3.org/TR/CSS2/selector.html

Related

RSpec HtmlMatchers have_tag: can we use regular expression to match HTML attributes?

Trying to spec for a part of value of the the src attribute in an img tag rendered by one of my views, I was hoping to use the following syntax:
is_expected.to have_tag 'img', with: {src: /thumb_product\.png/}
It does not work, which is weird because the expression seems to be correctly interpreted by nokogiri (see the (?-mix:...) criteria)
expected following:
<div class='active uploaded_image'>
<img alt="Thumb product" src="/uploads/tmp/1404970363-22080-9933/thumb_product.png" />
</div>
to have at least 1 element matching "img[src='(?-mix:thumb_product\.png)']", found 0.
So I wonder if I forgot something, or if it's simply not possible. Thanks in advance
Manually, to test that src string ends with the expected name of the file:
is_expected.to have_tag 'img[src$="thumb_product.png"]'
For anyone looking into matching tags' text with a regular expression:
According to RSpecHtmlMatchers#have_tag's documentation (https://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers:have_tag), you can pass a regular expression to the have_tag method.
Example:
is_expected.to have_tag 'img', /^something(.*)in\-the\-text/
And, even though it's not explicit in the documentation, you can also match attributes of tags with regular expressions, for example:
is_expected.to have_tag 'img', :with => {:class => /some\-class\-(one|two)$/}
Not sure why it didn't work for the original poster with the src attribute. It does work for the class attribute at least.

Capybara+Rspec how to match a part of tag attribute

I have a page with several links like that <a href='/bla/bla/bla/?page=xxx>text</a>
I want to match certains xxx values links using Capybara and RSpec, i don't care about bla/bla/bla part of href attribute.
page.should have_selector("div.class ul li a", :href => "page=2")
doesn't work,
also
page.should have_xpath("//a[#href='page=2']")
is not an option because i don't know the full href attribute value.
PS: also didn't find any complete Capybara API documentation just to get all available methods and parameters' description. I there such thing?
Try using contains:
page.should have_xpath "//a[contains(#href,'page=2')]"
Try this:
link = page.find('div.class ul li a')
link[:href].should match(/page=2/)
More information here.

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.

Finding a label with Webrat that contains a link

So I'm doing BDD with Cucumber and have a form with checkboxes populated from a database. The labels for the checkboxes contain hyperlinks. So far, not too exotic (note, this is HAML and not Erb, but it should be readable enough for any Rails person):
I would like my donation to support:
%br
- for podcast in #podcasts
= check_box_tag "donation[podcast_ids][]", podcast.id, true
= donation.label "donation[podcast_ids][]", link_to(podcast.name, podcast.url), :value => podcast.id
%br
The problem is that in my Cucumber features, I can't figure out how to find that checkbox to check it. The relevant part of the story is this:
Scenario: Happy path
Given I am on the home page
When I fill in "My email address" with "john#example.org"
# Skipped for brevity...
And I check the "Escape Pod" podcast
And I check the "PodCastle" podcast
And I press "I'm ready!"
Then I should see "Thank you!"
And there should be 2 podcast donation records
If I'm using the bare webrat_steps.rb file I get the following error:
Could not find field: "Escape Pod" (Webrat::NotFoundError)
I'm quite certain it's because of that link_to() method, which I'm using to make "Escape Pod" a hyperlink to the actual Web site. But I can't easily access link_to from my Cucumber step, and I can't figure out any reasonable way of pointing Webrat at the right checkbox short of kludging up a whole bunch of hyperlink code in my step (which makes it very brittle).
My BDD is stalled at this point. I don't want to take out the link just because it's hard to test. And it feels like it shouldn't be hard to test. Webrat is just limiting what I can pass into the checks() method. Can anyone suggest an elegant answer for this?
The short answer is the to use field_by_xpath or one of the other Webrat::Locators methods to select what element to manipulate in your step:
When(/^I check the "(.+?)" podcast$/) do |name|
check(field_by_xpath("//label/a[.=#{name}]")
end
You might need to play with that xpath a little, or use field_by_id instead. Remember it is looking got the html id of the tag not the id from the database.
Can you post what your HTML looks like in the rendered page near the problematic checkbox(es)? Sometimes you have to play with naming the field... I had all sorts of trouble with a login form... I ended up doing this:
<%= submit_tag 'Enter', {:id => "login_button"} %>
So that the following worked:
Given /^I am logged in as admin$/ do
visit login_path
fill_in "login", :with => "admin"
fill_in "password", :with => "password"
# click_button "login_button"
click_button
end
I know it's not a checkbox example, but maybe fiddling with your name/id/etc will work

Resources