Skip to content

Instantly share code, notes, and snippets.

@johnmeehan
Last active March 1, 2020 22:10
Show Gist options
  • Save johnmeehan/b7795f08c037a13f5b694ee8209be65e to your computer and use it in GitHub Desktop.
Save johnmeehan/b7795f08c037a13f5b694ee8209be65e to your computer and use it in GitHub Desktop.
Browserstack + Rails5 + RSpec + Capybara

Browserstack

My setup for configuring a Rails app with RSpec Feature tests that uses Capabara to test on a browserstack remote browser.

The browserstack docs only show either an RSpec or a Capabara setup.

What I wanted:

  1. In development run my tests quickly with poltergiest.
  2. Run nightly builds on CI to test against different web browsers using browserstack.
  3. Have the option to visually run my tests with selenium with Chrome, Firefox etc.

Running Test Headless or in a Browser

What browsers are used in the test can now be changed when using the selenium driver. The test suite can be changed by setting optional Environment variables TEST_BROWSER.

If TEST_BROWSER is set it will determin whether to use selenium or poltergiest.

The default is to run poltergiest.

TEST_BROWSER=chrome          rspec spec/some_spec.rb
TEST_BROWSER=headless_chrome rspec spec/some_spec.rb
TEST_BROWSER=firefox         guard

rspec spec/some_spec.rb      # Defaults

Requirements:

Each browser requires its own driver installed.

  • Firefox requires geckodriver

    brew install geckodriver

  • Chrome Requires ChromeDriver

    This is setup by chromedriver-helper in the Gemfile!

#TODO: Circleci will have to pre install these!

Running options

Currently we can run our tests in three modes:

  1. Headless :

    • poltergiest ( Default and fastest )

      rspec

    • selenium with headless chrome browser using ChromeDriver ( Not fully compatible yet. )

      TEST_BROWSER=headless_chrome rspec

  2. In Browser:

    • selenium driver with chrome browser

      TEST_BROWSER=chrome rspec

      TEST_BROWSER=chrome guard

    • selemium driver with firefox browser

      TEST_BROWSER=firefox rspec

      TEST_BROWSER=firefox guard

  3. On a remote browser

    • on browserstack

      TEST_BROWSER=browserstack rspec

      rake browserstack:local

Current Compatibility Issues:

  • Only on poltergiest do all the tests pass.
  • headless chrome cannot do save_and_open_screenshot
  • chrome cannot .trigger('click')
  • page.driver.network_traffic only works in poltergeist.

Running tests on a remote browser

We can use browserstack to run our test against any browser. Edge and Firefox are free to use on browserstack.

Running modes:

  • environment variables need to be set for it to run. BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY. Get these on the browserstack settings page.
  • The browser used can be set in the browserstack config files.
  • It is set up to run against the latest version of whatever browser selected.
  • It can be configured to use a specific bowser by setting “browser_version”: “48”
Local (Default)

Allows you to run the server locally on your machine and run all the feature tests on the remote browsers on browserstacks.

  • rake browserstack:local

The rake task sets a ENV['TEST_BROWSER'] = 'browserstack' which triggers the switchover in rails_helper.rb to run the tests on browserstack.

Notes:

  • We have a window of 2 hours to finish our tests before our session on browserstack ends.
  • It is REALLY slow to test on a remote browser. Only makes sense to run on a nightly build.

Open localhost on a remote browser

Steps:

  1. Start the rails sever locally.
  2. Added the Chrome exension for browserstack.
  3. Open any web browser on browserstack and turn on local server testing.
  4. Navigate to http://localhost:3000 on the browserstack browser.
  5. Browser within a browser inception.
  • Great for clicking around the development site in any browser.
