Rspec feature test: Cannot visit a path - ruby-on-rails

I have rspec features tests that are all failing because i cannot visit the indicated path. They all seems to be stuck at the root path after logging in. A screenshot shows that the page still remains on the root path. The test steps work on the browser, which means that the routing is correct. Any ideas?
I am getting the below error message for the test:
Failure/Error: page.evaluate_script('jQuery.active').zero?
Extract of my feature spec test:
describe 'follow users' do
let!(:user) { FactoryGirl.create(:user) }
let!(:other_user) { FactoryGirl.create(:friend) }
describe "Managing received friend request", js: true do
let!(:request) { Friendship.create(user_id: other_user.id, friend_id: user.id, accepted: false) }
before do
login_as(user, :scope => :user)
visit followers_path
end
it 'friend request disappear once user clicks accept' do
click_on "Accept"
wait_for_ajax
expect(current_path).to eq(followers_path)
expect(page).to have_css(".pending-requests", text: "You have 0 pending friend requests")
expect(page).to_not have_css(".pending-requests", text: other_user.name)
expect(page).to_not have_link("Accept")
expect(page).to_not have_link("Decline")
end
end
end

The issue here is you're calling 'wait_for_ajax' either on a page that doesn't include jQuery or at a time when it hasn't yet been loaded. The solution is to stop using wait_for_ajax and instead use the Capybara expectations/matchers as designed. There are very very few cases where wait_for_ajax is actually needed and even then it's usually a sign of bad UI decisions (no indication to the user something is happening). You should also not be using the eq matcher with current_path and should be using the Capybara provided have_current_path matcher instead since it has waiting/retrying behavior like all of the Capybara provided matchers.
it 'friend request disappear once user clicks accept' do
click_on "Accept"
expect(page).to have_current_path(followers_path)
expect(page).to have_css(".pending-requests", text: "You have 0 pending friend requests")
expect(page).to_not have_css(".pending-requests", text: other_user.name)
expect(page).to_not have_link("Accept")
expect(page).to_not have_link("Decline")
end
If that doesn't work for you, then either the button click isn't actually triggering page changes (check your test log), your Capybara.default_max_wait_time isn't set high enough for the hardware you're testing on, your login_as statement isn't actually logging in the user (although then I would expect the click on the accept button to fail), or you have a bug in your app.
If it's that login_as isn't actually logging in then make sure the server being used to run the AUT is running in the same process as the tests, if you're using puma that means making sure in the output it doesn't say it' s running in clustered mode.

Try to this approach to wait for all the ajax requests to finish:
def wait_for_ajax
Timeout.timeout(Capybara.default_wait_time) do
active = page.evaluate_script('jQuery.active')
until active == 0
active = page.evaluate_script('jQuery.active')
end
end
end
Taken from: Wait for ajax with capybara 2.0

Related

Rspec System Test passes when run individually, fails when run with entire suite

I have the following test that passes when I run it in isolation:
require 'rails_helper'
RSpec.describe 'deleting a non-recurring user event', js: true do
let(:user) { create(:proofreader_user) }
let(:date) { Date.current.strftime('%Y-%m-%d') }
let(:date_time) { date + ' 00:00'}
it 'deletes the event' do
visit root_path
click_on "Login"
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_on "Sign In"
visit calendar_path
expect(current_path).to eq(calendar_path)
expect(page).to have_css("#complete_registration:disabled")
expect(page).to_not have_css("td.fc-event-container")
find("td.fc-day[data-date='#{date}']").click
expect(page).to have_css("div#user-event-modal")
expect(page).to have_select('user_event[title]', :options => UserEvent.titles.keys)
expect(find('input', id: 'user_event_starting').value).to eq date_time
expect(find('input', id: 'user_event_ending').value).to eq date_time
page.execute_script("$('input#user_event_starting').val('#{date} 09:00')")
expect(find('input', id: 'user_event_starting').value).to eq date + ' 09:00'
page.execute_script("$('input#user_event_ending').val('#{date} 12:00')")
expect(find('input', id: 'user_event_ending').value).to eq date + ' 12:00'
click_on 'Save'
expect(page).to have_css("td.fc-event-container a.fc-day-grid-event")
expect(page).to have_css("span.fc-time", text: '9a - 12p')
expect(page).to have_css("span.fc-title", text: 'work')
find("span.fc-time", text: '9a - 12p').click
expect(page).to have_css("div#user-event-modal")
find("#del_one_event").click
within('.swal2-actions') { click_button('Yes') }
wait_for { page }.to_not have_css("div#user-event-modal")
expect(page).to_not have_css("td.fc-event-container")
expect(page).to_not have_css("span.fc-time", text: '10a - 14p')
end
end
However, when I run all of the tests I get the following error:
Failure/Error: within('.swal2-actions') { click_button('Yes') }
Capybara::ElementNotFound:
Unable to find visible css ".swal2-actions"
Why does this test fail when I run it with the other test and how can I fix it so it passes when I run all tests?
Cannot be sure without a lot more information. It would be best if you can create and publish a minimum complete verifiable example.
Based on the information given, I would guess that the test that fails when run with the other tests is not properly isolated. The other examples are changing the state of the application, causing this test to fail. So look at your before and after hooks and make sure you are setting config.use_transactional_fixtures = true in rails_helper.rb
If it is possible for there to be a delay between the time #del_one_event is clicked and .swal2-actions appears, then change
within('.swal2-actions') { click_button('Yes') }
to
find('.swal2-actions').click_button('Yes')
If none of those fix the problem, you might have an issue with browser caching, but that is even harder to debug.
This is hard to answer without seeing the other tests.
But I noticed some points that are worth checking out:
you are creating data by navigating the application, perhaps other tests create data, that prevent data in this test to be created
The command find("#del_one_event").click (one line before) just executes the click, but does not wait until anything happens. With all tests and more data in the db, it might take a while longer until .swal2-actions appears and is not yet present when you execute within('.swal2-actions') { click_button('Yes') }
Another way to get closer to the bug is by making screenshots and comparing them. Check out the gem capybara-screenshot

