/system_helper.rb Secret
Last active
September 8, 2022 12:52
Star
You must be signed in to star a gist
[Brainwashing Rails] system_helper.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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