Skip to content

Instantly share code, notes, and snippets.

@palkan
Last active Jan 27, 2021
Embed
What would you like to do?
[Brainwashing Rails] system_helper.rb
# frozen_string_literal: true
require "rails_helper"
require "capybara"
require "capybara/email/rspec"
require "selenium-webdriver"
# Make capybara-email compatible with Capybara 3.17+
# PR: https://github.com/DavyJonesLocker/capybara-email/pull/77
Capybara::Email::Node.class_eval do
def disabled?
string_node.disabled?
end
end
# Capybara settings (not covered by Rails system tests)
# Use 0.0.0.0:3002 to make it possible to access
# the test server from the outside world
Capybara.server_host = `hostname`.strip&.downcase || "0.0.0.0"
Capybara.server_port = 3002
# Don't wait too long in `have_xyz` matchers
Capybara.default_max_wait_time = 2
# Normalizes whitespaces when using `has_text?` and similar matchers
# (the old Capybara behaviour)
Capybara.default_normalize_ws = true
# Where to store artefacts (e.g. screenshots)
Capybara.save_path = "./tmp/capybara_output"
# Save capybara screenshots to circleci artifacts dir if present
if ENV["CIRCLE_ARTIFACTS"]
Capybara.save_path = ENV["CIRCLE_ARTIFACTS"]
end
# Re-raise server errors instead of ignoring them
Capybara.raise_server_errors = true
CHROME_HEADLESS = !%w[0 no false].include?(ENV["HEADLESS"])
# Options to run local chrome with
# See https://github.com/GoogleChrome/puppeteer/issues/1645#issuecomment-356060348
CHROME_OPTIONS = %w[
--window-size=1400,1400
--no-sandbox
--disable-dev-shm-usage
--disable-default-apps
--disable-extensions
--disable-sync
--disable-gpu
--disable-translate
--hide-scrollbars
--metrics-recording-only
--mute-audio
--no-first-run
].tap do |options|
options << "--headless" if CHROME_HEADLESS
end.freeze
REMOTE_CHROME_URL = ENV["SELENIUM_DRIVER_URL"]
REMOTE_CHROME_HOST, REMOTE_CHROME_PORT =
if REMOTE_CHROME_URL
URI.parse(REMOTE_CHROME_URL).yield_self do |uri|
[uri.host, uri.port]
end
end
# Check whether the remote chrome is running and configure the Capybara
# driver for it.
remote_chrome =
begin
if REMOTE_CHROME_URL.nil?
false
else
Socket.tcp(REMOTE_CHROME_HOST, REMOTE_CHROME_PORT, connect_timeout: 1).close
true
end
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
false
end
# Fallback to local chrome
CHROME_DRIVER_OPTIONS =
if remote_chrome
{
browser: :remote,
url: REMOTE_CHROME_URL,
desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
args: CHROME_OPTIONS
}
)
}
else
$stdout.puts "⚠️ Make sure you have Chrome installed in your system or " \
"launch it via Docker by running: `docker-compose up selenium`"
{
browser: :chrome,
options: Selenium::WebDriver::Chrome::Options.new(
args: CHROME_OPTIONS
)
}
end
# Make browser slow down execution to see what's going on
# in the browser (when running non-Headless)
module SlomoBridge
TIMEOUT = ENV.fetch("SLOMO", "0").to_i / 10.0
def execute(*)
sleep TIMEOUT
super
end
end
# Register custom driver to support uploading
# files from other containers (`file_detector` option, for example)
Capybara.register_driver :headless_chrome do |app|
Capybara::Selenium::Driver.new(
app,
CHROME_DRIVER_OPTIONS
).tap do |driver|
# Enable slomo mode
driver.browser.send(:bridge).singleton_class.prepend(SlomoBridge) unless CHROME_HEADLESS
# File detector is only available for remote driver
next unless CHROME_DRIVER_OPTIONS.fetch(:browser) == :remote
driver.browser.file_detector = lambda do |args|
str = args.first.to_s
str if File.exist?(str)
end
end
end
Capybara.javascript_driver = Capybara.default_driver = :headless_chrome
# Rails is not flexible enough; at least for now(
module BetterRailsSystemTests
def absolute_image_path
Rails.root.join("#{Capybara.save_path}/#{image_name}.png")
end
def display_image
relative_image_path = Pathname.new(image_path).relative_path_from(Rails.root)
"🖼 [Screenshot]: #{relative_image_path}\n"
end
end
RSpec.configure do |config|
config.define_derived_metadata(file_path: /spec\/system/) do |metadata|
metadata[:browser] = true
end
config.filter_run_excluding(browser: true) unless ARGV.join(" ").match?(/spec\/system/)
# Skip features configuration if we exclude system specs
# (excluded by default).
#
# This file is required even if tags filter has been added,
# but we don't need this configuration if we don't run these specs.
next if config.filter.opposite.rules[:browser] == true
$stdout.puts "\n🚒 Systems test configuration is loaded!\n"
config.include BetterRailsSystemTests, type: :system
config.before(:suite) do
if Webpacker.dev_server.running?
$stdout.puts "\n⚙️ Webpack dev server is running! Skip assets compilation.\n"
next
else
$stdout.puts "\n🐢 Precompiling assets.\n"
original_stdout = $stdout.clone
# Use test-prof now 'cause it couldn't be monkey-patched (e.g., by Timecop or similar)
start = TestProf.now
begin
# Silence Webpacker output
$stdout.reopen(File.new('/dev/null', 'w'))
# next 3 lines to compile webpacker before running our test suite
require 'rake'
Rails.application.load_tasks
Rake::Task["webpacker:compile"].execute
ensure
$stdout.reopen(original_stdout)
$stdout.puts "Finished in #{(TestProf.now - start).round(2)} seconds"
end
end
end
# Make urls in mailers contain the correct server host
config.around(:each, type: :system) do |ex|
was_host, Rails.application.default_url_options[:host] = Rails.application.default_url_options[:host], Capybara.server_host
ex.run
Rails.application.default_url_options[:host] = was_host
end
config.before(:each, type: :system) do
# Rails sets host to `127.0.0.1` for every test by default.
host! "http://#{Capybara.server_host}"
driven_by :headless_chrome
end
if defined?(WebMock)
config.before(:each, type: :system) do
WebMock.allow_net_connect!(net_http_connect_on_start: true)
end
end
if defined?(VCR)
config.around(:each, type: :system) do |ex|
VCR.turned_off(&ex)
end
end
config.after(:each, type: :system) do
if defined?(WebMock)
# There could still be some requests from browser, 'cause
# they're made asynchrounously
allowed = []
allowed << REMOTE_CHROME_HOST unless REMOTE_CHROME_HOST.nil?
WebMock.disable_net_connect!(allow_localhost: true, allow: allowed)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment