Edit: The Turnip piece is a red herring. Running rspec natively by itself remains an issue.
Example invocation:
bundle exec rspec spec/features/subscription_spec.rb:33
Errors:
Failures:
1) Subscription should redirect to back for free subscription
Got 0 failures and 2 other errors:
1.1) Failure/Error: visit subscription_status_path
Selenium::WebDriver::Error::NoSuchWindowError:
Browsing context has been discarded
# WebDriverError@chrome://marionette/content/error.js:175:5
# NoSuchWindowError@chrome://marionette/content/error.js:409:5
# assert.that/<@chrome://marionette/content/assert.js:428:13
# assert.open@chrome://marionette/content/assert.js:183:72
# GeckoDriver.prototype.get@chrome://marionette/content/driver.js:1109:10
# despatch@chrome://marionette/content/server.js:305:40
# execute@chrome://marionette/content/server.js:275:16
# onPacket/<@chrome://marionette/content/server.js:248:20
# onPacket@chrome://marionette/content/server.js:249:9
# _onJSONObjectReady/<@chrome://marionette/content/transport.js:503:20
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/response.rb:72:in `assert_ok'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:88:in `new'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:88:in `create_response'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/default.rb:114:in `request'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:64:in `call'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/bridge.rb:167:in `execute'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/w3c/bridge.rb:567:in `execute'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/w3c/bridge.rb:59:in `get'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/common/navigation.rb:32:in `to'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/selenium/driver.rb:71:in `visit'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/session.rb:278:in `visit'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/dsl.rb:58:in `block (2 levels) in <module:DSL>'
# ./spec/features/subscription_spec.rb:36:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:60:in `block (3 levels) in <top (required)>'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/generic/base.rb:16:in `cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:87:in `block (2 levels) in cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:88:in `cleaning'
# ./spec/rails_helper.rb:59:in `block (2 levels) in <top (required)>'
1.2) Failure/Error: example.run
Selenium::WebDriver::Error::UnknownError:
Failed to decode response from marionette
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/response.rb:72:in `assert_ok'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:88:in `new'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:88:in `create_response'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/default.rb:114:in `request'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:64:in `call'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/bridge.rb:167:in `execute'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/w3c/bridge.rb:567:in `execute'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/w3c/bridge.rb:92:in `alert_text'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/common/alert.rb:27:in `initialize'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/common/target_locator.rb:107:in `new'
# /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/common/target_locator.rb:107:in `alert'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/selenium/driver_specializations/firefox_driver.rb:48:in `reset!'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/session.rb:130:in `reset!'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara.rb:322:in `block in reset_sessions!'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara.rb:322:in `reverse_each'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara.rb:322:in `reset_sessions!'
# /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/rspec.rb:18:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:60:in `block (3 levels) in <top (required)>'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/generic/base.rb:16:in `cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:87:in `block (2 levels) in cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:88:in `cleaning'
# ./spec/rails_helper.rb:59:in `block (2 levels) in <top (required)>'
Finished in 9.46 seconds (files took 4.92 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/features/subscription_spec.rb:33 # Subscription should redirect to back for free subscription
Traceback (most recent call last):
12: from /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/selenium/driver.rb:458:in `block in setup_exit_handler'
11: from /usr/local/bundle/gems/capybara-3.32.2/lib/capybara/selenium/driver.rb:243:in `quit'
10: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/firefox/marionette/driver.rb:56:in `quit'
9: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/common/driver.rb:168:in `quit'
8: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/w3c/bridge.rb:155:in `quit'
7: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/w3c/bridge.rb:567:in `execute'
6: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/bridge.rb:167:in `execute'
5: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:64:in `call'
4: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/default.rb:114:in `request'
3: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:88:in `create_response'
2: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/http/common.rb:88:in `new'
1: from /usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
/usr/local/bundle/gems/selenium-webdriver-3.142.7/lib/selenium/webdriver/remote/response.rb:72:in `assert_ok': Tried to run command without establishing a connection (Selenium::WebDriver::Error::InvalidSessionIdError)
Gemfile:
...
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
# gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'pry', '~> 0.11.3'
gem 'pry-byebug'
gem 'pry-rails'
gem 'pry-remote'
gem 'faker'
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '3.32.2'
gem 'webdrivers', '4.4.1'
gem 'rspec-rails'
gem 'factory_bot_rails'
gem 'rails-controller-testing'
gem 'shoulda-matchers'
# Cucumber, webmock, etc
gem 'webmock', '3.8.3'
end
group :test do
gem 'database_cleaner'
gem 'elasticsearch-extensions'
gem 'turnip'
end
...
turnip_helper.rb:
require 'rails_helper'
require 'turnip/capybara' # to use Capybara DSL methods in steps
require 'sidekiq/testing'
Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }
rails_helper.rb:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'devise'
require 'webdrivers'
require 'factory_bot_rails'
require 'faker'
require 'capybara'
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
Capybara.register_driver :firefox_headless do |app|
options = ::Selenium::WebDriver::Firefox::Options.new
options.args << '--headless'
options.args << '--width=1920'
options.args << '--height=1080'
Capybara::Selenium::Driver.new(app, browser: :firefox, options: options)
end
Capybara.javascript_driver = :firefox_headless
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
end
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :request
config.include Devise::Test::IntegrationHelpers, type: :feature
config.include FactoryBot::Syntax::Methods
end
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :active_record
with.library :active_model
with.library :action_controller
with.library :rails
end
end
spec_helper.rb:
require 'rake'
require 'webmock/rspec'
require 'sidekiq/testing'
RSpec.configure do |config|
config.before(:each) do
Sidekiq::Worker.clear_all
Sidekiq::Queue.all.each(&:clear)
Sidekiq::RetrySet.new.clear
Sidekiq::ScheduledSet.new.clear
Sidekiq::DeadSet.new.clear
WebMock.disable!
end
config.before :each, elasticsearch: true do
ActiveRecord::Base.descendants.each do |model|
if model.respond_to?(:__elasticsearch__)
begin
model.__elasticsearch__.create_index! index: model.index_name
model.__elasticsearch__.refresh_index!
rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
rescue => e
STDERR.puts "There was an error creating the elasticsearch index for #{model.name}: #{e.inspect}"
end
end
end
end
config.after :each, elasticsearch: true do
ActiveRecord::Base.descendants.each do |model|
if model.respond_to?(:__elasticsearch__)
begin
model.__elasticsearch__.client.indices.delete index: model.index_name
rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
rescue => e
STDERR.puts "There was an error removing the elasticsearch index for #{model.name}: #{e.inspect}"
end
end
end
end
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
end
subscription_spec.rb:33
scenario 'should redirect to back for free subscription', js: true do
blah blah
end
If I run an rspec test that doesn't declare js: true
, it works.
I did some more exploration, and it turns out it's failing even without running after Turnip. So the Turnip piece may be a red herring.
At this point it's purely rspec/capybara that's failing (with the same errors from my stackoverflow post).
Interestingly, here's one particular task:
scenario 'should see an error message for existing subscription', js: true do
premium_user.confirm
sign_in_with premium_user.email, 'password'
visit '/subscriptions'
expect(page).to have_content 'You already have a subscription'
end
"sign_in_with" is a helper that uses Capybara syntax:
module Features
module SessionHelpers
def sign_up_with(email, password, confirmation)
visit new_user_registration_path
fill_in 'Email', with: email
fill_in 'user[password]', with: password
fill_in 'user[password_confirmation]', :with => confirmation
click_button 'Sign up!'
end
def sign_in_with(email, password)
visit new_user_session_path
fill_in 'Email', with: email
fill_in 'Password', with: password
click_button 'Sign in'
end
end
end
This (the sign_in_with
helper) works fine. If I use a binding.pry in this task, I see Firefox exists during "sign_in_with". But immediately after that step completes, Firefox goes away, and then the "visit" fails because Firefox is gone.