Capybara not waiting on expect - ruby-on-rails

I have a page with a link that opens a bootstrap modal with a simple input field. I'm having a hard time understanding why Capybara doesn't wait for the modal to open, and fails immediately.
I added expect(page).to have_content('Did you') so capybara will wait a few seconds until the modal is shown.
But for some reason it's not waiting and raises Capybara::ExpectationNotMet: expected to find text "Did you" in ...."
If I put a sleep of 1 second it will then find the modal, but it's not good enough because then a DB clean callback found in my spec_helper.rb is called:
config.after(:each) do
DatabaseCleaner.clean
end
This is the spec:
RSpec.describe "follower button", type: :request do
it "sends email #15B to owner", :js do
using_wait_time 20 do
FactoryGirl.create(:apartment_with_event)
visit apartment_path(Apartment.last)
click_on 'follow-events'
expect(page).to have_content('Did you')
within('#follow-events-modal') do
fill_in 'follower-email-signup-mail', with: 'follower#example.com'
click_button 'follower-signup-submit'
end
expect(page).to have_content(I18n.t("followers.create.title_success"))
expect(Follower.all.count).to eq(1)
end
end
end
I also set Capybara.default_max_wait_time = 10 in spec_helper.rb, even though it should wait for 20 seconds for this example.
Forgot to mention, I'm using Capybara-wekbit as the driver.
I really spent hours trying to discover why it's happening, while other examples run just fine.
UPDATE:
Adding full backtrace of failure.
Failures:
1) leeron's button sends email #15B to owner
Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
ActionController::RoutingError:
No route matches [GET] "/images/slider/missing.png"
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rollbar-1.2.13/lib/rollbar/middleware/rails/show_exceptions.rb:22:in `call_with_rollbar'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/railties-4.2.4/lib/rails/rack/logger.rb:38:in `call_app'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/railties-4.2.4/lib/rails/rack/logger.rb:20:in `block in call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/railties-4.2.4/lib/rails/rack/logger.rb:20:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/request_store-1.2.0/lib/request_store/middleware.rb:8:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/methodoverride.rb:22:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/runtime.rb:18:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/lock.rb:17:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/sendfile.rb:113:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/railties-4.2.4/lib/rails/engine.rb:518:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/railties-4.2.4/lib/rails/application.rb:165:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/urlmap.rb:66:in `block in call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/urlmap.rb:50:in `each'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/urlmap.rb:50:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/capybara-2.7.1/lib/capybara/server.rb:43:in `call'
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/rack-1.6.4/lib/rack/handler/webrick.rb:88:in `service'
# ------------------
# --- Caused by: ---
# Capybara::ExpectationNotMet:
# expected to find text "Did you" in "...(REMOVED BY ME TO SAVE SPACE)..."
# /Users/etaiso/.rbenv/versions/2.2.3/gemsets/dorbel/gems/capybara-2.7.1/lib/capybara/node/matchers.rb:527:in `block in assert_text'
Finished in 3.87 seconds (files took 9.79 seconds to load)
1 example, 1 failure

From the callback we can see that the actual exception is a routing error from the app. While Capybara is retrying/waiting on finders and expectations it checks for server thrown errors and if Capybara.raise_server_errors is true it will raise them in the test thread so they are visible to tests. A side-effect of the re-raising of exceptions from the app thread in the test thread is that any current exception in the test thread (such as ExpectationNotMet) which would normally be handled and retried by Capybara gets set by ruby as the nested cause of the apps exception. It isn't really the cause but we can't because of the way Capybara re-raises the server error it shows up there. You can either set Capybara.raise_server_errors = false or better yet, fix the missing image.

Related

VCR says "no cassette in use" even after specifying cassette

