End of file Rspec error with AJAX - ruby-on-rails

I'm trying to test that I can POST a survey via AJAX
Test code:
describe "when completing the survey" do
before do
visit "/orders/#{#order.disguised_id}?key=#{ENV['admin_code']}"
find("#show-survey").click
find(".modal#survey-box select[name='heard']").find("[value='Other']").select_option
find("#submit-survey").click
end
it "should submit correctly" do
#order.reload
#order.heard.should == "Other"
end
# test post survey
end
First off, I can confirm via manual testing that this works. The issue is that the spec fails:
1) when completing the survey should submit correctly
Failure/Error: find("#submit-survey").click
EOFError:
end of file reached
# ./spec/features/edit_order/editing_order_spec.rb:<<the line with find("#submit-suvery")>>
When it fails, the Rspec auto ends the run and I'm left with an open browser window (using Selenium) and a regular command line interface. This is the controller that receives the POST
def survey
#order = Order.find_by_disguised_id(order_params[:disguised_id])
#order.update_attributes(order_params)
render nothing: true
end

If you're using chrome with selenium make sure you're running the latest version of chromedriver (2.24 as of this answer).
Additionally - click is asynchronous so reloading the order immediately after clicking #submit-survey is going to be flaky at best since it's highly likely it will be reloading #order before the ajax request is processed. You need to wait for whatever visual change on the page would indicate the survey has been submitted after clicking.
find("#submit-survey").click
page.should have_text('something') # whatever shows up when the survey finishes submitting - or use have_css, etc
Also the previous line find(".modal#survey-box select[name='heard']").find("[value='Other']").select_option can be written in a more readable manner as
find(".modal#survey-box").select('Other', from: 'heard')

Related

How to assert url was called from javascript using rspec and capybara

Scenario:
We use capybara integration tests to test that our frontend plumbing (javascript) is connected properly.
Sometimes all we need to validate the test is:
has content rendered properly on the page
has the js called the correct url open interaction
Problem:
Item 1 above is easy. However, with item 2 I can't seem to find an easy way to say:
Assert that url was called from js in browser.
Example:
it "should call coorect url with correct query string" do
visit widgets_path
# THIS IS WHAT I NEED TO KNOW
expect(something).to receive(:some_method).with(url: "my/test/url", params: {per_page: 2})
# In other words, I don't want the controller action to run. I don't care about the result since the controller is being tested elsewhere.
# I just need to know that the correct URL was called with the correct params.
within 'ul.pagination' do
click_on '2'
end
end
I've tried mocking the controller action, but there's no way to inspect the params. Or is there? Without inspecting the params, how can I know if the correct stuff was sent? All I know it's the correct route, which isn't enough.
If I could inspect the params then this would be solved... but otherwise?
If you are looking for the Rails solution, here it is! Tested with Rails 5.1.3.
1) Create a request params matcher spec/support/matchers/request_with_params.rb
RSpec::Matchers.define :request_with_params do |params|
match { |request| request.params.symbolize_keys.slice(*params.keys) == params }
end
2) Create a helper method for your acceptance tests (you can use some logics to pass symbol instead of class UsersController -> :users if needed)
def expect_request(controller, action, params = {})
expect_any_instance_of(ActionDispatch::Routing::RouteSet::Dispatcher)
.to receive(:dispatch)
.with(controller, action.to_s, request_with_params(params), anything)
end
end
3) Use it!
expect_request(UsersController, :index)
or with params
expect_request(UsersController, :show, { id: 1 })
OR
4) There is another way in using https://github.com/oesmith/puffing-billy Check this gem for intercepting requests sent by your browser. But it can be an overkill if you need to mock only certain requests to your backend app.
Capybara integration tests intentionally don't support that. They are end-to-end blackbox tests, shouldn't generally be mocked, and really only support checking for things visible to the user in the browser. In your example case that would mean expecting on whatever visible change is caused by the JS call to the specific URL. Something like
expect(page).to have_css('div.widget', count: 2)

Rspec testing, Capybara unable to find link or button

