Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduction of system tests in Capybara #357

Merged
merged 6 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require:
- rubocop-capybara
- rubocop-factory_bot
- rubocop-performance
- rubocop-rails
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ group :development, :test do
gem 'bootsnap', '>= 1.16.0', require: false
# Call 'byebug' in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'capybara'
gem 'cuprite'
gem 'dotenv-rails'
gem 'pry-byebug', '~> 3.9'
gem 'rails-controller-testing'
gem 'rspec-rails', '~> 6.0.3'
gem 'selenium-webdriver'
end

group :test do
Expand Down
30 changes: 23 additions & 7 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ GEM
capistrano-rails (1.6.3)
capistrano (~> 3.1)
capistrano-bundler (>= 1.1, < 3)
capybara (3.39.2)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
carrierwave (3.0.3)
activemodel (>= 6.0.0)
activesupport (>= 6.0.0)
Expand All @@ -142,6 +151,9 @@ GEM
colorize (0.8.1)
concurrent-ruby (1.2.2)
crass (1.0.6)
cuprite (0.14.3)
capybara (~> 3.0)
ferrum (~> 0.13.0)
database_cleaner (2.0.2)
database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.1.0)
Expand Down Expand Up @@ -191,6 +203,11 @@ GEM
fasterer (0.10.1)
colorize (~> 0.7)
ruby_parser (>= 3.19.1)
ferrum (0.13)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
webrick (~> 1.7)
websocket-driver (>= 0.6, < 0.8)
ffi (1.16.3)
flay (2.13.1)
erubi (~> 1.10)
Expand Down Expand Up @@ -264,6 +281,7 @@ GEM
actionmailer (>= 5.2)
activemodel (>= 5.2)
marcel (1.0.2)
matrix (0.4.2)
memory_profiler (1.0.1)
meta-tags (2.18.0)
actionpack (>= 3.2.0, < 7.1)
Expand Down Expand Up @@ -441,7 +459,6 @@ GEM
simplecov (>= 0.22.0)
tty-which (~> 0.5.0)
virtus (~> 2.0)
rubyzip (2.3.2)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
Expand All @@ -459,10 +476,6 @@ GEM
tilt
scss_lint (0.60.0)
sass (~> 3.5, >= 3.5.5)
selenium-webdriver (4.13.1)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
sexp_processor (4.17.0)
shoulda-matchers (5.3.0)
activesupport (>= 5.2.0)
Expand Down Expand Up @@ -521,10 +534,12 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
websocket (1.2.10)
webrick (1.8.1)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.12)

PLATFORMS
Expand All @@ -542,8 +557,10 @@ DEPENDENCIES
bullet
byebug
capistrano-rails (~> 1.6)
capybara
carrierwave (~> 3.0)
colored
cuprite
database_cleaner (~> 2.0.1)
derailed_benchmarks
dockerfile-rails (>= 1.5)
Expand Down Expand Up @@ -585,7 +602,6 @@ DEPENDENCIES
rubycritic
sass-rails (~> 6.0)
scss_lint
selenium-webdriver
shoulda-matchers
simple_form (~> 5.2)
slim-rails (~> 3.6)
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
RSpec.configure do |config|
config.filter_run :focus
config.order = :random
config.profile_examples = true
config.run_all_when_everything_filtered = true
end
62 changes: 62 additions & 0 deletions spec/support/system/capybara_setup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Make server listening on all hosts
Capybara.server_host = '0.0.0.0'
Capybara.server_port = 3001

# Use a hostname accessible from the outside world
Capybara.app_host = "http://#{ENV.fetch('APP_HOST') { `hostname`.strip&.downcase || '0.0.0.0' }}"

# Which domain to use when setting cookies directly in tests.
CAPYBARA_COOKIE_DOMAIN = URI.parse(Capybara.app_host).host.then do |host|
# If host is a top-level domain
next host unless host.include?('.')

".#{host}"
end

# Usually, especially when using Selenium, developers tend to increase the max wait time.
# With Cuprite, there is no need for that.
# We use a Capybara default value here explicitly.
Capybara.default_max_wait_time = 2

# Normalize whitespaces when using `has_text?` and similar matchers,
# i.e., ignore newlines, trailing spaces, etc.
# That makes tests less dependent on slightly UI changes.
Capybara.default_normalize_ws = true

# Where to store system tests artifacts (e.g. screenshots, downloaded files, etc.).
# It could be useful to be able to configure this path from the outside (e.g., on CI).
# Capybara.save_path = ENV.fetch("CAPYBARA_ARTIFACTS", "./tmp/capybara")