# lib/tasks/browserstack.rake
unless Rails.env.production?
begin
require 'rake'
require 'json'
require 'rspec/core/rake_task'
namespace :browserstack do
RSpec::Core::RakeTask.new(:local) do |t|
ENV['CONFIG_NAME'] ||= "local"
ENV['TEST_BROWSER'] = "browserstack"
t.pattern = Dir.glob('spec/features/**/*_spec.rb')
t.rspec_opts = '--format documentation'
t.rspec_opts = "--tag js"
# t.rspec_opts = "--tag type:feature"
t.verbose = false
end
task :default => :local
RSpec::Core::RakeTask.new(:local_smoketest) do |t|
ENV['CONFIG_NAME'] ||= "local"
ENV['TEST_BROWSER'] = "browserstack"
t.pattern = Dir.glob('spec/browserstack/browserstack_local_spec.rb')
t.rspec_opts = '--format documentation'
t.verbose = false
end
RSpec::Core::RakeTask.new(:single) do |t|
ENV['CONFIG_NAME'] ||= "single"
t.pattern = Dir.glob('spec/browserstack/browserstack_single_spec.rb')
t.rspec_opts = '--format documentation'
t.verbose = false
end
task :test do |t, args|
Rake::Task["local_smoketest"].invoke
end
rescue => e
raise e.message
end
end
# spec/browserstack/browserstack_local_spec.rb
require 'rails_helper'
# BrowserStack provides a feature of Local Testing via Command Line Tunnel.
# To check if tunnel is connected or not, their JavaScript(JS) tries to talk to a http server which runs on port 45691.
feature "BrowserStack Local Setup" do
scenario "check the tunnel between local server and browserstack browser" do
visit "http://bs-local.com:45691/check"
expect(page).to have_content(/Up and running/)
end
end
# config/browserstack/local.config.yml
---
# Run the severs locally and used the browsers on browserstack
# Allows you access http://localhost:3000 on browserstack
server: "hub-cloud.browserstack.com"
user: <%= ENV['BROWSERSTACK_USERNAME'] %>
key: <%= ENV["BROWSERSTACK_ACCESS_KEY"] %>
common_caps:
"project": "Project 1"
"build": "browserstack-local"
"browserstack.debug": true
browser_caps:
-
"browser": "edge"
"os": "Windows"
"os_version": "10"
"browserstack.local": true
# spec/poltergeist_setup.rb
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(
app,
window_size: [1200, 1100],
timeout: TIMEOUT,
phantomjs: Phantomjs.path,
)
end
Capybara.default_driver = :poltergeist
Capybara.current_driver = :poltergeist
Capybara.javascript_driver = :poltergeist
puts "### Running tests with #{Capybara.current_driver} ###"
# spec/rails_helper.rb
TIMEOUT = 15
ENV["RAILS_ENV"] ||= "test"
test_browser = ENV['TEST_BROWSER']
require File.expand_path("../../config/environment", __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
abort("The Rails environment is running in development mode!") if Rails.env.development?
require "spec_helper"
require "rspec/rails"
require "factory_girl_rails"
require "awesome_print"
require "capybara/rails"
require "capybara/rspec"
require "database_cleaner"
require "email_spec"
require "ffaker"
require "pry"
require "#{Rails.root}/lib/rack/frontend_test_app"
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
Rails.application.routes.default_url_options = Rails.application.config.action_mailer.default_url_options
Capybara.app = Rack::FrontendTestApp.new
Capybara.default_max_wait_time = TIMEOUT
Capybara.always_include_port = true
# if we specify a browser we are using selenium.
if test_browser
require 'selenium/webdriver'
test_driver_name = test_browser.gsub('headless_', '')
test_driver_sym = test_driver_name.to_sym
case test_browser
when 'headless_chrome'
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( chromeOptions: { args: %w(headless disable-gpu) } )
Capybara.register_driver test_driver_sym do |app|
Capybara::Selenium::Driver.new(app, browser: test_driver_sym, desired_capabilities: capabilities )
end
Capybara.default_driver = test_driver_sym
Capybara.javascript_driver = test_driver_sym
Capybara.current_driver = test_driver_sym
when 'browserstack'
require 'yaml'
require 'browserstack/local'
# monkey patch to avoid reset sessions
class Capybara::Selenium::Driver < Capybara::Driver::Base
def reset!
if @browser
@browser.navigate.to('about:blank')
end
end
end
TASK_ID = (ENV['TASK_ID'] || 0).to_i
CONFIG_NAME = ENV['CONFIG_NAME'] || 'local'
CONFIG = YAML.load(File.read(File.join(File.dirname(__FILE__), "../config/browserstack/#{CONFIG_NAME}.config.yml")))
CONFIG['user'] = ENV['BROWSERSTACK_USERNAME'] || CONFIG['user']
CONFIG['key'] = ENV['BROWSERSTACK_ACCESS_KEY'] || CONFIG['key']
Capybara.register_driver :browserstack do |app|
@caps = CONFIG['common_caps'].merge(CONFIG['browser_caps'][TASK_ID])
# Code to start browserstack local before start of test
if @caps['browserstack.local'] && @caps['browserstack.local'].to_s == 'true';
@bs_local = BrowserStack::Local.new
bs_local_args = {"key" => "#{CONFIG['key']}"}
@bs_local.start(bs_local_args)
end
Capybara::Selenium::Driver.new(app,
:browser => :remote,
:url => "http://#{CONFIG['user']}:#{CONFIG['key']}@#{CONFIG['server']}/wd/hub",
:desired_capabilities => @caps
)
end
Capybara.default_driver = :browserstack
Capybara.javascript_driver = :browserstack
Capybara.current_driver = :browserstack
Capybara.run_server = true #Whether start server when testing
# Capybara.server_port = 3000
# Capybara.app_host = "http://127.0.0.1:3000"
# Code to stop browserstack local after end of test
at_exit do
@bs_local.stop unless @bs_local.nil?
end
puts "### Running specs with #{Capybara.current_driver} ###"
else
#chrome, firefox, safari, opera
# you will need to have their appropriate driver installed locally.
Capybara.register_driver test_driver_sym do |app|
Capybara::Selenium::Driver.new(app, browser: test_driver_sym)
end
Capybara.default_driver = test_driver_sym
Capybara.javascript_driver = test_driver_sym
Capybara.current_driver = test_driver_sym
puts "### Running specs with #{Capybara.current_driver} ###"
end
else
require_relative './poltergeist_setup.rb'
end
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.include Capybara::DSL
config.include ApiHelpers
config.include BackgroundJobHelpers
config.include CommonHelpers
config.include Features
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
config.order = :random
config.tty = true
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding disabled: true
end
@valdemarua
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment