specs: migrate from features to system specs

System specs have been available since Rails 5.1, and are better
integrated with the Rails framework.

- Rename `spec/features` to `spec/system`
- Rename `feature do` to `describe do`
- Configure Capybara for system specs

Steps mostly taken from https://medium.com/table-xi/a-quick-guide-to-rails-system-tests-in-rspec-b6e9e8a8b5f6
This commit is contained in:
Pierre de La Morinerie 2021-10-26 09:44:53 +00:00
parent df9fa258ae
commit 9fd38cae5e
42 changed files with 58 additions and 53 deletions

View file

@ -59,7 +59,7 @@ jobs:
- bin/rspec spec/controllers/*_spec.rb - bin/rspec spec/controllers/*_spec.rb
- bin/rspec spec/controllers/[a-l]**/*_spec.rb - bin/rspec spec/controllers/[a-l]**/*_spec.rb
- bin/rspec spec/controllers/[m-z]**/*_spec.rb - bin/rspec spec/controllers/[m-z]**/*_spec.rb
- bin/rspec spec/features - bin/rspec spec/system
- bin/rspec spec/helpers spec/lib spec/middlewares - bin/rspec spec/helpers spec/lib spec/middlewares
- bin/rspec spec/mailers spec/jobs spec/policies - bin/rspec spec/mailers spec/jobs spec/policies
- bin/rspec spec/models - bin/rspec spec/models

View file

@ -722,7 +722,7 @@ Rails/DelegateAllowBlank:
Rails/DynamicFindBy: Rails/DynamicFindBy:
Enabled: true Enabled: true
Exclude: Exclude:
- "spec/features/**/*.rb" - "spec/system/**/*.rb"
Rails/EnumUniqueness: Rails/EnumUniqueness:
Enabled: true Enabled: true

View file

@ -89,10 +89,10 @@ group :test do
gem 'capybara' # Integration testing gem 'capybara' # Integration testing
gem 'capybara-email' # Access emails during integration tests gem 'capybara-email' # Access emails during integration tests
gem 'capybara-screenshot' # Save a dump of the page when an integration test fails gem 'capybara-screenshot' # Save a dump of the page when an integration test fails
gem 'capybara-selenium'
gem 'factory_bot' gem 'factory_bot'
gem 'launchy' gem 'launchy'
gem 'rails-controller-testing' gem 'rails-controller-testing'
gem 'selenium-webdriver'
gem 'shoulda-matchers', require: false gem 'shoulda-matchers', require: false
gem 'timecop' gem 'timecop'
gem 'vcr' gem 'vcr'

View file

@ -162,9 +162,6 @@ GEM
capybara-screenshot (1.0.25) capybara-screenshot (1.0.25)
capybara (>= 1.0, < 4) capybara (>= 1.0, < 4)
launchy launchy
capybara-selenium (0.0.6)
capybara
selenium-webdriver
case_transform (0.2) case_transform (0.2)
activesupport activesupport
caxlsx (3.1.0) caxlsx (3.1.0)
@ -792,7 +789,6 @@ DEPENDENCIES
capybara capybara
capybara-email capybara-email
capybara-screenshot capybara-screenshot
capybara-selenium
charlock_holmes charlock_holmes
chartkick chartkick
chunky_png chunky_png
@ -867,6 +863,7 @@ DEPENDENCIES
sanitize-url sanitize-url
sassc-rails sassc-rails
scss_lint scss_lint
selenium-webdriver
sentry-delayed_job sentry-delayed_job
sentry-rails sentry-rails
sentry-ruby sentry-ruby

View file

@ -2,7 +2,7 @@
# More info at https://github.com/guard/guard#readme # More info at https://github.com/guard/guard#readme
## Uncomment and set this to only include directories you want to watch ## Uncomment and set this to only include directories you want to watch
# directories %w(app lib config test spec features) \ # directories %w(app lib config test spec system) \
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
## Note: if you are using the `directories` clause above and you are not ## Note: if you are using the `directories` clause above and you are not
@ -77,8 +77,8 @@ guard :rspec, cmd: 'spring rspec' do
watch('app/controllers/application_controller.rb') { "spec/controllers" } watch('app/controllers/application_controller.rb') { "spec/controllers" }
watch('spec/rails_helper.rb') { "spec" } watch('spec/rails_helper.rb') { "spec" }
# Capybara features specs # Capybara system specs
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/system/#{m[1]}_spec.rb" }
# Turnip features and steps # Turnip features and steps
watch(%r{^spec/acceptance/(.+)\.feature$}) watch(%r{^spec/acceptance/(.+)\.feature$})