Capybara.singleton_class.prepend(
Module.new do
attr_accessor :last_used_session

def using_session(name, &)
self.last_used_session = name
super
ensure
self.last_used_session = nil
end
end
)

RSpec.configure do |config|
config.include ActionView::RecordIdentifier, type: :system

# 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
driven_by :rack_test
end

config.before(:each, :js, type: :system) do
# Use JS driver always
driven_by Capybara.javascript_driver
end
end
72 changes: 72 additions & 0 deletions spec/support/system/cuprite_setup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Cuprite is a modern Capybara driver which uses Chrome CDP API
# instead of Selenium & co.

remote_chrome_url = ENV.fetch('CHROME_URL') { 'http://0.0.0.0:3000' }

# Chrome doesn't allow connecting to CDP by hostnames (other than localhost),
# but allows using IP addresses.
if remote_chrome_url&.match?(/host.docker.internal/)
require 'resolv'

uri = URI.parse(remote_chrome_url)
ip = Resolv.getaddress(uri.host)

remote_chrome_url = remote_chrome_url.sub('host.docker.internal', ip)
end

REMOTE_CHROME_URL = remote_chrome_url
REMOTE_CHROME_HOST, REMOTE_CHROME_PORT =
if REMOTE_CHROME_URL
URI.parse(REMOTE_CHROME_URL).then do |remote_uri|
[remote_uri.host, remote_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

remote_options = remote_chrome ? { url: REMOTE_CHROME_URL } : {}

require 'capybara/cuprite'

Capybara.register_driver(:cuprite) do |app|
Capybara::Cuprite::Driver.new(
app,
**{
window_size: [1200, 800],
browser_options: remote_chrome ? { 'no-sandbox' => nil } : {},
inspector: true
}.merge(remote_options)
)
end

Capybara.default_driver = Capybara.javascript_driver = :cuprite

# Add shortcuts for cuprite-specific debugging helpers
module CupriteHelpers
def pause
page.driver.pause
end

def debug(binding = nil)
$stdout.puts '🔎 Open Chrome inspector at http://localhost:3333'
return binding.break if binding

page.driver.pause
end
end

RSpec.configure do |config|
config.include CupriteHelpers, type: :system
end
29 changes: 29 additions & 0 deletions spec/support/system/precompile_assets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Precompile assets before running tests to avoid timeouts.
# Do not precompile if webpack-dev-server is running
# (NOTE: MUST be launched with RAILS_ENV=test)
RSpec.configure do |config|
config.before(:suite) do
examples = RSpec.world.filtered_examples.values.flatten
has_no_system_tests = examples.none? { |example| example.metadata[:type] == :system }

if has_no_system_tests
$stdout.puts "\n🚀️️ No system test selected. Skip assets compilation.\n"
next
end

$stdout.puts "\n🐢 Precompiling assets.\n"
original_stdout = $stdout.clone

start = Time.current
begin
$stdout.reopen(File.new('/dev/null', 'w'))

require 'rake'
Rails.application.load_tasks
Rake::Task['assets:precompile'].invoke('silent')
ensure
$stdout.reopen(original_stdout)
$stdout.puts "Finished in #{(Time.current - start).round(2)} seconds"
end
end
end
18 changes: 18 additions & 0 deletions spec/system/check_version_of_javascript_libraries_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'system_helper'

RSpec.describe 'Application loads JavaScript libraries' do
it 'exist and allow us to check their versions', :js do
visit root_url

browser = page.driver.browser
bootstrap_version = browser.evaluate('bootstrap.Tooltip.VERSION')
jquery_version = browser.evaluate('jQuery().jquery')
leaflet_version = browser.evaluate('L.version')

aggregate_failures('verify version of JavaScript libraries') do
expect(bootstrap_version).to eq('5.3.1')
expect(jquery_version).to eq('3.7.1')
expect(leaflet_version).to eq('1.9.4')
end
end
end
16 changes: 16 additions & 0 deletions spec/system/seeds_loading_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'system_helper'

RSpec.describe 'Application loads the seeds' do
it 'is done correctly and homepage displays team member profiles' do
Rails.application.load_seed

visit '/'

aggregate_failures('verify first names of all team members') do
expect(page).to have_content 'Agnieszka'
expect(page).to have_content 'Alex'
expect(page).to have_content 'Anna'
expect(page).to have_content 'Grzegorz'
end
end
end
3 changes: 3 additions & 0 deletions spec/system_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require 'rails_helper'

Dir[Rails.root.join('spec/system/support/**/*.rb')].each { |f| require f }
Loading