I have an api-only RoR app with a user model. Users are authenticated via Twilio/Authy (using this gem). Each user has_one authy_user model for storing authy info, with dependent: :destroy.
The authy_user model has a before_destroy hook that connects to the authy api via the authy gem and deletes the user there.
The authy_user specs run just fine, and I've recorded cassettes for both registering and deleting users with the authy api.
A piece of the authy_user spec:
describe "delete user" do
before do
#user = create(:user_with_authy_user)
end
context "user is registered" do
it "deletes user in authy and locally" do
VCR.use_cassette("authy_delete_user") do
expect {
#user.authy_user.destroy
}.to change { AuthyUser.count }.by(-1)
end
end
end
end
(this fails with the same error as below if i change it from #user.authy_user.destroy to #user.destroy)
My problem is the user request spec, specifically the one that deletes the user.
Here's the spec that fails:
describe "DELETE /user" do
context "user exists" do
before do
#user = create(:user_with_authy_user)
#token = JWTService.issue_access_token user: #user, provider: "authy"
end
it "deletes the user" do
expect {
VCR.use_cassette("authy_delete_user") do
delete(user_path, headers: { "X-Auth-Token": #token })
end
}.to change { User.count }.by(-1)
expect(status).to eq(204)
expect(body).to be_blank
end
end
end
Here's the failure:
1) Users DELETE /user user exists deletes the user
Failure/Error: Authy::API.delete_user id: authy_id
VCR::Errors::UnhandledHTTPRequestError:
================================================================================
An HTTP request has been made that VCR does not know how to handle:
POST https://api.authy.com/protected/json/users/delete/92962960
There is currently no cassette in use. There are a few ways
you can configure VCR to handle this request:
* If you're surprised VCR is raising this error
and want insight about how VCR attempted to handle the request,
you can use the debug_logger configuration option to log more details [1].
* If you want VCR to record this request and play it back during future test
runs, you should wrap your test (or this portion of your test) in a
`VCR.use_cassette` block [2].
* If you only want VCR to handle requests made while a cassette is in use,
configure `allow_http_connections_when_no_cassette = true`. VCR will
ignore this request since it is made when there is no cassette [3].
* If you want VCR to ignore this request (and others like it), you can
set an `ignore_request` callback [4].
[1] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/configuration/debug-logging
[2] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/getting-started
[3] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/configuration/allow-http-connections-when-no-cassette
[4] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/configuration/ignore-request
================================================================================
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/vcr-4.0.0/lib/vcr/request_handler.rb:97:in `on_unhandled_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/vcr-4.0.0/lib/vcr/library_hooks/webmock.rb:129:in `on_unhandled_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/vcr-4.0.0/lib/vcr/request_handler.rb:24:in `handle'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/vcr-4.0.0/lib/vcr/library_hooks/webmock.rb:144:in `block in <module:WebMock>'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/stub_registry.rb:28:in `block in register_global_stub'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/request_pattern.rb:40:in `matches?'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/stub_registry.rb:58:in `block in request_stub_for'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/stub_registry.rb:57:in `each'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/stub_registry.rb:57:in `detect'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/stub_registry.rb:57:in `request_stub_for'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/stub_registry.rb:50:in `response_for_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/http_lib_adapters/httpclient_adapter.rb:187:in `block in webmock_responses'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/http_lib_adapters/httpclient_adapter.rb:59:in `do_get'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/webmock-3.4.2/lib/webmock/http_lib_adapters/httpclient_adapter.rb:47:in `do_get_block'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/httpclient-2.8.3/lib/httpclient.rb:1019:in `block in do_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/httpclient-2.8.3/lib/httpclient.rb:1133:in `protect_keep_alive_disconnected'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/httpclient-2.8.3/lib/httpclient.rb:1014:in `do_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/httpclient-2.8.3/lib/httpclient.rb:856:in `request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/httpclient-2.8.3/lib/httpclient.rb:765:in `post'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/authy-2.7.4/lib/authy/api.rb:119:in `post_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/authy-2.7.4/lib/authy/api.rb:100:in `delete_user'
# ./app/services/authy_service.rb:20:in `delete'
# ./app/models/authy_user.rb:74:in `remove_from_authy'
# ./app/controllers/users_controller.rb:31:in `destroy'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-2.0.5/lib/rack/etag.rb:25:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-2.0.5/lib/rack/conditional_get.rb:38:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-2.0.5/lib/rack/head.rb:12:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:38:in `call_app'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:26:in `block in call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:26:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-2.0.5/lib/rack/runtime.rb:22:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-2.0.5/lib/rack/sendfile.rb:111:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:524:in `call'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-test-1.0.0/lib/rack/mock_session.rb:29:in `request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-test-1.0.0/lib/rack/test.rb:259:in `process_request'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/rack-test-1.0.0/lib/rack/test.rb:119:in `request'
# ./spec/requests/users_request_spec.rb:137:in `block (5 levels) in <main>'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/vcr-4.0.0/lib/vcr/util/variable_args_block_caller.rb:9:in `call_block'
# /Users/raymondgulbrandsen/.gem/ruby/2.5.0/gems/vcr-4.0.0/lib/vcr.rb:188:in `use_cassette'
# ./spec/requests/users_request_spec.rb:136:in `block (4 levels) in <main>'
It looks like use_cassette is called, but maybe it gets unset somewhere along the road?
Does it have something to do with the dependent: :destroy relationship between user and authy_user?
Any help is appreciated, and let me know if you need more info.
Maybe, something in the request is changing each time you do, like an HTTP header, a timestamp, and since you didn't specified VCR to record new transactions, it will fail.
I would debug it, using
VCR.use_cassette("authy_delete_user", record: :new_episodes)
and see if records a new cassette and compare both, to see the output, see what is changing.
By default, if anything in the request changes, a new cassette is written or an error is thrown (your case). You can, in this case, specify how the cassette data and request is matched, for example, using match_requests_on, see here

Why is my rspec test sending a get request to javascripts/sessions.js?

I have a very messy Rails app that I built some time ago that I am currently extending. This has involved a number of major changes to Ruby Version (1.9.x to 2.3.x), Rails Version (3.2.x to 5.0.x), and many other associated changes. It has a lot of cruft and I am uncertain about a lot of things going on in it at the moment.
In order to sort out this horrible mess I have created for myself I have decided to write a bunch of feature tests with RSpec and Capybara. I was making good progress until I needed to test something that used Javascript (eventually most of my feature tests will be testing Javascript).
My Rspec test looks like this so far (there is much left to complete - I just got stuck here);
feature "User registers a new customer", js: true do
scenario "Logged in user registers a new customer" do
user = FactoryGirl.create(:user)
visit_root_and_login user
fill_in 'reg_number', with: '123456'
click_button 'Look up'
end
end
In my app this triggers an AJAX call that looks up the registration number and returns a piece of Javascript that adds additional fields to the form depending on whether the customer has been previously registered or not.
What happens though is this;
Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
ActionController::RoutingError:
No route matches [GET] "/javascripts/sessions.js"
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/rack/logger.rb:36:in `call_app'
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/rack/logger.rb:24:in `block in call'
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/rack/logger.rb:24:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/method_override.rb:22:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/runtime.rb:22:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/sendfile.rb:111:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/engine.rb:522:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:68:in `block in call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `each'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/capybara-2.12.1/lib/capybara/server.rb:43:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/handler/webrick.rb:86:in `service'
# ------------------
# --- Caused by: ---
# NameError:
# uninitialized constant Selenium::WebDriver::Remote::W3CCapabilities
# /Users/brad/.gem/ruby/2.3.1/gems/capybara-2.12.1/lib/capybara/selenium/driver.rb:282:in `marionette?'
I tried adding driver: :webkit to see if anything different happened and got something slightly different;
Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
ActionController::RoutingError:
No route matches [GET] "/javascripts/sessions.js"
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/rack/logger.rb:36:in `call_app'
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/rack/logger.rb:24:in `block in call'
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/rack/logger.rb:24:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/method_override.rb:22:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/runtime.rb:22:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/sendfile.rb:111:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/railties-5.0.1/lib/rails/engine.rb:522:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:68:in `block in call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `each'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/capybara-2.12.1/lib/capybara/server.rb:43:in `call'
# /Users/brad/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/handler/webrick.rb:86:in `service'
# ------------------
# --- Caused by: ---
# Capybara::ElementNotFound:
# Unable to find field "reg_number"
# /Users/brad/.gem/ruby/2.3.1/gems/capybara-2.12.1/lib/capybara/node/finders.rb:44:in `block in find'
I don't understand why "/javascripts/sessions.js" is being called. I don't have any such file and I don't make this request myself. It looks to be coming from Rack, but this is a zone of the Ruby and Rails world that I really don't understand.
Is anyone able to shed any light on this so I at least know where to start looking?
TIA
OK, I sorted this out.
It was an asset pipeline issue.
In my application.html.haml I had = javascript_include_tag "application", params[:controller] which was causing an attempt to include the file /app/assets/javascripts/sessions.js in my assets during the login process. I had no such file so created a blank one. I then had to add
%w( sessions ).each do |controller|
Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
end
to my /app/config/initializers/assets.rb to get it all working.
The same exception was being raised in my development environment in the server console but I had not noticed it and it was not stopping execution. In Capybara this exception was stopping the whole show though.

Poltergeits doesn't working well in rspec tests

I have the following feature tests passing:
require 'rails_helper'
describe "Create a quetsion", type: :feature do
let(:question) { build(:question) }
before do
login_as create(:user, :teacher)
exercise = create(:exercise)
visit new_exercise_question_url(exercise.id)
end
context "whit valid attributes" do
subject do
fill_in "question_score", with: question.score
fill_in "question_description", with: question.description
click_on "Criar"
end
it "create the question" do
expect{ subject }.to change(Question, :count).by(1)
expect(page).to have_current_path(question_path(Question.first.id))
end
end
context "whit invalid attributes" do
subject do
click_on "Criar"
end
it "doesn't create the exercise" do
expect{ subject }.to change(Question, :count).by(0)
expect(page).to have_selector("div.alert.alert-danger")
end
end
end
That works fine, unless I add js: true. In this case, I have the following errors:
1) Create a quetsion whit valid attributes create the question
Failure/Error: fill_in "question_score", with: question.score
Capybara::ElementNotFound:
Unable to find field "question_score"
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/finders.rb:44:in `block in find'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/base.rb:85:in `synchronize'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/finders.rb:33:in `find'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/actions.rb:85:in `fill_in'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:735:in `block (2 levels) in <class:Session>'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/create_a_question_spec.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/features/create_a_question_spec.rb:20:in `block (4 levels) in <top (required)>'
# ./spec/features/create_a_question_spec.rb:20:in `block (3 levels) in <top (required)>'
2) Create a quetsion whit invalid attributes doesn't create the exercise
Got 0 failures and 2 other errors:
2.1) Failure/Error: visit new_exercise_question_url(exercise.id)
Capybara::Poltergeist::StatusFailError:
Request to 'http://www.example.com/exercises/1/questions/new' failed to reach server, check DNS and/or server status
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:376:in `command'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:35:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/driver.rb:97:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:240:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/create_a_question_spec.rb:8:in `block (2 levels) in <top (required)>'
2.2) Failure/Error: #socket.send(command.id, command.message, receive_timeout) or raise DeadClient.new(command.message)
Capybara::Poltergeist::DeadClient:
PhantomJS client died while processing {"id":"2928bf26-2dd8-45d7-8822-41b2923fd40d","name":"reset","args":[]}
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/server.rb:38:in `send'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:369:in `command'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:224:in `reset'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/driver.rb:183:in `reset!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:109:in `reset!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `block in reset_sessions!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `reverse_each'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `reset_sessions!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/rspec.rb:21:in `block (2 levels) in <top (required)>'
I checked, whit js: true my page html isn't written too (instead the desired HTML, I only receive <html><head></head><body></body></html>).
I added poltergeist gem to test javascritp, and configured it in my rspec rails_helper.rb file:
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
PhantomJS is already instaled and available in my $PATH. I'm completelly out of ideas, what can be happening here?
There are a number of potential issues here.
If you have set Capybara.server = :puma, make sure you're not running puma 3.7.0 which has a bug in it that will be fixed when 3.7.1 is released. For now use 3.6.9
If this is the first time you're setting up js tests make sure you have setup and correctly configured DatabaseCleaner so that you are no running in transaction mode for js: true tests. See https://github.com/teamcapybara/capybara#transactions-and-database-setup and https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example for the recommended configuration
Once you have #1 and #2 worked out then you'll need to look at your tests and deal with the fact that after a click/click_on type action you need to check for a visible page change to ensure the action has completed before you move on. This is because clicks happen but the actions those clicks trigger can occur asynchronously. In your current example that means you would need something like
...
click_on "Criar"
expect(page).to have_text("Question created!") #whatever message is shown when the action has completed, or check the page etc.
Without something like that the change check will fail because it will check the count before the actual actually occurs.
Default to _path helpers rather than _url helpers when there's no need for a specific host name (99.9% of the time). This lets Capybara fill_in the correct host/port for the server being run. To visit _url helpers there are a number of configuration parameters that need to be carefully set.
Note: It's generally not best practice to be checking database counts in feature tests, they should generally be limited to checking on visible page changes.

