In my ruby on Rails 4.2 app, on a page I have a conditional back end rule that translates into the page front end in a change in class
<div id="content">
<i class='<% if x=true %>glyphicon glyphicon-thumbs-down<% else> glyphicon glyphicon-thumbs-up ><% end %>' </i>
this is the message.
</div>
How to check presence with rspec 3/capybara that the page contains the class glyphicon-thumbs-down OR the class glyphicon-thumbs-up ?
I tried the code below but it fails:
it "should sheck one of the 2 classes presence" do
expect(page).to have_css '.glyphicon-thumbs-down' || expect(page).to have_css '.glyphicon-thumbs-up'
end
I am getting the following error message:
syntax error, unexpected tIDENTIFIER, expecting keyword_end (SyntaxError)
Multiple OR-ed css selectors can be specified separated by a comma. Try the following:
it "should sheck one of the 2 classes presence" do
expect(page).to have_css '#content i.glyphicon-thumbs-down,#content i.glyphicon-thumbs-up'
end
(I added the #content and i selectors so that the query is more specific.)
However, instead of doing this I would recommend trying to make the test behave in a precisely defined way and test for just a single class in the spec. Have a look at this SO question and its answers for various ways to stub or preset the random number generator in tests.
Firstly, you're checking for a class name so you need a . in front of the class names to make it a CSS class selector. Then, you could use the RSpec or matcher combinator
expect(page).to have_css('.glyphicon-thumbs-down').or(have_css '.glyphicon-thumbs-up')
but it has the downside of the first one retrying/waiting for Capybara.default_max_wait_time seconds before checking the second. You could specify a 0/false wait time if you know the page is already loaded and therefore don't need retrying/waiting
expect(page).to have_css('.glyphicon-thumbs-down', wait: false).or(have_css '.glyphicon-thumbs-up', wait: false)
However, it's probably fine to just check for either element using the normal CSS ,
expect(page).to have_css('.glyphicon-thumbs-down, .glyphicon-thumbs-up')
Maybe you can try to use option :count, in expect method like this:
it 'should contains 2 same selector' do
expect(page).to have_css '.some-class', count: 2
end
Your error is from this line:
expect(page).to have_css 'glyphicon-thumbs-down' || expect(page).to have_css 'glyphicon-thumbs-up'
You just need to add some parens then it will be valid syntax:
expect(page).to(have_css('glyphicon-thumbs-down')) || expect(page).to(have_css('glyphicon-thumbs-up'))
that won't fix your issue though, because if the left condition fails then rspec will exit and not run the second half.
A working approach could be to evaluate the condition to a boolean variable, then pass it to a single rspec expectation. Doing this requires using the core Capybara method has_css to test for css presence, not have_css? from the rspec matchers package:
selectors = ['.glyphicon-thumbs-down', '.glyphicon-thumbs-up']
glyph_exists = selectors.any? do |selector|
page.has_css? selector
end
expect(glyph_exists).to be true
Note also that I've added . to the selector strings which is necessary since it's a css class.
Related
I am newbie in testing and using RSpec and need some help:
I have shared example group:
shared_examples_for 'request which do something' do |opts = {}|
respond.should redirect_to(opts[:redirect_to])
end
In my spec file:
describe "behavior" do
it_should_behave_like 'request which do something', :redirect_to => root_path
end
Looks great, but I get this error:
Exception encountered: #<NameError: undefined local variable or method `root_path' for #<Class:0x000000069e2838>>
and it points on line with 'it_should_behave_like ... '
I tried to include Rails.application.routes.url_helper in spec_helper, but it doesn't work anyway.
By the way it perfectly works from example like this:
describe "behavior" do
it "should redirect" do
response.should redirect_to(root_path)
end
end
(even without explicit including url_helpers)
Thanks for any help.
You can't use path helpers within example groups, but there is a workaround. See this answer:
Passing a named route to a controller macro in RSpec
With this, you can pass a symbol and use send.
In example groups, write Rails.application.routes.url_helpers.root_path
instead of just root_path. You could create your own helper method to
make repeated calls short.
I have a still pretty simple Rails application that I want to develop using BDD with Cucumber and TDD with RSpec. Currently, I am hanging at a test where I want to check that if a new instance of an Organizer (that's the model I have) cannot be created due to a validation error. I would like to check that the errors Array of the object to be created is not empty so that I can be sure that error messages are available for showing them in the view.
require 'spec_helper'
describe OrganizersController do
render_views
describe "POST 'create'" do
describe "with invalid arguments" do
before(:each) do
request.env["HTTP_REFERER"] = organizers_new_path
#organizer_args = { :name => "" }
end
it "should return a non-empty list of errors" do
post 'create', :organizer => #organizer_args
#organizer.errors.empty?.should_not be_true
end
end
end
end
I am developing based on Rails 3.2.9 with RSpec 2 and cucumber-rails.
Any suggestions are appreciated. Thanks!
You should use assigns method to get instance variable from controller action:
assigns(:organizer).errors.empty?.should_not be_true
The latest preferred syntax is:
expect(assigns(:organizer).errors.empty?).to_not be_true
thanks for the answer guys but I'd like to suggest a slightly nicer syntax:
expect(assigns(:organizer).errors).to_not be_empty
(unrelated to the question 👇)
Basically whenever you have a method that ends with ? you'll have the corresponding rspec matcher that starts with be_ e.g.
1.odd? #=> true
expect(1).to be_odd
I am trying to write an integration test where if a user clicks on a button, it creates a new record in the database (CheckPrice model).
I am running into the error nil is not a symbol when I try to run my test.
require 'spec_helper'
describe 'CheckPrice', type: :request, js: true do
it "should create a new CheckPrice record when user clicks Check Price on topic page" do
city = create :city
hotel = create :hotel
affiliate_link = create :affiliate_link
visit '/hotel-bilboa-hotel'
sleep 2
click_button "Check Prices"
response.should change(CheckPrice.count).by(1)
end
end
When "Check Prices" is clicked, there is an event listener that triggers the new method in the checkprices_controller.
The error seems to occur on the last line response.should change(CheckPrice.count).by(1). It looks like the method does not recognize the model CheckPrice. How do I reference the CheckPrice table?
Thanks.
I don't think you can use the change matcher like this on the response object. Try this:
expect {
click_button "Check Prices"
}.to change{ CheckPrice.count }.by(1)
This makes more semantic sense, too, IMO.
See this cheat sheet for more examples.
Semantic aside, to answer the original question (getting "nil is not a symbol") and help other people who might land here like I did: make sure to use curly brackets {} instead of parentheses ().
So (correct)
response.should change{CheckPrice.count}.by(1)
response.should change(CheckPrice, :count).by(1)
instead of (won't work, a mix of the 2 above)
response.should change(CheckPrice.count).by(1)
Edit:
Same answer with recommended expect syntax
So (correct)
expect{response}.to change{CheckPrice.count}.by(1)
expect{response}.to change(CheckPrice, :count).by(1)
instead of (won't work, a mix of the 2 above)
expect{response}.to change(CheckPrice.count).by(1)
Another way to do this would be:
expect do
click_button "Check Prices"
end.to change(CheckPrice, :count).by(1)
Which indicates that the output of the count method on CheckPrice is what is supposed to be changing. When two parameters are passed to change, one is assumed to be a receiver, the other a symbol to send.
I ran into the same problem, the case is, as the other answers says, that both the methods expect and change, in this case, expect a block as parameter.
So, in rails, you can use either { } or do end syntaxes.
I'm trying to match a class attribute using Capybara with RSpec.
I can very clearly see the elements and their classes but no matter what I try, Capybara seems to know nothing about the classes. But it can find id's no problem. Any clues, please? https://gist.github.com/1428472
visit "/admin/staff?mobile=1"
page.should have_selector("ul") #works
page.should have_selector("body#page") #works
page.should have_selector("html.ui-mobile") #fails
page.should have_selector("body.ui-mobile-viewport") #fails
save_and_open_page # this launches the page so I can see it and verify these attributes are indeed there.
The html:
< html class="ui-mobile" >
< body class="ui-mobile-viewport" id="page" >
I also set a breakpoint and did this stuff which also didn't work right.
#works
p find('body')[:id]
p find(:xpath, '//body[#id="page"]')
# doesn't work
p find('body')[:class]
p find(:xpath, '//html[#class="ui-mobile"]')
What's going on?
UPDATE: It turns out that it does actually work, however the problem here is that the html displayed by save_and_open_page differs from what capybara sees. When I break right after save_and_open_page and puts page.html, it's different. It's generally the same but a bunch of class attributes are gone as well as some other stuff. Very odd.
I'm very rigorous when it comes to my HTML markup and I follow a strict coding convention for forms, lists, etc...
I would like to include reusable test in my RSpec tests that would allow for me call a form test from any other test and target it directly to the page or URL that I'm testing.
Something like this:
# spec/helpers/form_tester.rb
describe FormTester
it "should check to see if all the text fields have an ID prefix of 'input-'" do
... #form should be valid ...
should be true
end
end
# spec/requests/user_form.rb
describe UserForm
it "should validate the form" do
#form = find(:tag,'form')
# call the FormTester method
end
end
Any ideas on how todo this? I'm using Rails 3.1, with RSpec, Capybara and FactoryGirl.
Use shared examples. In you case, something like this may work:
# spec/shared_examples_for_form.rb
shared_examples 'a form' do
describe 'validation' do
it 'should be validated' do
form.should_be valid
end
end
end
# spec/requests/user_form.rb
describe UserForm
it_behaves_like 'a form' do
let(:form) { find(:tag, 'form') }
end
end
It's also possible to pass parameters to shared examples and place the shared examples inside spec/support. Just have a read at the documentation.
Shared examples are great, but you've got at least two serious problems here.
First: why are you giving your form fields IDs at all? You've already got perfectly good selectors: just use input[name='whatever']. And even if you are giving them IDs, don't put a prefix on them: input#whatever or just #whatever is probably a more sensible selector in your CSS than #input-whatever. By being overspecific on your selector names, you're most likely making your CSS and JavaScript harder to write than they have to be.
Second: don't use RSpec to test your views. RSpec is at its best when confined to models. Cucumber is better for anything user-facing.