This the part of my requests test that fails:
scenario 'Admin destroys a job posting + gets notified' do
parent = create(:parent)
create(:assignment, user_id: #user.id, role_id: 1)
demand = create(:demand, shift_id: 4)
sign_in(#user)
visit demands_path
click_on 'Destroy'
expect(page).to have_content('successfully')
end
This is the error:
Failure/Error: click_on 'Destroy'
Capybara::ElementNotFound:
Unable to find link or button "Destroy"
And here is the corresponding index view, including a "Destroy" link in the app:
Any idea why this test fails??
Odds are the data you assume is on the page actually isn't. This could be for a number of reasons.
Your page requires JS and you're not using a JS capable driver - see https://github.com/teamcapybara/capybara#drivers
Your sign_in method is defined to fill in user/pass and then click a button, but doesn't have an expectation for content that confirms the user has completed login at the end. This can lead to the following visit occurring before login has completed and therefore not actually logging in. Verify that by inspecting the result of page.html or calling page.save_and_open_screenshot before the click.
Your 'Destroy' "button" is neither an actual <a> element or <button> element. Fix that by either using semantic markup or swapping to find(...).click
You are using a JS capable driver but your records aren't actually visible to the app - this would affect all your tests though so I assume it's probably not this. If this was the case the login would fail and you'd probably need to install database_cleaner and configure for use with RSpec & Capybara - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example

Capybara integration test object creation

I have a basic integration test that is using Capybara, the problem is that if I do not create the required objects firstly the integration test fails. Am I required to create all objects as the first step in an integration test using Capybara? I am using Rails 4.2.4 with Capybara 2.4.3
Fail
scenario 'if media content contains more than 10 items display pagination links' do
sign_in
# Object creation
11.times do
FactoryGirl.create(:media_content)
end
within '.pagination' do
expect(page).to have_content '1'
end
end
Success
scenario 'if media content contains more than 10 items display pagination links' do
# Object creation
11.times do
FactoryGirl.create(:media_content)
end
sign_in
within '.pagination' do
expect(page).to have_content '1'
end
end
If the objects creation affects the page that you are visit-ing in your capybara test then yes, you need to create the objects before you test for elements on that page because upon visiting the page, its content is already grabbed by the test browser.
I assume that you have a visit "some_login_page" and perhaps a redirect upon successful login in your sign_in method, so when finishing the sign_in, the test browser already visited (i.e. grabbed) the page on which you are trying to test content later.
The only exception that comes to my mind is if you used a delayed AJAX request to grab the newly created elements from the server to the page dynamically - in that case creating the objects after page visit might work ok.

Functional tests for flash messages after redirect

I'm not sure how to check flash messages with functional tests. My problem has something to do with redirect_to because my test for flash[:error] DOES pass. Here is what my create action looks like:
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user, flash: { success: "Welcome!" }
else
flash.now[:error] = "Signup failed:"
render 'new'
end
end
I have a passing test for flash[:error] that 1) checks that flash[:error] is the correct value, 2) checks the page for the flash div, and 3) gets a new page and checks that the flash message has been cleared. I tried to make an analogous test for flash[:success] but it is failing when it tries to find the div on the page. Here is the failing test:
should "show flash[:success] message" do
post :create, user: { name: "valid user",
password: "userpw",
password_confirmation: "userpw",
email: "a#b.com" }
assert_redirected_to user_path(assigns(:user)), "user isn't being redirected to their page after registration"
assert_equal flash[:success], "Welcome!", "flash[:success] isn't being set properly"
assert_select "div.alert-success", flash[:success]
get :new
assert flash.empty?, "there shouldn't be any flash messages left after getting new page"
end
When I test this manually in a browser, it behaves as expected, but I'm not sure how to write the tests to reflect this. The first two assertions are passing (so the test recognizes that it is being redirected correctly, and that flash[:success] is being set to "Welcome!"). Based on that, I would think the assert_select statement would be able to find the div, but if fails with this error message:
Expected at least 1 element matching "div.alert-success", found 0.
So I tried adding puts #response.body right before the assert_select statement, which printed this:
<html><body>You are being redirected.</body></html>
This explains why it couldn't find the flash div, but I'm not sure how to "finish" the redirect so that it renders the correct view with the flash div. If I add a get statement right before the assert_select statement, the test passes, because now #response.body has the flash div. However, this doesn't seem like an effective test because it doesn't reflect the behavior of my application - the user doesn't have to make a new request to see the flash message.
It also doesn't guarantee that the flash message shows up on the correct page. If I use the statement get :new, the assertion still passes, which implies that the flash message would be present on the users#new view. Even if I use
get :show, id: assigns(:user).id
to make sure the flash message shows up on the correct page, this still feels wrong because 1) now it is a success response instead of a redirect response, and 2) it still doesn't explain why I have to manually request ANY page to find the flash div - according to the docs, "The flash is a special part of the session which is cleared with each request."
Anyway, is there a way to test that a flash message shows up correctly after a redirect?
I'm answering this question from the POV that what you're trying to test would be better tested elsewhere. If you're having a lot of trouble setting up the test, odds are there's a better way to test it :)
Testing that flash messages get cleared - flash messages are built into Rails, so it doesn't make a lot of sense to test something that the framework is handling for you.
Testing that elements show up on the page - view specs don't belong in controllers. Here is a description of the role of the controller:
A controller can thus be thought of as a middle man between models and
views. It makes the model data available to the view so it can display
that data to the user.
The controller is supposed to make data available to the view, not to specify how the data gets rendered. Imagine if you changed the name of your CSS class, then you'd have to also update your controller specs.
If you really want to test that flash messages show up, I would use an integration test.

how to test a stale link click (or link I can't get to) using capybara/rspec

Artists cannot rate their own artworks
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
page.should have_content("Can't vote on your own artwork")
end
This was passing just fine.
However, I can't click on like_post_1 anymore because I added a feature to prevent voting links from appearing next to your own artworks.
Does this mean I no longer need test coverage for this scenario because it's extremely rare that someone can click on a voting link for their own artwork? Or should still have coverage to test the ajax response, because it's not tested anywhere else and it's possible for some stale page of links to somehow exist in a tabbed browser window. If so... how do I test it if I cannot call click_link?
I could try to create a POST request to create the vote, but capybara doesn't support posts, and I can't test the ajax response that way...
Or is there a way to simulate tabbed browsing in capybara?
Suggestions?
You can use CSS display:none or visibility:hidden for the own artwork instead of eliminating the link from the DOM. You might have to set
Capybara.ignore_hidden_elements = false
Another way is giving up Capybara and putting them into the controller/model spec. Controller/model spec might be a better place for the extremely rare case or safeguarding your app case.
Please try this:
Use sleep function.
sleep 10 use to wait process upto 10 seconds..
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
sleep 10
page.should have_content("Can't vote on your own artwork")
end

Resources