Testing sidekiq WebUI reachability in Rails4/Capybara/Rspec3

I'm currently moving some feature tests from Minitest to RSpec. My last problem is with reaching the Sidekiq WebUI which I enabled in my routes.rb
require 'sidekiq/web'
require 'admin_constraint'
Rails.application.routes.draw do
mount Sidekiq::Web => '/sidekiq', constraints: AdminConstraint.new
...
end
The ressource is admin-only protected, which I now want to test. I have two questions
1) Contemplating about the test again made me think, whether it would be better formulated as a routing test. What do you think? Keep in mind that it routes to an external Engine which might make it kinda more complicated. The feature test seemed an easy way around this (simply test whether the ressource is reachable).
2) In Minitest I could do e.g. the following, which was working fine
require_relative '../test_helper'
feature 'Sidekiq dashboard' do
scenario 'Dashboard cannot be reached as guest user' do
assert_raise ActionController::RoutingError do
visit sidekiq_web_path
end
end
scenario 'Dashboard cannot be reached as regular user' do
login_as_user
assert_raise ActionController::RoutingError do
visit sidekiq_web_path
end
end
scenario 'Dashboard can be reached as admin' do
login_as_admin
assert_nothing_raised do
visit sidekiq_web_path
end
end
end
I tried to convert it to RSpec directly like so
scenario 'Dashboard can be reached as user' do
login_as_user
expect {
visit sidekiq_web_path
}.to raise_error(ActionController::RoutingError)
end
which produces the following error
Failures:
1) Sidekiq dashboard Dashboard cannot be reached as regular user
Got 1 failure and 1 other error:
1.1) Failure/Error:
expect {
visit sidekiq_web_path
}.to raise_error(ActionController::RoutingError)
expected ActionController::RoutingError but nothing was raised
# ./spec/features/sidekiq_monitoring_spec.rb:12:in `block (2 levels) in <top (required)>'
1.2) Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
ActionController::RoutingError:
No route matches [GET] "/sidekiq"
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:38:in `call_app'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:20:in `block in call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:20:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/request_store-1.3.2/lib/request_store/middleware.rb:9:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/methodoverride.rb:22:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/runtime.rb:18:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/lock.rb:17:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/sendfile.rb:113:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/engine.rb:518:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/application.rb:165:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/urlmap.rb:66:in `block in call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/urlmap.rb:50:in `each'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/urlmap.rb:50:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/capybara-2.12.0/lib/capybara/server.rb:43:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/handler/webrick.rb:88:in `service'
# ------------------
# --- Caused by: ---
# Capybara::CapybaraError:
# Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/capybara-2.12.0/lib/capybara/session.rb:129:in `raise_server_error!'
Why does this not work in RSpec?
Thanks in advance, all the best,
Andi
When testing with a JS capable driver (poltergeist, etc) Capybara runs the app in a separate server thread. Because of that errors raised in the app don't automatically get seen in the test code. To overcome that Capybara stores any errors raised in the server and, due to the asynchronous nature of commands when using a JS capable driver, re-raises them in the test code the next time it attempts to interact with Capybara. Because of that you would need to have a second interaction inside the block you expect to raise the error
expect {
visit sidekiq_web_path
expect(page).to have_text("Something") # the error will actually raise here
}.to raise_error(ActionController::RoutingError)
I have no clue why it was working for you like that with minitest, and would have to go look at the Minitest code to attempt to figure out why. I would expect your tests to have worked correctly if you were running those tests with the rack_test driver since it doesn't run a separate thread and just calls directly into the app (you claim not to have changed that though).
Note: This really isn't the sort of thing that you should be checking in a feature test, instead it would be better to check for what is displayed on the page when permission is denied/allowed instead of a specific error class that is raised