View file

@ -127,4 +127,5 @@ RSpec.configure do |config|
config.include Shoulda::Matchers::ActiveModel, type: :model config.include Shoulda::Matchers::ActiveModel, type: :model
config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :system
end end

View file

@ -3,9 +3,6 @@ require 'capybara-screenshot/rspec'
require 'capybara/email/rspec' require 'capybara/email/rspec'
require 'selenium/webdriver' require 'selenium/webdriver'
Capybara.javascript_driver = :headless_chrome
Capybara.ignore_hidden_elements = false
Capybara.register_driver :chrome do |app| Capybara.register_driver :chrome do |app|
Capybara::Selenium::Driver.new(app, browser: :chrome) Capybara::Selenium::Driver.new(app, browser: :chrome)
end end
@ -36,6 +33,8 @@ end
Capybara.default_max_wait_time = 2 Capybara.default_max_wait_time = 2
Capybara.ignore_hidden_elements = false
# Save a snapshot of the HTML page when an integration test fails # Save a snapshot of the HTML page when an integration test fails
Capybara::Screenshot.autosave_on_failure = true Capybara::Screenshot.autosave_on_failure = true
# Keep only the screenshots generated from the last failing test suite # Keep only the screenshots generated from the last failing test suite
@ -46,13 +45,21 @@ Capybara::Screenshot.register_driver :headless_chrome do |driver, path|
end end
RSpec.configure do |config| RSpec.configure do |config|
# Set the user preferred language before Javascript feature specs. config.before(:each, type: :system) do
driven_by :rack_test
end
config.before(:each, type: :system, js: true) do
driven_by :headless_chrome
end
# Set the user preferred language before Javascript system specs.
# #
# Features specs without Javascript run in a Rack stack, and respect the Accept-Language value. # System specs without Javascript run in a Rack stack, and respect the Accept-Language value.
# However specs using Javascript are run into a Headless Chrome, which doesn't support setting # However specs using Javascript are run into a Headless Chrome, which doesn't support setting
# the default Accept-Language value reliably. # the default Accept-Language value reliably.
# So instead we set the locale cookie explicitly before each Javascript test. # So instead we set the locale cookie explicitly before each Javascript test.
config.before(:each, js: true) do config.before(:each, type: :system, js: true) do
visit '/' # Webdriver needs visiting a page before setting the cookie visit '/' # Webdriver needs visiting a page before setting the cookie
Capybara.current_session.driver.browser.manage.add_cookie( Capybara.current_session.driver.browser.manage.add_cookie(
name: :locale, name: :locale,

View file

@ -1,4 +1,4 @@
module FeatureHelpers module SystemHelpers
include ActiveJob::TestHelper include ActiveJob::TestHelper
def login_admin def login_admin
@ -177,5 +177,5 @@ module FeatureHelpers
end end
RSpec.configure do |config| RSpec.configure do |config|
config.include FeatureHelpers, type: :feature config.include SystemHelpers, type: :system
end end

View file

@ -1,4 +1,4 @@
feature 'wcag rules for usager', js: true do describe 'wcag rules for usager', js: true do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_all_champs, :with_service, :for_individual, :published) } let(:procedure) { create(:procedure, :with_type_de_champ, :with_all_champs, :with_service, :for_individual, :published) }
let(:password) { 'a very complicated password' } let(:password) { 'a very complicated password' }
let(:litteraire_user) { create(:user, password: password) } let(:litteraire_user) { create(:user, password: password) }

View file

@ -1,4 +1,4 @@
feature 'As an administrateur', js: true do describe 'As an administrateur', js: true do
let(:super_admin) { create(:super_admin) } let(:super_admin) { create(:super_admin) }
let(:admin_email) { 'new_admin@gouv.fr' } let(:admin_email) { 'new_admin@gouv.fr' }
let(:new_admin) { Administrateur.by_email(admin_email) } let(:new_admin) { Administrateur.by_email(admin_email) }

View file

@ -1,6 +1,6 @@
require 'features/admin/procedure_spec_helper' require 'system/admin/procedure_spec_helper'
feature 'As an administrateur I wanna clone a procedure', js: true do describe 'As an administrateur I wanna clone a procedure', js: true do
include ProcedureSpecHelper include ProcedureSpecHelper
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }

View file

