Unable to find css "ul#items li:first" (Capybara::ElementNotFound) - ruby-on-rails

I have inherited an Rails app, but am unfamiliar with this particular test environment. When the cucumber tests are run we get:
Scenario: Add only an image to a profile by url # features/add_an_image_by_file_url.feature:11
Given I am logged in as a user "admin" # features/step_definitions/user_steps.rb:19
And I create a profile for "Joe Blogs" # features/step_definitions/profile_steps.rb:1
And I have a fake image url "http://fake.com/images/profile.jpg" # features/step_definitions/photo_steps.rb:1
When I follow "New photo" # features/step_definitions/web_steps.rb:32
And I fill in "Url" with "http://fake.com/images/profile.jpg" # features/step_definitions/web_steps.rb:38
And I press "Create" # features/step_definitions/web_steps.rb:26
Then I should see "Photo was successfully uploaded." # features/step_definitions/web_steps.rb:99
And I should see "profile.jpg" within first profile item # features/step_definitions/item_steps.rb:18
Unable to find css "ul#items li:first" (Capybara::ElementNotFound)
(eval):2:in `find'
./features/step_definitions/web_steps.rb:13:in `with_scope'
./features/step_definitions/web_steps.rb:100:in `/^I should see "([^\"]*)"(?: within "([^\"]*)")?$/'
features/add_an_image_by_file_url.feature:19:in `And I should see "profile.jpg" within first profile item'
This come from item_steps:
Then /^I should see "([^\"]*)" within (.*?) profile item$/ do |string, filter|
Then %Q{I should see "#{string}" within "ul#items li:#{filter}"}
end
And web steps concerned:
Then /^I should see "([^\"]*)"(?: within "([^\"]*)")?$/ do |text, selector|
with_scope(selector) do
if defined?(Spec::Rails::Matchers)
page.should have_content(text)
else
assert page.has_content?(text)
end
end
end
The HTML in question look like this:
<ul id='items'>
<li class='note clearfix'>
<div class='content'>
<img src="profile.jpg"/>
</div>
</li>
</ul>
What on Earth is going on here?

Well, "what's going on here" is pretty simple:
cucumber executes each step of the scenario using matched step definition
everything goes ok, until
it executes "I should see "profile.jpg" within first profile item"
during this step, cucumber goes to the matched step definition in item_steps
that definition in item_steps calls another step definition from web_steps
this last one checks if the page does really have the given content inside the given scope
and the check fails...
I suppose, the interesting question is "why it doesn't work". Very similar css selector works fine for me when I used it in my cucumber/capybara scenario.
Are you sure the HTML code is really displayed in the browser? There is no conditions in template or in controller which could prevent it from being displayed? What happens when you change 'ul#items li:first' to something more simple, say just 'ul#items'?
Update: the problem is really very simple: profile.jpg IS NOT a text content - it's an "invisible" html code. The only error you have is scenario itself.

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')

How do I confirm a css element attribute with Capybara?

