Rspec testing view test fails but HTML is OK? - ruby-on-rails

With Rspec, I'm testing the presence of the logo in the navnar:
#spec/views/_header_spec.html.erb
require 'spec_helper'
describe "layouts/_header.html.erb" do
subject{rendered}
it "should have the clickable logo" do
render
should have_selector("img")
should have_link("/")
end
end
This is my generated HTML:
<a href="/" class="navbar-brand">
<img alt="Logo" src="/assets/logo.png">
</a>
The page is OK, but the test fails:
$rspec spec/views/_header_spec.rb
F
Failures:
1) layouts/_header.html.erb should have the clickable logo
Failure/Error: should have_link("/")
Capybara::ExpectationNotMet:
expected to find link "/" but there were no matches
# ./spec/views/_header_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.13914 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/views/_header_spec.rb:7 # layouts/_header.html.erb should have the clickable logo
Randomized with seed 37707
The test fails, but the HTML behaviour page is correct, so I think my test is not working properly. Can you help me?

Replace
should have_link("/")
With
should have_link("Logo", href: "/")
have_link takes the display text of the link or alt attribute value of an image, and using the href option you can specify the corresponding path.

Related

Rails, Capybara - click_link on remote links doesnt work

I'm using Capybara to test my project. But i have a problem.
I have some remote forms on my project. They add records via ajax. When i'm testing with capybara it works well on development environment. It visits the page, fills in the form and submits. Booom, record has been added and test didnt fail.
But when i run rspec with test environments i'm getting unknown format exception.
1) add new address user adds new address
Failure/Error: find("input[value='Adres Ekle']").click
ActionController::UnknownFormat:
Account::AddressesController#create is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
# ./spec/features/user_add_new_address_spec.rb:28:in `block (2 levels) in <top (required)>'
I've also tried to respond via js from controller like;
def create
request.format = :js
end
Then it returns;
1) add new address user adds new address
Failure/Error: find("input[value='Adres Ekle']").click
ActionController::UnknownFormat:
Account::AddressesController#create is missing a template for this request format and variant.
request.formats: ["text/javascript"]
request.variant: []
# ./spec/features/user_add_new_address_spec.rb:28:in `block (2 levels) in <top (required)>'
And my scenario if u want more info;
scenario 'user adds new address' do
expect(page).to have_content 'Kayıtlı Adreslerim'
find("a[title='Adres Ekle']").click
expect(page).to have_content 'Yeni Adres Ekle'
expect(page).to have_content 'Adres Başlığı'
fill_in 'address[name]', with:'Izmir Ofisi'
select('Izmir', :from => 'address[city_id]')
fill_in 'address[address]', with: 'Lorem ipsum dolor sit amet.'
find("input[value='Adres Ekle']").click # It submits remote: true form.
expect(page).to have_content 'Success!'
end
PS: my create action doesnt render something like that.
its like;
def create
#new_address = Address.new
#address = #current_account.addresses.new(address_params)
if #address.save
#check = true
else
#check = false
end
end
it renders: create.js.erb
<% if #check %>
if($('.addresses').length) {
$('.addresses').append('<%= j(render('account/addresses/address', address: #address)) %>');
}
if($('#did-addresses').length){
$('#did-addresses').append("<%= "<option selected='true' value='#{#address.id}'>#{#address.name}</option>".html_safe %>").selectpicker('refresh');
}
$('#new-address').html('<%= j(render('account/addresses/form', new_address: #new_address)) %>');
swal({
type: 'success',
title: "<%= t('response.success') %>",
text: "<%= t('flash.actions.create.notice', resource_name: Address.model_name.human) %>",
timer: 2000
});
quickview.close('#new-address');
<% else %>
<% #address.errors.each do |error| %>
<% end %>
<% end %>
$('.preloader').fadeOut();
I was facing the same case in rails 6 but I fixed it be adding js: true to the scenario and it automatically worked well.
scenario 'Should delete the feature', js: true do
# Your logic
# Your expectations
end
Since copying your development config over your test config fixed your issue, it sounds like you probably an error in one of your JS files. Normally in the test and production environment all of your JS assets get concatenated into one file which means an error in any one of them can prevent the code in the others from being executed. In the development environment each JS file is loaded separately which means an error in any file can only affect the rest of the code in that file. Check your the console in your browser for any JS errors when going to the page in question and fix them.

How to give a unique id to a test object in Rspec?

context 'with dish available to pre-order' do
let!(:dish) { create :dish, chef: chef }
let!(:dish2) { create :dish, chef: chef2}
it 'should show dish' do
Time.zone = 'Asia/Kuala_Lumpur'
t = Time.zone.local(2016, 7, 25, 20, 30, 0)
Timecop.travel(t)
visit '/'
set_delivery_time
select_delivery_area_by_chef dish.chef
visit '/preorders'
expect(page).to have_content('PREORDER MENU')
within('div#dishes-container') do
expect(page).to have_xpath("//img[#src=\"#{dish.image.small}\"]")
expect(page).to have_content(dish.description)
expect(page).to have_content(dish.name)
expect(page).to have_content(dish.price)
expect(dish).to have_content("Add to Cart")
expect(dish2).to have_content("Pre-Order")
expect(current_path).to eq(preorders_path)
end
end
1) Visitor can see preorders with dish available to add to cart, preparation time over 1 hour should show dish
Failure/Error: expect(dish).to have_content("Add to Cart")
expected to find text "Add to Cart" in "Dock Bogisich"
# ./spec/features/visitor/preorders/list_of_preorders_spec.rb:129:in `block (4 levels) in <top (required)>'
# ./spec/features/visitor/preorders/list_of_preorders_spec.rb:124:in `block (3 levels) in <top (required)>'
I am trying to get rspec to check if specifically dish has 'add to cart' content and dish 2 to have 'pre-order' content. How can I get Rspec to check it? Is it possible to give them both a unique id and call it out here?
In your view assign ids to both the buttons. Something like:
<button id="add_to_cart-{dish.id}"> ADD to Cart </button>
<button id="pre-order-{dish2.id}"> Pre-Order </button>
Than in spec:
add-cart-btn = "button#add_to_cart-#{dish.id}"
with_in add-cart-btn do
page.should have_content 'Add to Cart'
end
Same for the Pre-order!