@ -1,6 +1,6 @@
require 'features/admin/procedure_spec_helper' require 'system/admin/procedure_spec_helper'
feature 'As an administrateur I wanna create a new procedure', js: true do describe 'As an administrateur I wanna create a new procedure', js: true do
include ProcedureSpecHelper include ProcedureSpecHelper
let(:administrateur) { create(:administrateur, :with_procedure) } let(:administrateur) { create(:administrateur, :with_procedure) }

View file

@ -1,4 +1,4 @@
feature 'procedure locked' do describe 'procedure locked' do
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }
before do before do

View file

@ -1,6 +1,6 @@
require 'features/admin/procedure_spec_helper' require 'system/admin/procedure_spec_helper'
feature 'Publication de démarches', js: true do describe 'Publication de démarches', js: true do
include ProcedureSpecHelper include ProcedureSpecHelper
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }

View file

@ -1,6 +1,6 @@
require 'features/admin/procedure_spec_helper' require 'system/admin/procedure_spec_helper'
feature 'Administrateurs can edit procedures', js: true do describe 'Administrateurs can edit procedures', js: true do
include ProcedureSpecHelper include ProcedureSpecHelper
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }

View file

@ -1,4 +1,4 @@
feature 'fetch API Particulier Data', js: true do describe 'fetch API Particulier Data', js: true do
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }
let(:expected_token) { 'd7e9c9f4c3ca00caadde31f50fd4521a' } let(:expected_token) { 'd7e9c9f4c3ca00caadde31f50fd4521a' }

View file

@ -1,4 +1,4 @@
feature 'Inviting an expert:' do describe 'Inviting an expert:' do
include ActiveJob::TestHelper include ActiveJob::TestHelper
include ActionView::Helpers include ActionView::Helpers

View file

@ -1,4 +1,4 @@
feature 'Protecting against request forgeries:', :allow_forgery_protection, :show_exception_pages do describe 'Protecting against request forgeries:', :allow_forgery_protection, :show_exception_pages do
let(:user) { create(:user, password: password) } let(:user) { create(:user, password: password) }
let(:password) { 'ThisIsTheUserPassword' } let(:password) { 'ThisIsTheUserPassword' }

View file

@ -1,4 +1,4 @@
feature 'France Connect Particulier Connexion' do describe 'France Connect Particulier Connexion' do
let(:code) { 'plop' } let(:code) { 'plop' }
let(:given_name) { 'titi' } let(:given_name) { 'titi' }
let(:family_name) { 'toto' } let(:family_name) { 'toto' }

View file

@ -1,4 +1,4 @@
feature 'Getting help:' do describe 'Getting help:' do
scenario 'a Help button is visible on public pages' do scenario 'a Help button is visible on public pages' do
visit '/' visit '/'
within('.new-header') do within('.new-header') do

View file

@ -1,4 +1,4 @@
feature 'Accessing the website in different languages:' do describe 'Accessing the website in different languages:' do
context 'when the i18n feature-flag is enabled' do context 'when the i18n feature-flag is enabled' do
before { ENV['LOCALIZATION_ENABLED'] = 'true' } before { ENV['LOCALIZATION_ENABLED'] = 'true' }
after { ENV['LOCALIZATION_ENABLED'] = 'false' } after { ENV['LOCALIZATION_ENABLED'] = 'false' }

View file

@ -1,4 +1,4 @@
feature 'Inviting an expert:', js: true do describe 'Inviting an expert:', js: true do
include ActiveJob::TestHelper include ActiveJob::TestHelper
include ActionView::Helpers include ActionView::Helpers

View file

@ -1,4 +1,4 @@
feature 'As an instructeur', js: true do describe 'As an instructeur', js: true do
let(:administrateur) { create(:administrateur, :with_procedure) } let(:administrateur) { create(:administrateur, :with_procedure) }
let(:procedure) { administrateur.procedures.first } let(:procedure) { administrateur.procedures.first }
let(:instructeur_email) { 'new_instructeur@gouv.fr' } let(:instructeur_email) { 'new_instructeur@gouv.fr' }

View file

@ -1,4 +1,4 @@
feature 'Instructing a dossier:', js: true do describe 'Instructing a dossier:', js: true do
include ActiveJob::TestHelper include ActiveJob::TestHelper
let(:password) { 'my-s3cure-p4ssword' } let(:password) { 'my-s3cure-p4ssword' }

View file