This may seem unusually basic but how do I confirm the presence of a pop up confirmation?
<a data-confirm="delete this video?" rel="nofollow" data-method="delete" href="/videos/21">Delete</a>
<a is the "tag"/"element" and data-confirm is an attribute. I want to test for the existence of the "data-confirm" attribute within the <a> element/tag
I have tried
expect(page).to have_css("a.data-confirm.delete this video?")
from
capybara assert attributes of an element
but no joy.
Edit:
I've tried the expectation from Arup's comment below
expect(page).to have_content "Content"
click_link "Delete"
expect(page).to have_css('a[data-confirm="delete this video?"]')
But it raises the following (same) error
Failures:
1) Visiting the video index page should search and save movies
Failure/Error: expect(page).to have_css('a[data-confirm="delete this video?"]')
expected #has_css?("a[data-confirm=\"delete this video?\"]") to return true, got false
but the page source shows it there and it is clearly working for the user
Any assistance would be very appreciated
You can write this expectation as:
expect(page).to have_css('a[data-confirm="delete this video?"]')
The answer by Arup is correct for the title of the question (and as he stated in the comments it's just valid CSS - https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors), however it's not actually testing the more detailed part of the question "how do I confirm the presence of a pop up confirmation". All it is doing is confirming the correct data attribute is on the link element to trigger the rails provided JS that should show a confirm.
If you wanted to actually test the confirm box is shown you would need to swap to using a JS capable driver - https://github.com/teamcapybara/capybara/tree/2.17_stable#drivers - and then use something like the following in your test
expect(page).to have_content "Content"
accept_confirm "delete this video?" do
click_link "Delete" # The action that will make the system modal confirm box appear
end
See - http://www.rubydoc.info/gems/capybara/Capybara/Session#accept_confirm-instance_method

Testing ruby with rails, Element not found

I get the error:
Capybara::ElementNotFound:
Unable to find field "user_email"
And this is the test code:
feature 'User' do
given!(:user) { User.new(email: 'testuserid#example.com', encrypted_password: 'test') }
scenario 'opens sign_up page' do
visit new_user_session_path
expect(page).to have_content 'unique text on the page'
end
scenario 'signs in with invalid email' do
visit new_user_session_path
fill_in('user_email',with: 'ssd')
expect(page).to have_content 'unique text on the page'
end
end
My HTML file consists of this code literally:
unique text on the page
<br>
<input type="text" id="user_email">
So this proves that the path is correct because my first scenario runs correctly. It is visiting the right page. But still I get this error for second scenario in fill_in.
I have also tried element = page.find("user_email"), it gives same error.
What am I possibly doing wrong?
I have been scratching my head like hell.
Usually the reason for this is that the input isn't actually visible on the page. You can verify this by doing
fill_in('user_email', with: 'ssd', visible: false)
If that succeeds in finding the element, then you need to change your test to first perform whatever actions make the field visible before attempting to fill it in.
Your code seems right. Maybe you are visiting wrong url or you have used user_email id once more. But you can give a try with alternative syntax like following :
find("input[id$='user_email']").set "ssd"

How do I find an image on a page with Cucumber / Capybara in Rails 3

I am using Cucumber / Capybara with Rails 3 and am trying to validate the existence of an image after upload. I'm not sure how to check the url of the image to validate it.
I have the following scenario:
Scenario: Create new listing
Given I am on the new listing page
When I fill in "listing_name" with "Amy Johnson Photography"
And I attach the file "features/support/test_image.jpg" to "listing_images_attributes_0_photo"
And I press "Create"
Then I should see "Amy Johnson Photography"
And I should see the image "test_image.jpg"
Everything passes except the last step.
I've tried this for my step definition, which works great if it's text on the page, but doesn't for an image url:
Then /^I should see the image "(.+)"$/ do |image|
if page.respond_to? :should
page.should have_content(image)
else
assert page.has_content?(image)
end
end
Then I've also tried something like this step definition instead:
Then /^I should see the image "(.+)"$/ do |image|
html = Nokogiri::HTML(response.body)
tags = html.xpath('//img[#src="/public/images/foo.png"]')
# tags.length.should eql(num_of_images)
end
which causes the following error:
And I should see the image "test_image.jpg" # features/step_definitions/listing_steps.rb:41
undefined method `body' for nil:NilClass (NoMethodError)
./features/step_definitions/listing_steps.rb:42:in `/^I should see the image "(.+)"$/'
features/manage_listings.feature:24:in `And I should see the image "test_image.jpg"'
I'm assuming you need a nokogiri step to find this properly. Or if there's a better way, I'm open to suggestions. How can I validate that the image I just uploaded is on the page? Thanks.
The solution with Nokogiri should work fine. The only problem is that the Cabybara API is different to Webrat, so instead of response.body, you must use page.body.
But theres an even better way to test for the image with Cabybara :
Then /^I should see the image "(.+)"$/ do |image|
page.should have_xpath("//img[#src=\"/public/images/#{image}\"]")
end
Hi I'm not good with XPATH but with CSS you can try :
if page.respond_to? :should
page.should have_selector("img[src$='#{imagename}']")
else
assert page.has_selector?("img[src$='#{imagename}']")
end
wish helps !
jramby
You can test it in this way, and also not depending on the path:
Then /^I should see the image "(.+)"$/ do |image|
page.should have_xpath("//img[contains(#src, \"#{image}\")]")
end
page.should
is now deprecated. Use instead
expect(page).to
Full example :
Then /^I should see the image "(.+)"$/ do |image|
expect(page).to have_xpath("//img[contains(#src, \"#{image}\")]")
end
Does this work?
page.should have_xpath('//img[#src="/public/images/foo.png"]')
If you have a lot of images that you would like to test you could create a has_image? matcher for Capybara.
It is easy and this post explains it step-by-step: Adding a has_image? Matcher to Capybara
Yes, but these xpath tests won't deal with the fact that...
/public/images/foo.jpg
public/images/foo.jpg
http://mydomain.com/public/images/foo.jpg
...are all the same valid link to the image.
this syntax worked for me and is more readable.
page.should have_css("img", :src => "/public/images/foo.png")

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