capybara have_title NoMethodError

At the moment, this is a simple project - just a couple of static pages. I'm developing a generic test framework but am struggling to differentiate between the different test options. I have added Rspec, Capybara, Faker, Factory Girl, Spring, and shoulda (though I'm not using the shoulda matchers at the moment).
I have this controller test file:
require 'rails_helper'
RSpec.describe StaticPagesController, type: :controller do
describe "GET #a_page" do
before(:each) { get :a_page }
it "returns http success" do
expect(response).to have_http_status(:success)
end
it "has a page title Static Site" do
expect(response).to have_title('Static Site')
end
end
end
When this runs through guard, it throws an error stack:
23:13:39 - INFO - Run all
23:13:39 - INFO - Running all specs
Running via Spring preloader in process 4498
Running via Spring preloader in process 4506
/home/steve/workspaces/static_site/db/schema.rb doesn't exist yet. Run `rake db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter /home/steve/workspaces/static_site/config/application.rb to limit the frameworks that will be loaded.
.F
Failures:
1) StaticPagesController GET #a_page has a page title Static Site
Failure/Error: expect(response).to have_title('Static Site')
NoMethodError:
undefined method `match' for nil:NilClass
Did you mean? catch
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/queries/title_query.rb:18:in `resolves_for?'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/node/document_matchers.rb:20:in `block in assert_title'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/node/simple.rb:144:in `synchronize'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/node/document_matchers.rb:19:in `assert_title'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/rspec/matchers.rb:105:in `matches?'
# ./spec/controllers/static_pages_controller_spec.rb:34:in `block (3 levels) in <top (required)>'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb:18:in `call'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/command_wrapper.rb:38:in `call'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:191:in `block in serve'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:161:in `fork'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:161:in `serve'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:131:in `block in run'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:125:in `loop'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:125:in `run'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application/boot.rb:19:in `<top (required)>'
# -e:1:in `<main>'
Finished in 0.029 seconds (files took 2.54 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/controllers/static_pages_controller_spec.rb:33 # StaticPagesController GET #a_page has a page title Static Site
The first test runs OK and, without the second, I get a clean result. I've spent a lot of time going over my config and it looks OK. I have also looked at the docs and some support sites.
Can anybody help out?
Capybara matchers need to be called against a html/xml document element (or a string that parses into a document), not against a response object. By default Capybaras matchers are normally only available in feature and view specs (not controller) was there a particular reason you included them into controller specs? Verifying a pages title really should lean more towards being a view spec than controller (by default views aren't rendered in controller specs - https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs).
As #Thomas Walpole pointed out, Capybara is not enabled in controller specs by default. One option is to change the type of the test to feature (type: :feature).
To make Capybara's built-in matcher have_title work, you need to use visit from Capybara's API, not get from ActionDispatch::IntegrationTest.

Resources