@ -1,4 +1,4 @@
feature "procedure filters" do describe "procedure filters" do
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published, :with_type_de_champ, instructeurs: [instructeur]) } let(:procedure) { create(:procedure, :published, :with_type_de_champ, instructeurs: [instructeur]) }
let!(:type_de_champ) { procedure.types_de_champ.first } let!(:type_de_champ) { procedure.types_de_champ.first }

View file

@ -1,4 +1,4 @@
feature 'As an administrateur I can edit types de champ', js: true do describe 'As an administrateur I can edit types de champ', js: true do
let(:administrateur) { procedure.administrateurs.first } let(:administrateur) { procedure.administrateurs.first }
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }

View file

@ -1,4 +1,4 @@
feature 'Outdated browsers support:' do describe 'Outdated browsers support:' do
context 'when the user browser is outdated' do context 'when the user browser is outdated' do
before(:each) do before(:each) do
ie_10_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; InfoPath.3)' ie_10_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; InfoPath.3)'

View file

@ -1,4 +1,4 @@
feature 'The routing', js: true do describe 'The routing', js: true do
let(:password) { 'a very complicated password' } let(:password) { 'a very complicated password' }
let(:procedure) { create(:procedure, :with_type_de_champ, :with_service, :for_individual) } let(:procedure) { create(:procedure, :with_type_de_champ, :with_service, :for_individual) }
let(:administrateur) { create(:administrateur, procedures: [procedure]) } let(:administrateur) { create(:administrateur, procedures: [procedure]) }

View file

@ -1,4 +1,4 @@
feature 'Signin in:' do describe 'Signin in:' do
let!(:user) { create(:user, password: password) } let!(:user) { create(:user, password: password) }
let(:password) { 'my-s3cure-p4ssword' } let(:password) { 'my-s3cure-p4ssword' }

View file

@ -1,4 +1,4 @@
feature 'The user' do describe 'The user' do
let(:password) { 'my-s3cure-p4ssword' } let(:password) { 'my-s3cure-p4ssword' }
let!(:user) { create(:user, password: password) } let!(:user) { create(:user, password: password) }

View file

@ -1,4 +1,4 @@
feature 'Changing an email' do describe 'Changing an email' do
let(:old_email) { 'old@email.com' } let(:old_email) { 'old@email.com' }
let(:user) { create(:user, email: old_email) } let(:user) { create(:user, email: old_email) }

View file

@ -1,4 +1,4 @@
feature 'Creating a new dossier:' do describe 'Creating a new dossier:' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:siret) { '41816609600051' } let(:siret) { '41816609600051' }
let(:siren) { siret[0...9] } let(:siren) { siret[0...9] }

View file

@ -1,4 +1,4 @@
require 'features/users/dossier_shared_examples.rb' require 'system/users/dossier_shared_examples.rb'
describe 'Dossier details:' do describe 'Dossier details:' do
let(:user) { create(:user) } let(:user) { create(:user) }

View file

@ -1,6 +1,6 @@
require 'features/users/dossier_shared_examples.rb' require 'system/users/dossier_shared_examples.rb'
feature 'Invitations' do describe 'Invitations' do
let(:owner) { create(:user) } let(:owner) { create(:user) }
let(:invited_user) { create(:user, email: 'user_invite@exemple.fr') } let(:invited_user) { create(:user, email: 'user_invite@exemple.fr') }
let(:procedure) { create(:simple_procedure) } let(:procedure) { create(:simple_procedure) }

View file

@ -1,4 +1,4 @@
feature 'linked dropdown lists' do describe 'linked dropdown lists' do
let(:password) { 'my-s3cure-p4ssword' } let(:password) { 'my-s3cure-p4ssword' }
let!(:user) { create(:user, password: password) } let!(:user) { create(:user, password: password) }

View file

@ -1,4 +1,4 @@
feature 'Managing password:' do describe 'Managing password:' do
context 'for simple users' do context 'for simple users' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:new_password) { 'a simple password' } let(:new_password) { 'a simple password' }

View file

@ -1,4 +1,4 @@
feature 'Sign out' do describe 'Sign out' do
context 'when a user is logged in' do context 'when a user is logged in' do
let(:user) { create(:administrateur).user } let(:user) { create(:administrateur).user }

View file

@ -1,4 +1,4 @@
feature 'Signing up:' do describe 'Signing up:' do
let(:user_email) { generate :user_email } let(:user_email) { generate :user_email }
let(:user_password) { 'my-s3cure-p4ssword' } let(:user_password) { 'my-s3cure-p4ssword' }
let(:procedure) { create :simple_procedure, :with_service } let(:procedure) { create :simple_procedure, :with_service }