From a6cdf714a689f6741fd8e724b2775dee12be6f87 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 4 Jan 2018 10:46:33 +0100 Subject: [PATCH 1/3] Use capybara-selenium with headless chrome --- .circleci/config.yml | 57 +++++-------------- .ruby-version | 2 +- Gemfile | 2 +- Gemfile.lock | 17 +++--- README.md | 2 +- .../features/admin/procedure_creation_spec.rb | 20 +++---- spec/features/users/complete_demande_spec.rb | 4 +- spec/features/users/dossier_creation_spec.rb | 8 +-- spec/features/users/dossier_edition_spec.rb | 2 +- spec/features/users/dossier_index_spec.rb | 12 ++-- spec/lib/carto/geocodeur_spec.rb | 6 +- spec/spec_helper.rb | 19 +++++-- 12 files changed, 68 insertions(+), 83 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 10807e7dd..f7afa729a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,68 +1,43 @@ version: 2 defaults: &defaults - working_directory: /tps + working_directory: ~/tps docker: - - image: ruby:2.3.5 - - image: postgres:9.4.1 + - image: circleci/ruby:2.3.6-node-browsers + - image: circleci/postgres:9.5 environment: POSTGRES_USER: tps_test POSTGRES_PASSWORD: tps_test POSTGRES_DB: tps_test -install_system_deps: &install_system_deps - run: - name: Install System Dependencies - command: apt-get update -qq && apt-get install -y build-essential nodejs - -restore_phantomjs_cache: &restore_phantomjs_cache - restore_cache: - key: phantomjs-2-1-1 - bundle_restore_cache: &bundle_restore_cache restore_cache: - key: bundle-install-v4-{{ arch }}-{{ checksum "Gemfile.lock" }} + key: bundle-install-v7-{{ arch }}-{{ checksum "Gemfile.lock" }} + +bundle_save_cache: &bundle_save_cache + save_cache: + key: bundle-install-v7-{{ arch }}-{{ checksum "Gemfile.lock" }} + paths: + - ~/vendor/bundle bundle_install: &bundle_install run: name: Install Ruby Dependencies - command: bundle install + command: bundle install --path ~/vendor/bundle jobs: build: <<: *defaults steps: - checkout - - *install_system_deps - *bundle_restore_cache - *bundle_install - - save_cache: - key: bundle-install-v4-{{ arch }}-{{ checksum "Gemfile.lock" }} - paths: - - /usr/local/bundle - - *restore_phantomjs_cache - - run: - name: Install PhantomJS Dependencies - command: | - [ -f /usr/local/bin/phantomjs ] || apt-get update - [ -f /usr/local/bin/phantomjs ] || apt-get install -y fontconfig wget - - run: - name: Install PhantomJS - command: | - [ -f /usr/local/bin/phantomjs ] || wget -O /tmp/phantomjs.tar.bz2 https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 - [ -f /usr/local/bin/phantomjs ] || tar -xjf /tmp/phantomjs.tar.bz2 -C /tmp - [ -f /usr/local/bin/phantomjs ] || mv /tmp/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs - - save_cache: - key: phantomjs-2-1-1 - paths: - - /usr/local/bin/phantomjs + - *bundle_save_cache test: <<: *defaults parallelism: 4 steps: - checkout - - *install_system_deps - - *restore_phantomjs_cache - *bundle_restore_cache - *bundle_install - run: @@ -77,16 +52,15 @@ jobs: command: | bundle exec rspec --profile 10 \ --format RspecJunitFormatter \ - --out $CIRCLE_TEST_REPORTS/rspec.xml \ + --out ~/test_results/rspec.xml \ --format progress \ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) - store_test_results: - path: $CIRCLE_TEST_REPORTS/rspec.xml + path: ~/test_results/rspec.xml lint: <<: *defaults steps: - checkout - - *install_system_deps - *bundle_restore_cache - *bundle_install - run: @@ -94,7 +68,7 @@ jobs: command: bundle exec rubocop -R - run: name: Run brakeman - command: bundle exec brakeman -z + command: bundle exec brakeman --no-exit-on-warn - run: name: Run haml-lint command: bundle exec haml-lint app/views/ @@ -105,7 +79,6 @@ jobs: <<: *defaults steps: - checkout - - *install_system_deps - *bundle_restore_cache - *bundle_install - add_ssh_keys: diff --git a/.ruby-version b/.ruby-version index cc6c9a491..e75da3e63 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.5 +2.3.6 diff --git a/Gemfile b/Gemfile index ba37b0091..331caa640 100644 --- a/Gemfile +++ b/Gemfile @@ -120,7 +120,7 @@ group :test do gem 'database_cleaner' gem 'webmock' gem 'shoulda-matchers', require: false - gem 'poltergeist' + gem 'capybara-selenium' gem 'timecop' gem 'guard' gem 'guard-rspec', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 83e48ec2b..d61476374 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -105,6 +105,9 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + capybara-selenium (0.0.6) + capybara + selenium-webdriver carrierwave (0.11.2) activemodel (>= 3.2.0) activesupport (>= 3.2.0) @@ -113,9 +116,10 @@ GEM mimemagic (>= 0.3.0) carrierwave-i18n (0.2.0) chartkick (2.2.1) + childprocess (0.8.0) + ffi (~> 1.0, >= 1.0.11) chunky_png (1.3.8) clamav-client (3.1.0) - cliver (0.3.2) coderay (1.1.1) coffee-rails (4.2.1) coffee-script (>= 2.2.0) @@ -466,10 +470,6 @@ GEM ast (~> 2.2) pdf-core (0.6.1) pg (0.19.0) - poltergeist (1.14.0) - capybara (~> 2.1) - cliver (~> 0.3.1) - websocket-driver (>= 0.2.0) powerpack (0.1.1) prawn (2.0.2) pdf-core (~> 0.6.0) @@ -613,6 +613,9 @@ GEM select2-rails (4.0.3) thor (~> 0.14) selectize-rails (0.12.4.1) + selenium-webdriver (3.8.0) + childprocess (~> 0.5) + rubyzip (~> 1.0) sentry-raven (2.2.0) faraday (>= 0.7.6, < 1.0) sexp_processor (4.10.0) @@ -726,6 +729,7 @@ DEPENDENCIES browser byebug capybara + capybara-selenium carrierwave carrierwave-i18n chartkick @@ -765,7 +769,6 @@ DEPENDENCIES openid_connect openstack pg - poltergeist prawn (~> 2.0.1) prawn_rails (~> 0.0.11) pry-byebug @@ -804,4 +807,4 @@ DEPENDENCIES xray-rails BUNDLED WITH - 1.16.0 + 1.16.1 diff --git a/README.md b/README.md index 818096ce8..e22c836c8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Téléprocédures Simplifiées, ou TPS pour les intimes, est une plateforme 100 ### Tests -- PhantomJS +- Chrome ## Initialisation de l'environnement de développement diff --git a/spec/features/admin/procedure_creation_spec.rb b/spec/features/admin/procedure_creation_spec.rb index c5edfce80..6b3111560 100644 --- a/spec/features/admin/procedure_creation_spec.rb +++ b/spec/features/admin/procedure_creation_spec.rb @@ -10,17 +10,17 @@ feature 'As an administrateur I wanna create a new procedure', js: true do context 'Right after sign_in I shall see all procedure states links' do scenario 'Finding draft procedures' do - page.find_by_id('draft-procedures').trigger('click') + page.find_by_id('draft-procedures').click expect(page).to have_current_path(admin_procedures_draft_path, only_path: true) end scenario 'Finding active procedures' do - page.find_by_id('active-procedures').trigger('click') + page.find_by_id('active-procedures').click expect(page).to have_current_path(admin_procedures_path, only_path: true) end scenario 'Finding archived procedures' do - page.find_by_id('archived-procedures').trigger('click') + page.find_by_id('archived-procedures').click expect(page).to have_current_path(admin_procedures_archived_path, only_path: true) end end @@ -58,13 +58,13 @@ feature 'As an administrateur I wanna create a new procedure', js: true do page.find_by_id('procedure_types_de_champ_attributes_1_libelle') expect(Procedure.first.types_de_champ.first.libelle).to eq('libelle de champ') - page.find_by_id('onglet-pieces').trigger('click') + page.find_by_id('onglet-pieces').click expect(page).to have_current_path(admin_procedure_pieces_justificatives_path(Procedure.first.id.to_s)) page.find_by_id('procedure_types_de_piece_justificative_attributes_0_libelle').set 'libelle de piece' page.find_by_id('add_piece_justificative').click page.find_by_id('procedure_types_de_piece_justificative_attributes_1_libelle') - page.find_by_id('onglet-preview').trigger('click') + page.find_by_id('onglet-preview').click expect(page).to have_current_path(admin_procedure_previsualisation_path(Procedure.first.id.to_s)) expect(page.find("input[type='text']")['placeholder']).to eq('libelle de champ') expect(page.first('.piece-libelle').text).to eq('libelle de piece') @@ -73,21 +73,21 @@ feature 'As an administrateur I wanna create a new procedure', js: true do scenario 'After adding champ and file, check impossibility to publish procedure, add accompagnateur and make publication' do page.find_by_id('procedure_types_de_champ_attributes_0_libelle').set 'libelle de champ' page.find_by_id('add_type_de_champ').click - page.find_by_id('onglet-pieces').trigger('click') + page.find_by_id('onglet-pieces').click page.find_by_id('procedure_types_de_piece_justificative_attributes_0_libelle').set 'libelle de piece' page.find_by_id('add_piece_justificative').click - page.find_by_id('onglet-infos').trigger('click') + page.find_by_id('onglet-infos').click expect(page).to have_current_path(admin_procedure_path(Procedure.first.id.to_s)) - expect(page.find_by_id('publish-procedure')['disabled']).to eq('disabled') + expect(page.find_by_id('publish-procedure')['disabled']).to eq('true') - page.find_by_id('onglet-accompagnateurs').trigger('click') + page.find_by_id('onglet-accompagnateurs').click expect(page).to have_current_path(admin_procedure_accompagnateurs_path(Procedure.first.id.to_s)) page.find_by_id('gestionnaire_email').set 'gestionnaire@apientreprise.fr' page.find_by_id('add-gestionnaire-email').click page.first('.gestionnaire-affectation').click - page.find_by_id('onglet-infos').trigger('click') + page.find_by_id('onglet-infos').click expect(page).to have_selector('#publish-procedure', visible: true) page.find_by_id('publish-procedure').click diff --git a/spec/features/users/complete_demande_spec.rb b/spec/features/users/complete_demande_spec.rb index c2f455100..d763ec450 100644 --- a/spec/features/users/complete_demande_spec.rb +++ b/spec/features/users/complete_demande_spec.rb @@ -61,7 +61,7 @@ feature 'user path for dossier creation' do context 'when validating info entreprise recap page' do before do page.check('dossier_autorisation_donnees') - page.find_by_id('etape_suivante').trigger('click') + page.find_by_id('etape_suivante').click end scenario 'user is on description page' do expect(page).to have_css('#description-page') @@ -69,7 +69,7 @@ feature 'user path for dossier creation' do context 'user fill and validate description page' do before do page.find_by_id("champs_#{Dossier.last.champs.first.id}").set 'Mon super projet' - page.find_by_id('suivant').trigger('click') + page.find_by_id('suivant').click end scenario 'user is on recap page' do expect(page).to have_css('#users-recapitulatif-dossier-show') diff --git a/spec/features/users/dossier_creation_spec.rb b/spec/features/users/dossier_creation_spec.rb index 4c10c007e..bba22f26c 100644 --- a/spec/features/users/dossier_creation_spec.rb +++ b/spec/features/users/dossier_creation_spec.rb @@ -55,14 +55,14 @@ feature 'As a User I wanna create a dossier' do stub_request(:get, "https://staging.entreprise.api.gouv.fr/v2/associations/#{siret}?token=#{SIADETOKEN}") .to_return(status: 404, body: '') page.find_by_id('dossier-siret').set siret - page.find_by_id('submit-siret').trigger('click') + page.find_by_id('submit-siret').click expect(page).to have_css('#recap-info-entreprise') find(:css, "#dossier_autorisation_donnees[value='1']").set(true) - page.find_by_id('etape_suivante').trigger('click') + page.find_by_id('etape_suivante').click expect(page).to have_current_path(users_dossier_carte_path(procedure_with_siret.dossiers.last.id.to_s), only_path: true) - page.find_by_id('etape_suivante').trigger('click') + page.find_by_id('etape_suivante').click fill_in "champs_#{procedure_with_siret.dossiers.last.champs.first.id}", with: 'contenu du champ 1' - page.find_by_id('suivant').trigger('click') + page.find_by_id('suivant').click expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_with_siret.dossiers.last.id.to_s), only_path: true) end end diff --git a/spec/features/users/dossier_edition_spec.rb b/spec/features/users/dossier_edition_spec.rb index dd9f47aad..32c972110 100644 --- a/spec/features/users/dossier_edition_spec.rb +++ b/spec/features/users/dossier_edition_spec.rb @@ -37,7 +37,7 @@ feature 'As a User I want to edit a dossier I own' do linked_dossier_id = dossier.champs.find { |c| c.type_de_champ.type_champ == 'dossier_link' }.value expect(page).to have_link("Dossier #{linked_dossier_id}") - page.find_by_id('maj_infos').trigger('click') + page.find_by_id('edit-dossier').click expect(page).to have_current_path(users_dossier_description_path(dossier.id.to_s), only_path: true) champ_id = dossier.champs.find { |t| t.type_champ == "text" }.id fill_in "champs_#{champ_id.to_s}", with: 'Contenu du champ 1' diff --git a/spec/features/users/dossier_index_spec.rb b/spec/features/users/dossier_index_spec.rb index 7365366af..e508add36 100644 --- a/spec/features/users/dossier_index_spec.rb +++ b/spec/features/users/dossier_index_spec.rb @@ -11,8 +11,8 @@ feature 'As a User I want to sort and paginate dossiers', js: true do fill_in 'dossier_individual_attributes_prenom', with: 'Prenom' fill_in 'dossier_individual_attributes_birthdate', with: '14/10/1987' find(:css, "#dossier_autorisation_donnees[value='1']").set(true) - page.find_by_id('etape_suivante').trigger('click') - page.find_by_id('suivant').trigger('click') + page.find_by_id('etape_suivante').click + page.find_by_id('suivant').click 50.times do Dossier.create(procedure_id: procedure_for_individual.id, user_id: user.id, state: "en_construction") end @@ -35,15 +35,15 @@ feature 'As a User I want to sort and paginate dossiers', js: true do scenario 'Using pagination' do visit "/users/dossiers?dossiers_smart_listing[sort][id]=asc" expect(page.all(:css, '#dossiers-list tr')[1].text.split(" ").first).to eq(user.dossiers.first.id.to_s) - page.find('.next_page a').trigger('click') + page.find('.next_page a').click wait_for_ajax expect(page.all(:css, '#dossiers-list tr')[1].text.split(" ").first).to eq((user.dossiers.first.id + 10).to_s) - page.find('.next_page a').trigger('click') + page.find('.next_page a').click wait_for_ajax expect(page.all(:css, '#dossiers-list tr')[1].text.split(" ").first).to eq((user.dossiers.first.id + 20).to_s) - page.find('.prev a').trigger('click') + page.find('.prev a').click wait_for_ajax - page.find('.prev a').trigger('click') + page.find('.prev a').click wait_for_ajax expect(page.all(:css, '#dossiers-list tr')[1].text.split(" ").first).to eq((user.dossiers.first.id).to_s) end diff --git a/spec/lib/carto/geocodeur_spec.rb b/spec/lib/carto/geocodeur_spec.rb index 3a2018188..a9a071010 100644 --- a/spec/lib/carto/geocodeur_spec.rb +++ b/spec/lib/carto/geocodeur_spec.rb @@ -3,10 +3,8 @@ require 'spec_helper' describe Carto::Geocodeur do let(:address) { '50 av des champs elysees' } describe '.convert_adresse_to_point', vcr: { cassette_name: 'bano_octo' } do - if ENV['CIRCLECI'].nil? - it 'return a point' do - expect(described_class.convert_adresse_to_point(address).class).to eq(RGeo::Cartesian::PointImpl) - end + it 'return a point' do + expect(described_class.convert_adresse_to_point(address).class).to eq(RGeo::Cartesian::PointImpl) end context 'when RestClient::Exception' do before do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8d23bd1a1..96d04f492 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -28,11 +28,22 @@ require 'shoulda-matchers' require 'devise' require 'factory_girl' -require 'capybara/poltergeist' -Capybara.javascript_driver = :poltergeist +require 'selenium/webdriver' +Capybara.javascript_driver = :headless_chrome Capybara.ignore_hidden_elements = false -Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new(app, js_errors: true, port: 44_678, phantomjs_options: ['--proxy-type=none'], timeout: 180) + +Capybara.register_driver :chrome do |app| + Capybara::Selenium::Driver.new(app, browser: :chrome) +end + +Capybara.register_driver :headless_chrome do |app| + capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( + chromeOptions: { args: %w(headless disable-gpu window-size=2560,1600) } + ) + + Capybara::Selenium::Driver.new app, + browser: :chrome, + desired_capabilities: capabilities end ActiveSupport::Deprecation.silenced = true From 6a43248cb37589954f7b0d5b023bf04b68ba4ce2 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 11 Jan 2018 18:09:01 +0100 Subject: [PATCH 2/3] Fix CSS injection --- .circleci/config.yml | 2 +- Gemfile | 2 ++ Gemfile.lock | 2 ++ app/helpers/application_helper.rb | 6 ++++++ app/views/admin/procedures/show.html.haml | 2 +- app/views/dossiers/etapes/_etape1.html.haml | 3 +-- .../users/description/_pieces_justificatives.html.haml | 2 +- 7 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7afa729a..bd4c1e637 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -68,7 +68,7 @@ jobs: command: bundle exec rubocop -R - run: name: Run brakeman - command: bundle exec brakeman --no-exit-on-warn + command: bundle exec brakeman -z - run: name: Run haml-lint command: bundle exec haml-lint app/views/ diff --git a/Gemfile b/Gemfile index 331caa640..c7e41c398 100644 --- a/Gemfile +++ b/Gemfile @@ -94,6 +94,8 @@ gem 'skylight' gem 'scenic' +gem 'sanitize-url' + # Cron jobs gem 'delayed_job_active_record' gem "daemons" diff --git a/Gemfile.lock b/Gemfile.lock index d61476374..f3a9f14d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -593,6 +593,7 @@ GEM sexp_processor (~> 4.9) rubyzip (1.0.0) safe_yaml (1.0.4) + sanitize-url (0.1.4) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -782,6 +783,7 @@ DEPENDENCIES rspec_junit_formatter rubocop rubocop-rspec-focused + sanitize-url sass-rails (~> 5.0) scenic scss_lint diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f5572c1d9..fa1bb5063 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,10 @@ module ApplicationHelper + include SanitizeUrl + + def sanitize_url(url) + super(url, schemes: ['http', 'https'], replace_evil_with: root_url) + end + def flash_class(level) case level when "notice" then "alert-success" diff --git a/app/views/admin/procedures/show.html.haml b/app/views/admin/procedures/show.html.haml index 731002cc3..0056e98af 100644 --- a/app/views/admin/procedures/show.html.haml +++ b/app/views/admin/procedures/show.html.haml @@ -47,7 +47,7 @@ %h3 Lien procédure %div{ style: 'margin-left: 3%;' } - if @facade.procedure.publiee_ou_archivee? - = link_to @facade.procedure.lien, @facade.procedure.lien, target: '_blank' + = link_to @facade.procedure.lien, sanitize_url(@facade.procedure.lien), target: :blank - else %b Cette procédure n'a pas encore été publiée et n'est donc pas accessible par le public. diff --git a/app/views/dossiers/etapes/_etape1.html.haml b/app/views/dossiers/etapes/_etape1.html.haml index 713fde2fb..54cd6c939 100644 --- a/app/views/dossiers/etapes/_etape1.html.haml +++ b/app/views/dossiers/etapes/_etape1.html.haml @@ -20,5 +20,4 @@ - if @facade.procedure.lien_site_web.present? .center - %a{ href: @facade.procedure.lien_site_web, target: '_blank' } - En savoir plus ... + = link_to "En savoir plus ...", sanitize_url(@facade.procedure.lien_site_web), target: '_blank' diff --git a/app/views/users/description/_pieces_justificatives.html.haml b/app/views/users/description/_pieces_justificatives.html.haml index 3d3b90588..fa2713ede 100644 --- a/app/views/users/description/_pieces_justificatives.html.haml +++ b/app/views/users/description/_pieces_justificatives.html.haml @@ -7,7 +7,7 @@ - if dossier.procedure.lien_demarche.present? %em Récupérer le formulaire de demande ou CERFA vierge pour mon dossier : - = link_to "Télécharger", "#{dossier.procedure.lien_demarche}", target: :blank, id: :lien_cerfa + = link_to "Télécharger", sanitize_url(dossier.procedure.lien_demarche), target: :blank, id: :lien_cerfa -# %a#lien_cerfa{ href: "#{dossier.procedure.lien_demarche}", target: '_blank' } Télécharger %td From a6b60faf9b5f85d8c7a4065540c72d499706cb09 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 16 Jan 2018 16:08:20 +0100 Subject: [PATCH 3/3] [Fix #1266] Fix an incorrect legacy route redirection --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index e28361914..8e3069eea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,5 +264,5 @@ Rails.application.routes.draw do # Legacy routes get 'backoffice' => redirect('/procedures') get 'backoffice/sign_in' => redirect('/users/sign_in') - get 'backoffice/dossiers/procedure/:id' => redirect('/procedures/:id') + get 'backoffice/dossiers/procedure/:procedure_id' => redirect('/procedures/%{procedure_id}') end