Capybara not finding signout link by id

For a rails app I'm currently working on I recently changed the design somewhat so that the signout link no longer has the anchor text "Sign out" but instead a glyphicon from twitter bootstrap. the html for link now looks like this:
<a class="btn btn-primary btn-sm" data-method="delete" href="/users/sign_out" rel="nofollow">
<span class="glyphicon glyphicon-log-out"></span>
</a>
meanwhile my capybara test looks like this:
context "when not logged in" do
it 'cannot create wikis' do
#free_user = create(:user)
login_as(#free_user, :scope => :user)
click_link "Sign out"
visit root_path
expect(page).to_not have_link('Create wiki')
end
end
Now that the text "Sign out" is no longer there, I need a new way to identify the link. Checking the documentation for capybara (or rather this handy cheatsheet), it looks like I can supply either the text of the link or its id. So I tried giving it an id:
<a class="btn btn-primary btn-sm" data-method="delete" href="/users/sign_out" id="signout" rel="nofollow">
<span class="glyphicon glyphicon-log-out"></span>
</a>
So now it's got an id of "signout" however when I make this change to the test, it still won't pass.
1) Standard (free) User when not logged in cannot create wikis
Failure/Error: click_link "signout"
Capybara::ElementNotFound:
Unable to find link "signout"
# ./spec/features/standard_user_role_spec.rb:107:in `block (3 levels) in <top (required)>'
I tried making sure that I was still a logged in user in the test by creating and logging in the user as seen above and by adding a check that the html on the page contains Hello, since it says "Hello" and the name of the user when the user is logged in:
expect(page).to have_content('Hello')
This gave me another error that I don't understand:
1) Standard (free) User when not logged in cannot create wikis
Failure/Error: expect(page).to have_content('Hello')
Capybara::ElementNotFound:
Unable to find xpath "/html"
# ./spec/features/standard_user_role_spec.rb:107:in `block (3 levels) in <top (required)>'
So what could be going on here?
Full spec available here
You can re-add the text but hide it from all but screen readers:
<a class="btn btn-primary btn-sm" data-method="delete" href="/users/sign_out" id="signout" rel="nofollow">
<span class="sr-only">Sign out</span>
<span class="glyphicon glyphicon-log-out"></span>
</a>
This also improves accessibility somewhat.
context "when not logged in" do
it 'cannot create wikis' do
#free_user = create(:user)
login_as(#free_user, :scope => :user)
visit root_path
click_link "Sign out", visible: false
expect(page).to_not have_link('Create wiki')
end
end
Note that we explicitly tell Capybara to look for hidden text with the visible option.
For the applications, which use JS, that means the request is being sent using for example json format, and is begin triggered by click on the link. (suppose the HTML markup is the same). It is useful the following call:
find('Sign Out', visible: false).trigger('click')
or by id (if you'll add it):
find(:css, "#sign-out", visible: false).trigger('click')

Failing Spec for button

So I recently took on a project upgrading a Ruby 1.9.3 / Rails 3.2 application to Ruby 2 / Rails 4.0.2. All seems to be well, but I have a failing spec. I'm trying to test that a button is working at the bottom of the page for a "suggestions" box.
Failure message is:
Failures:
1) StaticPages Scheduler Page after authenticating filling in the suggestion form after submitting the form should send an email request with the form contents
Failure/Error: click_button("suggestion-button")
ActionController::UnknownFormat:
ActionController::UnknownFormat
# ./app/controllers/static_pages_controller.rb:39:in `suggestion'
# ./spec/features/static_pages_spec.rb:93:in `block (6 levels) in <top (required)>'
Spec reads:
context "after submitting the form" do
before(:each) do
click_button("suggestion-button")
end
Controller reads:
def suggestion
if user_signed_in?
user = current_user.email
else
user = "< not logged in >"
end
if params[:suggestion]
ContactMailer.suggestion_email(params[:suggestion], params[:pathname], user).deliver!
end
respond_to do |format|
format.json { render json: params[:suggestion] }
end
end
The source for the button on the page is:
<button type="submit" class="btn" id="suggestion-button">
Tell Us!
</button>
</form>
<script>
// After form submission, gray out and disable the form
$(document).ready(function(){
$("#footer-suggestion-form").bind("ajax:complete", function(event, xhr, status){
console.log($("#footer-suggestion-form input"));
$("#footer-suggestion-form input").attr("disabled","disabled");
$("#footer-suggestion-form button").attr("disabled","disabled").html("Thanks!");
console.log("did it!");
});
$("#suggestion-pathname").val(location.pathname);
});
</script>
Is it because it's an AJAX form? Or just a test that worked in Rails 3.2 but no longer works in 4 because of some change? I appreciate any help, I am truly lost.
I did try using :js => true in the spec and downloading the capybara-webkit gem, but when I try to run the spec it gets to that test and then just hangs there waiting for something to happen that never does.
EDIT: Progress. Now it gets to that test, sits for a big, and then fails with
1) StaticPages Scheduler Page after authenticating filling in the suggestion form after submitting the form should send an email request with the form contents
Failure/Error: before { visit scheduler_path }
RuntimeError:
Rack application timed out during boot
./spec/features/static_pages_spec.rb:31:in `block (3 levels) in

Rails Capybara not seeing fields

I feel like I'm going insane here. Doing a routine capybara test of my Post model. Specifically, making sure the new page redirects to the newly created post after a valid submission. So in my spec/requests folder, I've got this:
describe "Valid post submission --" do
it "should log in a user and redirect to new post" do
# Sign in works find, so I won't reproduce it
# Make a blogpost
Post.destroy_all
fill_in :name_here, with: "Post Name Here"
fill_in :content_here, with: "This is the content of a really short post."
screenshot_and_open_image
click_on "Post It"
screenshot_and_open_image
puts current_path
page.should have_selector 'h2', text: "prohibited this post"
page.should have_selector 'h1', text: "Post Name Here"
page.should have_selector '.alert', text: "Post was successfully created."
page.should have_selector 'title', text: full_title('Post Name Here')
end
The screenshots and puts are there to clarify what's going on. There are basically two different cases. In
I just leave the fields be and test for the default IDs ("post_name"
and "post_content"); obviously the above test is for case two:
I change the field ids to "name_here" and "content_here" to test if
the label names or anything else is interfering. That's the one the spec of which is
above.
In case 2 the post is getting rejected because Capybara can't find either field.
In case 1, only the content would be empty, but the name field would be filled with the content.
The precise error in case 1 is:
2) Posts Invalid post - should refresh with error if content empty
Screenshot: /Users/myusername/rails/appname/tmp/capybara/screenshot_2013-03-14-22-37-36.634.png
Failure/Error: fill_in :post_name, with: "Post Name Here"
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'post_name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:43:in `block (3 levels) in <top (required)>'
Case 2 doesn't throw an error about not finding the appropriate content, I believe because the label has that id as well.
This is ridiculous, because I took a snapshot of the HTML durin the test, just before it errors. Here's case 2 -- both cases look 90% the same here, apart from that ID difference:
<form accept-charset="UTF-8" action="/posts" class="new_post" id="new_post" method="post">
<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
<div class="field">
<label for="post_name">Name</label>
<br><input id="name_here" name="post[name]" size="30" type="text">
</div>
<div class="field text-area">
<label for="post_content">Content</label>
<br><textarea cols="50" id="content_here" name="post[content]" rows="20"></textarea>
</div>
<div class="actions btn-group">
<input class="btn" name="commit" type="submit" value="Post It">
</div>
</form>
Note the clear existence of the post_content field. In cases where both fail (Case 1), there are both content_here and name_here fields. So the fields are there. And capybara is generally working (this sort of thing works find in other parts of my app). And the problem isn't a conflict with the label name, because putting a different id on the input fields doesn't help capybara find them.
By the way, this all works perfectly in reality.
Any idea at all what's going on here? I'm super frustrated/clueless.
UPDATE -- Following user2172816's advice, I changed my HAML so the HTML looks like this:
<form accept-charset="UTF-8" action="/posts" class="new_post" id="new_post" method="post">
<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
<div class="field">
<label for="post_name">Name</label>
<br><input id="name" name="post[name]" size="30" type="text">
</div>
<div class="field text-area">
<label for="post_content">Content</label>
<br><textarea cols="50" id="content" name="post[content]" rows="20"></textarea>
</div>
<div class="actions btn-group">
<input class="btn" name="commit" type="submit" value="Post It">
</div>
</form>
The test now looks like this:
# Make a blogpost
Post.destroy_all
fill_in "Name", with: "Post Name Here"
fill_in "Content", with: "This is the content of a really short post."
page.should have_selector 'h2', text: "prohibited this post"
page.should have_selector 'h1', text: "Post Name Here"
page.should have_selector '.alert', text: "Post was successfully created."
page.should have_selector 'title', text: full_title('Post Name Here')
But it's still erroring (in new parts of the same spec, too!):
1) Posts Invalid post - should refresh with error if content empty
Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.072.png
Failure/Error: fill_in :name, with: "Post Name Here"
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:43:in `block (3 levels) in <top (required)>'
2) Posts Invalid post - should refresh with error if name empty
Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.274.png
Failure/Error: fill_in :name, with: ""
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:33:in `block (3 levels) in <top (required)>'
3) Posts Valid post submission -- should log in a user and redirect to new post
Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.459.png
Failure/Error: fill_in :name, with: "Post Name Here"
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:67:in `block (3 levels) in <top (required)>'
The attribute "for" of label should be the "id" of the input element, i.e.
<label for="name_here">Name</label>
<br><input id="name_here" name="post[name]" size="30" type="text">
And in the spec you should call:
fill_in 'Name', with: "Post Name Here"

Resources