1

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.

Erik Jacobs
  • 841
  • 3
  • 7
  • 19
  • I don't know why, but Firefox Headless seemed to be the problem. For whatever reason, it works with Turnip, but it did not work with pure rspec. When I removed the use of Firefox and went to strictly Chrome headless, it worked. – Erik Jacobs Sep 09 '20 at 12:17

1 Answers1

0

I had a similar issue when running tests in a docker container.

The overhead of the browser was causing the container to run out of memory, and as a result the brower was getting killed.

If this applies to your situation you can get round this by increasing the shared memory space. This is how you'd do it with a docker-compose file:

version: '3.8'
services:
  ...
  web:
    shm_size: '256mb'
    ...
  ...

I've written more about dealing with Selenium::WebDriver::Error::InvalidSessionIdError here: https://thecurve.io/dealing-with-cryptic-seleniumwebdrivererrorinvalidsessioniderror-errors/