Teardown called in the middle of a test Capybara

I have been trying to create the following test :
Edit a model (client side), check if the view is updated and if the model changed in database.
there is the code :
test 'a' do
user = User.joins(:organization_users).find_by organization_users: { role: OrganizationUser.roles.values_at(:ORGANIZER, :ADMINISTRATOR) }
sign_in_user user
criterion = create(:criterion, scoring_id: #scoring.id, name: "Test criterion name",
description: "Test description")
step = create(:step, criterion_id: criterion.id)
visit "scorings/" + (#scoring.id).to_s + "/criteria"
find("#criteria > div > div > a > i").click()
fill_in 'name', with: 'New name'
fill_in 'description', with: 'New description'
find('#criterion-modal > div:nth-child(2) > form > div:nth-child(4) > input').click()
criterion = criterion.reload
assert criterion.name == 'New name'
end
`
Driver :
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new app , { phantomjs: Phantomjs.path }
end
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = Capybara.javascript_driver
Teardown :
teardown do
DatabaseCleaner.clean
ActiveRecord::Base.connection.close
Capybara.reset_sessions!
end
As you can see at the end of the test i reload the criterion, but when i do that the teardown function is called. After that the Database is cleaned and i get the error "cant find criterion id:1". I'm only using minitest , factory girl and Capybara. So what i want to understand is why Teardown is called since its not the end of the test and how can i fix that ?
Thank you.
You don't show what you have setup for your teardown method, nor do you specify what driver you are using with Capybara. However, since the test code and the teardown are run in the same thread there really is no way for the teardown to run before the test has ended. What is possible (when using a JS capable driver, where clicks are processed asynchronously) is for the teardown to run before a click is processed/handled by the app code. That would mean the "cant find criterion id:1" would actually be coming from your controller code. The reason for this is that you're not actually checking for anything on the page to change after clicking so the test just keeps on moving, finishes (failing the assertion), the teardown cleans and the controller action can't find the record. Something like
assert_text 'Criterion updated' # if a message is displayed on successful update
or
assert_current_path("scorings/#{#scoring.id}") # whatever path it redirects to after updating
after the click and before your reload
On a side note - Using long selectors like '#criterion-modal > div:nth-child(2) > form > div:nth-child(4) > input' will lead to really brittle tests -- It would be much nicer to use simpler selectors or the capybara click_button type helpers if possible

Rails + Capybara + Braintree—how to feature test BT's Hosted Fields?

TL;DR—How do I access fields within Braintree's Hosted Fields' iframes?
I want to test a UX flow of paying a donation through Braintree. This is my code so far:
require "rails_helper"
RSpec.feature "Donation Module", type: :feature do
scenario "Public visitor creates a new donation" do
#load page
website = create(:website)
Capybara.current_session.driver.header 'Referer', website.website
visit "/donate?t=#{website.public_token}&frame=1"
#verify page loaded
expect(page).not_to have_content("Are you sure you're installing this on the correct website?")
#fill page 1
find("input[value='20']").click
#go to page 2
find("#credit-details").click
#verify page 2 content is loaded
expect(find(".total-cost-text")).to be_visible
#fill page 2
fill_in 'First Name', with: 'Leeroy'
fill_in 'Last Name', with: 'Jenkins'
fill_in 'Email for receipt', with: 'new_donor#email.com'
within_frame('#braintree-hosted-field-number') do
fill_in '#credit-card-number', with: '4111-1111-1111-1111'
end
within_frame('#braintree-hosted-field-expirationDate') do
fill_in '#expiration', with: '09/19'
end
within_frame('#braintree-hosted-field-cvv') do
fill_in '#cvv', with: '123'
end
find('Make payment').click
# expect to make a new user, new donation, new receipt, email receipt
end
end
Currently, it's breaking at the first within_frame saying Capybara::NotSupportedByDriverError:
Capybara::Driver::Base#within_frame
How do I access fields inside BT's iframes?
Well, I am writing here not exactly an answer to this question, but rather corrections to the question, as I was in the similar situation and was facing similar errors such as Selenium::WebDriver::Error::NoSuchFrameError: Unable to locate frame: #braintree-hosted-field-number and Test::Unit::Capybara::ElementNotFound: Unable to find field "#credit-card-number".
The within_frame should have the following format (the #-sign for ID should be removed from both):
within_frame('braintree-hosted-field-number') do
fill_in 'credit-card-number', :with => number
end
And in order to use the selenium driver in Test::Unit, I used the following helper:
def js
Capybara.current_driver = Capybara.javascript_driver
yield
Capybara.current_driver = Capybara.use_default_driver
end
And then wrapped my tests in it:
class SomeTest < ActionDispatch::IntegrationTest
test "should ..." do
js do
within_frame('braintree-hosted-field-number') do
fill_in 'credit-card-number', :with => number
end
# ...
end
end
Hopefully, someone will find it useful while using Unit Tests.
Seems like you're using the rack_test driver? That doesn't support JS or frames so braintree isn't going to work with that. You need to switch to one of the real browser drivers like selenium, capybara-webkit, or poltergeist.

Rspec with Capybara sometimes not pass tests

I have problem with testing Rails Application. My tests generally work perfectly. But sometimes tests will fail when I type some features test for modal bootstrap window, or notify with success/error [js]. How can I resolve this problem ?
I'm using Rspec, Capybara, Rails4.2, PhantomJs, Poltergeist as JS driver. Tests is running locally and in Wercker. In test mode, every bootstrap animation is disabled. What perhaps I do wrong ?
Test:
scenario 'return deutsch default title' do
find('.f-edit-item', match: :first).click
find('a', :text => 'Lang').click
find('a', :text => t('menu.languages.de')).click
find('.f-reset-button', match: :first).click
expect(page).to have_field('menu_item[title]', with: 'Exhibitions_de')
end
Output:
Objects Restore Language restore title translations exist for deutsch translation return deutsch default title
Failure/Error: expect(page).to have_field('object_item[title]', with: 'Exhibitions_de')
expected to find field "object_item[title]" with value "Exhibitions_de" but there were no matches. Also found "", "", which matched the selector but not all filters.
When I click manually, everything is working. When I run this test, sometimes passed, sometimes not. Form is in bootstrap modal. Curiosity: When I add save_and_open_page before find('.f-reset-button', match: :first).click test is passed always(5x in a row)
Because the tests are to do with a Bootstrap modal, my guess is that the test is searching the page for the matching elements, BEFORE the modal has loaded in the DOM.
Edit: As #TomWalpole pointed out, it should be enough to override Capybara's max wait time like so:
expect(page).to have_field('menu_item[title]', with: 'Exhibitions_de', wait: 1.0)
But if you are loading the contents of your modal via AJAX, you may need to force a wait for AJAX to complete the expect line. Here is a good guide on how to do this.
Specifically you need:
# spec/support/wait_for_ajax.rb
module WaitForAjax
def wait_for_ajax
Timeout.timeout(Capybara.default_wait_time) do
loop until finished_all_ajax_requests?
end
end
def finished_all_ajax_requests?
page.evaluate_script('jQuery.active').zero?
end
end
RSpec.configure do |config|
config.include WaitForAjax, type: :feature
end
And then your test would become:
scenario 'return deutsch default title' do
find('.f-edit-item', match: :first).click
find('a', :text => 'Lang').click
find('a', :text => t('menu.languages.de')).click
find('.f-reset-button', match: :first).click
wait_for_ajax
expect(page).to have_field('menu_item[title]', with: 'Exhibitions_de')
end

Capybara/Rspec not catching ActionController::UnpermittedParameters error

I was in the processing in adding a field to an ActiveRecord model and the corresponding view. Before I started, I had a full green test run. I started with writing the following test:
scenario "with all correct info" do
create_vendor "vendor.email#example.com", "vendor_passowrd"
create_consumer "consumer.email#example.com", "password"
sign_in_with "consumer.email#example.com", "password"
click_link "New Order"
select 'vendor.email#example.com', from: "Vendor"
fill_in "Order text", with: "my normal order"
click_button "Submit"
expect(page).to have_content "my normal order"
end
I added the field to the model using the proper rails migration and the route entry.
I would have expected the test to fail with a ActionController::UnpermittedParameters because I have not yet updated the controller. However, this is the failure I am getting:
"Failure/Error: expect(page).to have_content "my normal order"
When I test in the browser, I do get the ActionController::UnpermittedParameters, but not in the automated test.
My question is should the test get the ActionController::UnpermittedParameters error?
Capybara simulates a web browser request, so it will get back an error page from Rails. The UnpermittedParameters error will not propagate up to the spec code itself.
This sounds like it's working correctly.

Resources