Using Rspec and Capybara, I'm trying to test a failing validation for a form, where a "required" input is not filled in, so it fails. New navigators understanding HTML5 provide built-in validations, and I understand Capybara is using that as well. Before, I was using
page.should have_error
which doesn't work for me anymore.
Someone knows how to test this now?
Many thanks!
David
HTML5 client side validations are tricky to find. I found this post with a great answer.
The code is:
describe "when I leave required field empty" do
it "I get an the correct html5 validation error" do
#Leave the field empty
click_on "Save" # or whichever button triggers the submit
message = page.find("#field_id_attr").native.attribute("validationMessage")
expect(message).to eq "Please fill out this field."
end
end
Basically the way it works is that the field element has an attribute called "validationMessage" and the process is:
Click submit - this triggers the error message
Get a reference to the native(html) attribute(as opposed to the Capybara page object attribute) called "validationMessage". This will give you the value or the message itself.
Assert that the value is as expected.
I am not familiar with RSpec so I am not sure about what does have_error.
You should think about what you want to test exactly.
You surely don't want to test the exact behavior (what message is displayed, and how) as it is specific to each browser. What you want to test, because this is not specific to the browser, is the fact that the form is not submitted.
For instance, for a basic html form at root, with a required radio button "My value".
# Check form can not be submitted without the radio button
visit '/'
click_button 'Submit'
assert_equal '/', current_path
# Check form can be submitted with the radio button
visit '/'
choose 'My value'
click_button 'Submit'
assert_equal '/next', current_path
You should also consider to test only the presence of required in your html code, as the browser is supposed to work as expected (test only your code, not other's code)
If there is an error message, you can something along the lines of
page.should have_content("error")
This depends on how you handle the errors, and whether you use javascript or not.
This is an old post, however I will try to answer it
have_error is a method provided by webkit, to check e.g. if ajax requests or javascript in general running fine
I use to test my validations in my model specs:
describe 'validations' do
it { is_expected.to validate_presence_of :competitor_name }
it { is_expected.to validate_presence_of :chassi }
it { is_expected.to validate_presence_of :auction }
it { is_expected.to validate_presence_of :car_template_id }
end
or like
expect(FactoryGirl.create(:customer)).to be_valid
to check if my Factory is valid.
If you need to check your notices by targeting invalid inputs, you could test the html of your notice by capybara with the following:
it 'searches for specific order_car by chassi and model' do
visit order_cars_search_detailed_path
fill_in 'order_car_chassi', with: '123456'
select 'Octavia', from: 'order_car_car_template_car_template_id'
click_button 'Search Order'
expect(page).to have_content('THIS IS MY NOTICE')
expect(page).to have_content('123456')
end
Hope I could help some others running into this question.
Related
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')
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
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"
I've got a User resource where :name is a required attribute on the model.
If I try to create a new user without a name, then the validation fails and the error messages are displayed at the top of the form as expected, but the URL of the page changes from /users/new, to /users?
I hadn't noticed this behaviour until tonight when I started playing around with capybara for the first time, and was expecting the current_path after a validation failure to be http://localhost:3000/users/new
I couldn't figure out why my spec was failing:
it 'should not create an invalid user' do
fill_in "Name", :with=>""
click_button "Create User"
current_path.should == new_users_path
end
I've verified that it happens in all my other rails apps, so I realise this is the way rails works, but I really don't get what's going on here. Why does it work like this? Why does the path change from new_users_path to users_path when validations fail?
This has confused me immensely
It's perfectly normal.
In a basic CRUD, you're creating your users using a POST request to /users.
If validation fails, you just render the edit view, but it doesn't change the url.
To change the url, you should redirect_to but, this way you'd loose the info related to the performed validation.
I'm using tabnav plugin for Rails and I want to use rpsec to make sure it highlights properly.
describe 'account navigation links' do
it 'should have account settings link' do
get '/account/settings'
response.should have_tag("li", :text => "Account Settings")
end
it 'should be highlighted' do
get '/account/settings'
response.should have_tag("li", :color => "Account Settings")
end
end
However the above code doesn't seem to work. I'm using webrat with rspec btw. Any help? Thanks.
The only real thing to be testing here is whether or not a particular class name is applied, if highlighting comes from a class name. If so, you could do have_tag("li.highlighted", :text => "Account Settings").
Otherwise, you probably should not be automating your testing for whether or not the CSS selectors themselves are applied correctly. This is a purely presentational detail, and it isn't really what a test suite is designed to test. I suspect that Webrat doesn't bother to go through and apply your stylesheet for you, so testing that detail isn't feasible, not to mention that you could check with just one page load whether or not it's working - after all, you are arguably testing your stylesheet as you design it.
Anyway. Your question doesn't really make clear what you're really trying to test for, but you shouldn't be testing presentation, anyway. Testing the structure of the HTML document is good, but confirming how the client program interprets the document is the role of a designer, not a programmer. (If you wear both hats, so be it, but don't go mixing your foods.)
describe 'highlighting' do
it 'should highlight account/settings' do
get '/account/settings'
response.should have_tag("a.active[href=?]", account_settings_path, /Account Settings/i)
end
it 'should highlight account/profile' do
get '/account/profile'
response.should have_tag("a.active[href=?]", account_profile_path, /Profile Information/i)
end
it 'should highlight account/picture' do
get '/account/picture'
response.should have_tag("a.active[href=?]", account_picture_path, /Profile Picture/i)
end
it 'should highlight account/notifications' do
get '/account/notifications'
response.should have_tag("a.active[href=?]", account_notifications_path, /Notifications/i)
end
it 'should not highlight Profile' do
get '/account/profile'
response.should_not have_tag("a.active[href=?]", account_settings_path, /Account Settings/i)
end
it 'should not highlight Notifications' do
get '/account/profile'
response.should_not have_tag("a.active[href=?]", account_notifications_path, /Notifications/i)
end
it 'should not highlight Picture' do
get '/account/profile'
response.should_not have_tag("a.active[href=?]", account_picture_path, /Profile Picture/i)
end
end
You could write more test, especially for "doesn't highlight on wrong action" scenarios, but I think this is good enough.
If you're using Sass you can parse it with the Sass parser:
root = Sass::SCSS::Parser.new('.error { color: red; }', 'example.scss').parse
It returns a parse tree you could test by diving into it. For example:
prop = root.children.select {|child| child.rule.flatten.include?('.error')}.first
prop_strings = prop.children.map {|p| [p.name.flatten.first, p.value].join(':')}
prop_strings.should include?('color:red')