From 699d7e7c43536e2571547c747778b5046e595ca9 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 15 Nov 2022 11:24:58 +0100 Subject: [PATCH 01/12] Upgrade to Ruby 2.4 --- .ruby-version | 2 +- Dockerfile | 2 +- Dockerfile-dev | 6 +- Gemfile | 30 ++-- Gemfile.lock | 262 ++++++++++++++----------------- app/models/concerns/resource.rb | 2 +- app/models/message_encryption.rb | 9 +- spec/lib/sms_spec.rb | 14 +- spec/models/subscriber_spec.rb | 2 +- spec/support/blueprints.rb | 38 +++++ spec/support/capybara.rb | 4 - 11 files changed, 193 insertions(+), 178 deletions(-) diff --git a/.ruby-version b/.ruby-version index bc4abe86d..b0f6bf0cd 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.8 +2.4.10 diff --git a/Dockerfile b/Dockerfile index 562973cdb..30f9e35de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM instedd/nginx-rails:2.3 +FROM instedd/nginx-rails:2.4 # Cleanup expired Let's Encrypt CA (Sept 30, 2021) RUN sed -i '/^mozilla\/DST_Root_CA_X3/s/^/!/' /etc/ca-certificates.conf && update-ca-certificates -f diff --git a/Dockerfile-dev b/Dockerfile-dev index 5911e0cba..ca7aef1b1 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -1,4 +1,4 @@ -FROM ruby:2.3 +FROM ruby:2.4 # ENV POIROT_STDOUT true # ENV POIROT_SUPPRESS_RAILS_LOG true @@ -32,12 +32,12 @@ RUN \ imagemagick \ ghostscript \ libgs-dev \ - mysql-client \ + default-mysql-client \ && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ARG TARGETARCH # wkhtmltopdf -RUN curl -L https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.stretch_${TARGETARCH}.deb --output wkhtmltopdf.deb && \ +RUN curl -L https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.buster_${TARGETARCH}.deb --output wkhtmltopdf.deb && \ dpkg -i wkhtmltopdf.deb && \ rm -f wkhtmltopdf.deb diff --git a/Gemfile b/Gemfile index d36719329..a90403fae 100644 --- a/Gemfile +++ b/Gemfile @@ -22,10 +22,10 @@ gem 'mysql2', '~> 0.3' gem 'elasticsearch', '~> 1.0' # Models -gem 'encryptor', '~> 1.3' +gem 'encryptor', '~> 2.0' gem 'kaminari', '~> 0.16' gem 'paperclip', git: 'https://github.com/instedd/paperclip', branch: 'fix/v4.3.6-no-mimemagic' -gem 'paranoia', '<= 2.4.2' # last version to support ruby 2.2 +gem 'paranoia', '< 2.5.0' # last version to support ruby 2.4 / rails 5.0 gem 'premailer-rails', '< 1.10' # 1.10 requires Rails.application.assets_manifest # Views @@ -38,7 +38,7 @@ gem 'wicked_pdf', '~> 2.1' # Authentication # gem 'bcrypt-ruby', '~> 3.1.2' gem 'devise', '~> 4.0.0' -gem 'devise-security', '<= 0.12.0' # last version to support ruby 2.2 +gem 'devise-security', '< 0.15.0' # last version to support ruby 2.4 gem 'devise_invitable', '~> 1.5' gem 'doorkeeper', '~> 4.2.0' gem 'omniauth', '~> 1.2' @@ -46,7 +46,7 @@ gem 'omniauth-google-oauth2', '~> 0.2' gem 'recaptcha' # Libraries -gem 'aws-sdk', '~> 1.6' +# gem 'aws-sdk', '~> 1.6' gem 'base58', '~> 0.1' gem 'barby', '~> 0.6' gem 'config', '~> 1.2' @@ -54,12 +54,12 @@ gem 'dotiw', '~> 3.0' gem 'faker', '< 1.9.2' # NOTE: kept until we upgrade to ruby 2.5+ then we can upgrade to ffaker 2.20 to replace Faker::Number gem 'ffaker', '< 2.12.0' gem 'guid', '~> 0.1' -gem 'nokogiri', '~> 1.6', '< 1.10.0' # last version to support ruby 2.2 +gem 'nokogiri', '~> 1.6', '< 1.11.0' # last version to support ruby 2.4 gem 'oj', '~> 2.12', '< 2.17.3' # NOTE: 2.17.3 will stringify Time as a Float then load a BigDecimal... -gem 'poirot_rails', git: 'https://github.com/instedd/poirot_rails.git', branch: 'master' +# gem 'poirot_rails', git: 'https://github.com/instedd/poirot_rails.git', branch: 'master' gem 'rails-i18n', '~> 5.0' gem 'rchardet', '~> 1.6' -gem 'rest-client', '~> 1.8' # NOTE: only used for a single HTTP call +gem 'rest-client', '~> 2.1' # NOTE: only used for a single HTTP call + Nuntium (SMS) + LocationService gem 'rubyzip', '>= 1.0.0' gem 'rqrcode', '~> 0.10' # required by Barby::QRCode @@ -80,7 +80,7 @@ gem 'sidekiq-cron', '~> 0.3' # TODO: not maintained, consider sidekiq-scheduler gem 'whenever', '~> 1.0' # TODO: replace with a sidekiq-cron job # External services -gem 'location_service', git: 'https://github.com/instedd/ruby-location_service.git', branch: 'master' +gem 'location_service', path: "tmp/ruby-location_service" # git: 'https://github.com/instedd/ruby-location_service.git', branch: 'master' gem 'nuntium_api', '~> 0.21' gem 'sentry-raven', '~> 2.13' @@ -111,16 +111,16 @@ group :development do gem 'spring-commands-parallel-tests' gem 'spring-commands-rspec' gem 'spring-watcher-listen', '~> 2.0.0' - gem 'web-console', '< 4.0' # last version to support ruby 2.2 + gem 'web-console', '< 4.0' # last version to support ruby 2.4 / rails 5 end group :development, :test do - gem 'pry-byebug', '< 2.7.0' # last version to support ruby 2.2 + gem 'pry-byebug', '< 3.10.0' # last version to support ruby 2.4 gem 'pry-rescue' gem 'pry-stack_explorer' - gem 'parallel_tests', '~> 2.32.0' # last version to support ruby 2.2 - gem 'parallel', '~> 1.19.2' # TODO: remove after upgrading ruby and parallel_tests + gem 'parallel_tests', '~> 3.5.1' # last version to support ruby 2.4 + gem 'parallel', '~> 1.20.0' # TODO: remove after upgrading ruby and parallel_tests end group :test do @@ -133,12 +133,12 @@ group :test do gem 'rails-controller-testing' gem 'simplecov', require: false gem 'timecop', '~> 0.8' - gem 'webmock', '~> 1.23.0', require: false # a spec fails with 1.24.x + gem 'webmock', '~> 2.3.1', require: false # integration tests - gem 'capybara', '~> 2.4' + gem 'capybara', '~> 3.17.0' gem 'capybara-screenshot', '~> 1.0' gem 'cucumber-rails', '~> 1.5', require: false gem 'selenium-webdriver', '< 4.0' - gem 'site_prism', '~> 2.17' + gem 'site_prism', '~> 3.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 2904a888c..67b903ee1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,25 +24,6 @@ GIT cocaine (~> 0.5.5) mime-types -GIT - remote: https://github.com/instedd/poirot_rails.git - revision: c3e8ceeacd07a226b941a8cabb2dd762fa73846f - branch: master - specs: - poirot_rails (0.0.4) - bertrpc - guid - json - rails (>= 3.2) - -GIT - remote: https://github.com/instedd/ruby-location_service.git - revision: 830800eec4c4c7874dd6eb9ace576460fba7ba1e - branch: master - specs: - location_service (0.1.0) - rest-client (~> 1.6) - GIT remote: https://github.com/manastech/rails-view_components.git revision: 59861a6e716944441b4cd5e584bb3f36d0a7c37e @@ -61,6 +42,12 @@ PATH cdx elasticsearch +PATH + remote: tmp/ruby-location_service + specs: + location_service (0.1.0) + rest-client (~> 2.0) + GEM remote: https://rubygems.org/ remote: https://rails-assets.org/ @@ -102,14 +89,9 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) arel (7.1.4) - aws-sdk (1.67.0) - aws-sdk-v1 (= 1.67.0) - aws-sdk-v1 (1.67.0) - json (~> 1.4) - nokogiri (~> 1) babel-source (5.8.35) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) @@ -117,30 +99,24 @@ GEM backports (3.23.0) barby (0.6.8) base58 (0.2.3) - bcrypt (3.1.16) - bert (1.1.6) - bertrpc (1.3.1) - bert (>= 1.1.0, < 2.0.0) + bcrypt (3.1.18) bindex (0.8.1) binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) builder (3.2.4) - byebug (3.5.1) - columnize (~> 0.8) - debugger-linecache (~> 1.2) - slop (~> 3.6) - capybara (2.18.0) + byebug (11.1.3) + capybara (3.17.0) addressable mini_mime (>= 0.1.3) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (>= 2.0, < 4.0) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (~> 1.2) + xpath (~> 3.2) capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy - childprocess (0.9.0) - ffi (~> 1.0, >= 1.0.11) + childprocess (3.0.0) chronic (0.10.2) chunky_png (1.4.0) climate_control (0.2.0) @@ -154,8 +130,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - columnize (0.9.0) - concurrent-ruby (1.1.9) + concurrent-ruby (1.1.10) config (1.7.2) activesupport (>= 3.0) deep_merge (~> 1.2, >= 1.2.1) @@ -164,7 +139,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - css_parser (1.7.1) + css_parser (1.12.0) addressable csv_builder (2.1.3) actionpack (>= 3.0.0) @@ -182,10 +157,10 @@ GEM cucumber-tag_expressions (~> 1.1.0) gherkin (~> 5.0) cucumber-expressions (6.0.1) - cucumber-rails (1.7.0) - capybara (>= 2.3.0, < 4) + cucumber-rails (1.8.0) + capybara (>= 2.12, < 4) cucumber (>= 3.0.2, < 4) - mime-types (>= 1.17, < 4) + mime-types (>= 2.0, < 4) nokogiri (~> 1.8) railties (>= 4.2, < 7) cucumber-tag_expressions (1.1.1) @@ -194,7 +169,6 @@ GEM railties (>= 3.1.0) database_cleaner (1.99.0) debug_inspector (1.1.0) - debugger-linecache (1.2.0) deep_merge (1.2.2) devise (4.0.3) bcrypt (~> 3.0) @@ -219,33 +193,35 @@ GEM i18n dropzonejs-rails (0.8.5) rails (> 3.1) - dry-configurable (0.7.0) + dry-configurable (0.11.6) concurrent-ruby (~> 1.0) - dry-container (0.6.0) + dry-core (~> 0.4, >= 0.4.7) + dry-equalizer (~> 0.2) + dry-container (0.7.2) concurrent-ruby (~> 1.0) dry-configurable (~> 0.1, >= 0.1.3) dry-core (0.4.9) concurrent-ruby (~> 1.0) - dry-equalizer (0.2.2) - dry-inflector (0.1.2) - dry-logic (0.4.2) - dry-container (~> 0.2, >= 0.2.6) + dry-equalizer (0.3.0) + dry-inflector (0.2.0) + dry-logic (0.6.1) + concurrent-ruby (~> 1.0) dry-core (~> 0.2) dry-equalizer (~> 0.2) - dry-types (0.13.4) + dry-types (0.14.1) concurrent-ruby (~> 1.0) dry-container (~> 0.3) dry-core (~> 0.4, >= 0.4.4) dry-equalizer (~> 0.2) dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 0.4, >= 0.4.2) - dry-validation (0.12.3) + dry-logic (~> 0.5, >= 0.5) + dry-validation (0.13.3) concurrent-ruby (~> 1.0) dry-configurable (~> 0.1, >= 0.1.3) dry-core (~> 0.2, >= 0.2.1) dry-equalizer (~> 0.2) - dry-logic (~> 0.4.2) - dry-types (~> 0.13.1) + dry-logic (~> 0.5, >= 0.5.0) + dry-types (~> 0.14.0) elasticsearch (1.1.3) elasticsearch-api (= 1.1.3) elasticsearch-transport (= 1.1.3) @@ -254,21 +230,21 @@ GEM elasticsearch-transport (1.1.3) faraday multi_json - encryptor (1.3.0) + encryptor (2.0.0) erubis (2.7.0) et-orbi (1.2.7) tzinfo execjs (2.7.0) faker (1.9.1) i18n (>= 0.7) - faraday (0.17.5) + faraday (0.17.6) multipart-post (>= 1.2, < 3) ffaker (2.11.0) - ffi (1.12.2) + ffi (1.15.5) filewatcher (0.3.6) trollop (~> 2.0) - fugit (1.5.2) - et-orbi (~> 1.1, >= 1.1.8) + fugit (1.7.2) + et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) gherkin (5.1.0) globalid (0.4.2) @@ -289,21 +265,22 @@ GEM railties (>= 4.0.1) hashdiff (1.0.1) hashie (5.0.0) - html2haml (2.2.0) + html2haml (2.3.0) erubis (~> 2.7.0) - haml (>= 4.0, < 6) + haml (>= 4.0) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) htmlentities (4.3.4) - http-cookie (1.0.4) + http-accept (1.7.0) + http-cookie (1.0.5) domain_name (~> 0.5) - i18n (1.5.1) + i18n (1.12.0) concurrent-ruby (~> 1.0) interception (0.5) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jquery-rails (4.4.0) + jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) @@ -315,10 +292,10 @@ GEM kaminari (0.17.0) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - launchy (2.4.3) - addressable (~> 2.3) + launchy (2.5.0) + addressable (~> 2.7) leaflet-rails (0.7.7) - letter_opener (1.8.0) + letter_opener (1.8.1) launchy (>= 2.2, < 3) libv8-node (15.14.0.1) listen (3.0.8) @@ -326,39 +303,41 @@ GEM rb-inotify (~> 0.9, >= 0.9.7) lodash-rails (3.10.1) railties (>= 3.1) - loofah (2.15.0) + loofah (2.19.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) machinist (2.0) mail (2.7.1) mini_mime (>= 0.1.1) method_source (1.0.0) - mime-types (2.99.3) + mime-types (3.4.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2022.0105) mini_mime (1.1.2) mini_portile2 (2.4.0) mini_racer (0.4.0) libv8-node (~> 15.14.0.0) minitest (5.15.0) multi_json (1.15.0) - multi_test (0.1.2) + multi_test (1.1.0) multi_xml (0.6.0) - multipart-post (2.1.1) - mysql2 (0.5.3) + multipart-post (2.2.3) + mysql2 (0.5.4) netrc (0.11.0) - nio4r (2.3.1) - nokogiri (1.9.1) + nio4r (2.5.8) + nokogiri (1.10.10) mini_portile2 (~> 2.4.0) nuntium_api (0.21) json rest-client - oauth2 (1.4.9) + oauth2 (1.4.11) faraday (>= 0.17.3, < 3.0) jwt (>= 1.0, < 3.0) multi_json (~> 1.3) multi_xml (~> 0.5) - rack (>= 1.2, < 3) + rack (>= 1.2, < 4) oj (2.17.2) - omniauth (1.9.1) + omniauth (1.9.2) hashie (>= 3.4.6) rack (>= 1.6.2, < 3) omniauth-google-oauth2 (0.8.2) @@ -366,39 +345,39 @@ GEM oauth2 (~> 1.1) omniauth (~> 1.1) omniauth-oauth2 (>= 1.6) - omniauth-oauth2 (1.7.2) - oauth2 (~> 1.4) + omniauth-oauth2 (1.7.3) + oauth2 (>= 1.4, < 3) omniauth (>= 1.9, < 3) orm_adapter (0.5.0) - parallel (1.19.2) - parallel_tests (2.32.0) + parallel (1.20.1) + parallel_tests (3.5.2) parallel - paranoia (2.4.2) - activerecord (>= 4.0, < 6.1) - premailer (1.11.1) + paranoia (2.4.3) + activerecord (>= 4.0, < 6.2) + premailer (1.12.1) addressable css_parser (>= 1.6.0) htmlentities (>= 4.0.0) premailer-rails (1.9.7) actionmailer (>= 3, < 6) premailer (~> 1.7, >= 1.7.9) - pry (0.14.1) + pry (0.13.1) coderay (~> 1.1) method_source (~> 1.0) - pry-byebug (2.0.0) - byebug (~> 3.4) - pry (~> 0.10) + pry-byebug (3.9.0) + byebug (~> 11.0) + pry (~> 0.13.0) pry-rescue (1.5.2) interception (>= 0.5) pry (>= 0.12.0) pry-stack_explorer (0.4.12) binding_of_caller (~> 0.7) pry (~> 0.13) - public_suffix (3.1.1) + public_suffix (4.0.7) puma (3.12.6) raabro (1.4.0) - rack (2.1.4) - rack-protection (2.2.0) + rack (2.1.4.1) + rack-protection (2.2.2) rack rack-test (0.6.3) rack (>= 1.0) @@ -422,7 +401,7 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.2) + rails-html-sanitizer (1.4.3) loofah (~> 2.3) rails-i18n (5.1.3) i18n (>= 0.7, < 2) @@ -434,7 +413,7 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (10.5.0) - rb-fsevent (0.11.1) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rchardet (1.8.0) @@ -448,15 +427,17 @@ GEM recaptcha (4.9.0) json redis (3.3.5) + regexp_parser (1.8.2) request_store (1.5.1) rack (>= 1.4) - responders (2.4.1) - actionpack (>= 4.2.0, < 6.0) - railties (>= 4.2.0, < 6.0) - rest-client (1.8.0) + responders (3.0.1) + actionpack (>= 5.0) + railties (>= 5.0) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 3.0) - netrc (~> 0.7) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) rexml (3.2.5) rqrcode (0.10.1) chunky_png (~> 1.0) @@ -483,10 +464,10 @@ GEM rspec-mocks (~> 3.9.0) rspec-support (~> 3.9.0) rspec-support (3.9.4) - ruby_parser (3.18.1) + ruby_parser (3.19.1) sexp_processor (~> 4.16) - rubyzip (1.3.0) - rufus-scheduler (3.8.1) + rubyzip (2.3.2) + rufus-scheduler (3.8.2) fugit (~> 1.1, >= 1.1.6) sass (3.7.4) sass-listen (~> 4.0.0) @@ -499,12 +480,12 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - selenium-webdriver (3.141.0) - childprocess (~> 0.5) - rubyzip (~> 1.2, >= 1.2.2) + selenium-webdriver (3.142.7) + childprocess (>= 0.5, < 4.0) + rubyzip (>= 1.2.2) sentry-raven (2.13.0) faraday (>= 0.7.6, < 1.0) - sexp_processor (4.16.0) + sexp_processor (4.16.1) sidekiq (4.2.10) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) @@ -513,17 +494,16 @@ GEM sidekiq-cron (0.6.3) rufus-scheduler (>= 3.3.0) sidekiq (>= 4.2.1) - simplecov (0.17.1) + simplecov (0.18.5) docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) - site_prism (2.17.1) - addressable (~> 2.4) - capybara (>= 2.15, < 3.6) - slop (3.6.0) - spring (2.0.2) - activesupport (>= 4.2) + simplecov-html (~> 0.11) + simplecov-html (0.12.3) + site_prism (3.5) + addressable (~> 2.5) + capybara (<= 3.29) + site_prism-all_there (>= 0.3.1, < 1.0) + site_prism-all_there (0.3.2) + spring (2.1.1) spring-commands-parallel-tests (1.0.1) spring (>= 0.9.1) spring-commands-rspec (1.0.4) @@ -540,27 +520,27 @@ GEM sprockets (>= 3.0.0) thor (1.2.1) thread_safe (0.3.6) - tilt (2.0.10) + tilt (2.0.11) timecop (0.9.5) trollop (2.9.10) turbolinks (2.5.4) coffee-rails - tzinfo (1.2.9) + tzinfo (1.2.10) thread_safe (~> 0.1) uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) unf (0.1.4) unf_ext - unf_ext (0.0.8) - warden (1.2.7) - rack (>= 1.0) + unf_ext (0.0.8.2) + warden (1.2.9) + rack (>= 2.0.9) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) bindex (>= 0.4.0) railties (>= 5.0) - webmock (1.23.0) + webmock (2.3.2) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff @@ -569,19 +549,18 @@ GEM websocket-extensions (0.1.5) whenever (1.0.0) chronic (>= 0.6.3) - wicked_pdf (2.1.0) + wicked_pdf (2.6.3) activesupport - xpath (3.1.0) + xpath (3.2.0) nokogiri (~> 1.8) PLATFORMS ruby DEPENDENCIES - aws-sdk (~> 1.6) barby (~> 0.6) base58 (~> 0.1) - capybara (~> 2.4) + capybara (~> 3.17.0) capybara-screenshot (~> 1.0) cdx! cdx-api-elasticsearch! @@ -592,13 +571,13 @@ DEPENDENCIES d3_rails (~> 3.5.6) database_cleaner (~> 1.99) devise (~> 4.0.0) - devise-security (<= 0.12.0) + devise-security (< 0.15.0) devise_invitable (~> 1.5) doorkeeper (~> 4.2.0) dotiw (~> 3.0) dropzonejs-rails (~> 0.8.4) elasticsearch (~> 1.0) - encryptor (~> 1.3) + encryptor (~> 2.0) faker (< 1.9.2) ffaker (< 2.12.0) geojson_import! @@ -618,18 +597,17 @@ DEPENDENCIES machinist (~> 2.0) mini_racer mysql2 (~> 0.3) - nokogiri (~> 1.6, < 1.10.0) + nokogiri (~> 1.6, < 1.11.0) nuntium_api (~> 0.21) oj (~> 2.12, < 2.17.3) omniauth (~> 1.2) omniauth-google-oauth2 (~> 0.2) paperclip! - parallel (~> 1.19.2) - parallel_tests (~> 2.32.0) - paranoia (<= 2.4.2) - poirot_rails! + parallel (~> 1.20.0) + parallel_tests (~> 3.5.1) + paranoia (< 2.5.0) premailer-rails (< 1.10) - pry-byebug (< 2.7.0) + pry-byebug (< 3.10.0) pry-rescue pry-stack_explorer puma (~> 3.0) @@ -642,7 +620,7 @@ DEPENDENCIES rchardet (~> 1.6) react-rails (~> 1.3.2) recaptcha - rest-client (~> 1.8) + rest-client (~> 2.1) rqrcode (~> 0.10) rspec (~> 3.3) rspec-collection_matchers (~> 1.1) @@ -654,7 +632,7 @@ DEPENDENCIES sidekiq (~> 4.2) sidekiq-cron (~> 0.3) simplecov - site_prism (~> 2.17) + site_prism (~> 3.0) spring spring-commands-parallel-tests spring-commands-rspec @@ -665,7 +643,7 @@ DEPENDENCIES uglifier (~> 2.7) view_components! web-console (< 4.0) - webmock (~> 1.23.0) + webmock (~> 2.3.1) whenever (~> 1.0) wicked_pdf (~> 2.1) diff --git a/app/models/concerns/resource.rb b/app/models/concerns/resource.rb index ba278529e..61a234044 100644 --- a/app/models/concerns/resource.rb +++ b/app/models/concerns/resource.rb @@ -49,7 +49,7 @@ def supports_condition?(key) end def supports_identifier?(key) - key.blank? || key.kind_of?(Fixnum) || key.strip.match(/\A\d+\z/) + key.blank? || key.kind_of?(Integer) || key.strip.match(/\A\d+\z/) end def resource_name_prefix diff --git a/app/models/message_encryption.rb b/app/models/message_encryption.rb index 232013d9e..af36e584d 100644 --- a/app/models/message_encryption.rb +++ b/app/models/message_encryption.rb @@ -1,16 +1,17 @@ module MessageEncryption require 'securerandom' + ALGORITHM = "aes-256-cbc" # TODO: migrate to aes-256-gcm (default in encryptor 2.x) DEFAULT_IV = "\xD7\xCA\xD5\x9D\x1D\xC0I\x01Sf\xC8\xFBa\x88\xE1\x03" DEFAULT_SALT = "1403203711" def self.encrypt string - Encryptor.encrypt string, :key => secret_key, :iv => iv, :salt => salt unless string.blank? + Encryptor.encrypt string, :algorithm => ALGORITHM, :key => secret_key, :iv => iv, :salt => salt unless string.blank? end def self.decrypt string unless string.blank? - Encryptor.decrypt(string, :key => secret_key, :iv => iv, :salt => salt) + Encryptor.decrypt(string, :algorithm => ALGORITHM, :key => secret_key, :iv => iv, :salt => salt) else '' end @@ -26,8 +27,8 @@ def self.secure_random length def self.reencrypt(string, old_key:, old_iv: DEFAULT_IV, old_salt: DEFAULT_SALT, new_key:, new_iv: DEFAULT_IV, new_salt: DEFAULT_SALT) return '' if string.blank? - plain = Encryptor.decrypt(string, :key => old_key, :iv => old_iv, :salt => old_salt) - Encryptor.encrypt(plain, :key => new_key, :iv => new_iv, :salt => new_salt) unless plain.blank? + plain = Encryptor.decrypt(string, :algorithm => ALGORITHM, :key => old_key, :iv => old_iv, :salt => old_salt) + Encryptor.encrypt(plain, :algorithm => ALGORITHM, :key => new_key, :iv => new_iv, :salt => new_salt) unless plain.blank? end private diff --git a/spec/lib/sms_spec.rb b/spec/lib/sms_spec.rb index fb0b3268d..d9253d3cc 100644 --- a/spec/lib/sms_spec.rb +++ b/spec/lib/sms_spec.rb @@ -3,13 +3,15 @@ describe "SMS" do before(:each) do - stub_request(:get, "https://CDx%2FCDx-Dev:cdx123cdx@nuntium.instedd.org/CDx/CDx-Dev/send_ao?body=welcome%20mr%20smith&from=sms://442393162302&to=sms://123444"). - with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}). - to_return(:status => 200, :body => "{'id'=>'2051719', 'guid'=>'69abea50-840e-50a0-77f9-547b38b2943e', 'token'=>'76beefab-9686-cd17-a3bc-d79dec6e0544'}", :headers => {}) + authorization = Base64.urlsafe_encode64("CDx/CDx-Dev:cdx123cdx") - stub_request(:get, "https://CDx%2FCDx-Dev:cdx123cdx@nuntium.instedd.org/CDx/CDx-Dev/send_ao?body=welcome%20mr%20smith&from=sms://442393162302&to=sms://456777"). - with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}). - to_return(:status => 200, :body => "{'id'=>'2051719', 'guid'=>'69abea50-840e-50a0-77f9-547b38b2943e', 'token'=>'76beefab-9686-cd17-a3bc-d79dec6e0544'}", :headers => {}) + stub_request(:get, "https://nuntium.instedd.org/CDx/CDx-Dev/send_ao?body=welcome%20mr%20smith&from=sms://442393162302&to=sms://123444") + .with(:headers => {'Authorization' => "Basic #{authorization}", 'Content-Type'=>'application/json'}) + .to_return(:status => 200, :body => "{'id'=>'2051719', 'guid'=>'69abea50-840e-50a0-77f9-547b38b2943e', 'token'=>'76beefab-9686-cd17-a3bc-d79dec6e0544'}", :headers => {}) + + stub_request(:get, "https://nuntium.instedd.org/CDx/CDx-Dev/send_ao?body=welcome%20mr%20smith&from=sms://442393162302&to=sms://456777") + .with(:headers => {'Authorization' => "Basic #{authorization}", 'Content-Type'=>'application/json'}) + .to_return(:status => 200, :body => "{'id'=>'2051719', 'guid'=>'69abea50-840e-50a0-77f9-547b38b2943e', 'token'=>'76beefab-9686-cd17-a3bc-d79dec6e0544'}", :headers => {}) end context "sms operation" do diff --git a/spec/models/subscriber_spec.rb b/spec/models/subscriber_spec.rb index 426d1d77e..e6364a5b6 100644 --- a/spec/models/subscriber_spec.rb +++ b/spec/models/subscriber_spec.rb @@ -42,7 +42,7 @@ fields = ["test.assays", "patient.gender"] url = "http://subscriber/cdp_trigger" subscriber = Subscriber.make! fields: fields, url: url, filter: filter, verb: 'GET' - callback_query = "http://subscriber/cdp_trigger?patient%5Bgender%5D=male&test%5Bassays%5D%5B0%5D%5Bcondition%5D=mtb&test%5Bassays%5D%5B1%5D%5Bname%5D=mtb&test%5Bassays%5D%5B2%5D%5Bresult%5D=positive" + callback_query = "http://subscriber/cdp_trigger?patient%5Bgender%5D=male&test%5Bassays%5D%5B0%5D%5Bcondition%5D=mtb&test%5Bassays%5D%5B0%5D%5Bname%5D=mtb&test%5Bassays%5D%5B0%5D%5Bresult%5D=positive" callback_request = stub_request(:get, callback_query).to_return(status: 200, body: "", headers: {}) perform_enqueued_jobs { submit_test } diff --git a/spec/support/blueprints.rb b/spec/support/blueprints.rb index 8a58bd8e6..72880e147 100644 --- a/spec/support/blueprints.rb +++ b/spec/support/blueprints.rb @@ -8,6 +8,44 @@ 'bCAzqtIUUJOMKz4lHn5Os/d8temlYskaKQ1n+FuX5qJXNr1SW8euH72fjQndu78DCwVNwnnrG+nEe3a9m2QwL5xn'+ 'X8f1ohAZ9IG41hwIOvB5UcrFenqYIpMPBCCOnizUcyIFJhegJDWh2oWlBo041emGOX3VCRjtGug3 fbulgarelli@Manass-MacBook-2.local' +# NOTE: fixes compatibility with Ruby 2.4 (Fixnum is deprecated, use Integer) +module Machinist + class Lathe + protected + + def make_attribute(attribute, args, &block) #:nodoc: + count = args.shift if args.first.is_a?(Integer) + if count + Array.new(count) { make_one_value(attribute, args, &block) } + else + make_one_value(attribute, args, &block) + end + end + end + + module Machinable + private + + def decode_args_to_make(*args) #:nodoc: + shift_arg = lambda {|klass| args.shift if args.first.is_a?(klass) } + count = shift_arg[Integer] + name = shift_arg[Symbol] || :master + attributes = shift_arg[Hash] || {} + raise ArgumentError.new("Couldn't understand arguments") unless args.empty? + + @blueprints ||= {} + blueprint = @blueprints[name] + raise NoBlueprintError.new(self, name) unless blueprint + + if count.nil? + yield(blueprint, attributes) + else + Array.new(count) { yield(blueprint, attributes) } + end + end + end +end + User.blueprint do email { FFaker::Internet.email } password { FFaker::Internet.password } diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index a7405955d..edb95e5ff 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -56,7 +56,3 @@ # WARNING: this also affects matchers that want to verify that an element # doesn't exist, in which case you should override the wait time! Capybara.default_max_wait_time = 5 - -SitePrism.configure do |config| - config.use_implicit_waits = true -end From 3e65cb98596ffb2600e197724f2513356a89683d Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 15 Nov 2022 14:41:25 +0100 Subject: [PATCH 02/12] Fix: re-add aws-sdk and poirot_rails + update location_service gem --- Gemfile | 8 ++++---- Gemfile.lock | 35 +++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index a90403fae..04a506372 100644 --- a/Gemfile +++ b/Gemfile @@ -46,7 +46,7 @@ gem 'omniauth-google-oauth2', '~> 0.2' gem 'recaptcha' # Libraries -# gem 'aws-sdk', '~> 1.6' +gem 'aws-sdk', '~> 1.6' gem 'base58', '~> 0.1' gem 'barby', '~> 0.6' gem 'config', '~> 1.2' @@ -56,7 +56,7 @@ gem 'ffaker', '< 2.12.0' gem 'guid', '~> 0.1' gem 'nokogiri', '~> 1.6', '< 1.11.0' # last version to support ruby 2.4 gem 'oj', '~> 2.12', '< 2.17.3' # NOTE: 2.17.3 will stringify Time as a Float then load a BigDecimal... -# gem 'poirot_rails', git: 'https://github.com/instedd/poirot_rails.git', branch: 'master' +gem 'poirot_rails', git: 'https://github.com/instedd/poirot_rails.git', branch: 'master' gem 'rails-i18n', '~> 5.0' gem 'rchardet', '~> 1.6' gem 'rest-client', '~> 2.1' # NOTE: only used for a single HTTP call + Nuntium (SMS) + LocationService @@ -80,7 +80,7 @@ gem 'sidekiq-cron', '~> 0.3' # TODO: not maintained, consider sidekiq-scheduler gem 'whenever', '~> 1.0' # TODO: replace with a sidekiq-cron job # External services -gem 'location_service', path: "tmp/ruby-location_service" # git: 'https://github.com/instedd/ruby-location_service.git', branch: 'master' +gem 'location_service', git: 'https://github.com/instedd/ruby-location_service.git', ref: '99c97016c240af0d8e942add2e5ddc3e994a4d62' gem 'nuntium_api', '~> 0.21' gem 'sentry-raven', '~> 2.13' @@ -98,7 +98,7 @@ gem 'jquery-rails', '~> 4.0' gem 'jquery-turbolinks', '~> 2.1.0' gem 'leaflet-rails', '~> 0.7.4' gem 'lodash-rails', '~> 3.10.1' -gem 'react-rails', '~> 1.3.2' +gem 'react-rails', '~> 1.3.2' # NOTE: required for JSX templates source 'https://rails-assets.org' do gem 'rails-assets-urijs', '~> 1.17.0' diff --git a/Gemfile.lock b/Gemfile.lock index 67b903ee1..5710944e5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,6 +24,25 @@ GIT cocaine (~> 0.5.5) mime-types +GIT + remote: https://github.com/instedd/poirot_rails.git + revision: c3e8ceeacd07a226b941a8cabb2dd762fa73846f + branch: master + specs: + poirot_rails (0.0.4) + bertrpc + guid + json + rails (>= 3.2) + +GIT + remote: https://github.com/instedd/ruby-location_service.git + revision: 99c97016c240af0d8e942add2e5ddc3e994a4d62 + ref: 99c97016c240af0d8e942add2e5ddc3e994a4d62 + specs: + location_service (0.1.0) + rest-client (~> 2.0) + GIT remote: https://github.com/manastech/rails-view_components.git revision: 59861a6e716944441b4cd5e584bb3f36d0a7c37e @@ -42,12 +61,6 @@ PATH cdx elasticsearch -PATH - remote: tmp/ruby-location_service - specs: - location_service (0.1.0) - rest-client (~> 2.0) - GEM remote: https://rubygems.org/ remote: https://rails-assets.org/ @@ -92,6 +105,11 @@ GEM addressable (2.8.1) public_suffix (>= 2.0.2, < 6.0) arel (7.1.4) + aws-sdk (1.67.0) + aws-sdk-v1 (= 1.67.0) + aws-sdk-v1 (1.67.0) + json (~> 1.4) + nokogiri (~> 1) babel-source (5.8.35) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) @@ -100,6 +118,9 @@ GEM barby (0.6.8) base58 (0.2.3) bcrypt (3.1.18) + bert (1.1.6) + bertrpc (1.3.1) + bert (>= 1.1.0, < 2.0.0) bindex (0.8.1) binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) @@ -558,6 +579,7 @@ PLATFORMS ruby DEPENDENCIES + aws-sdk (~> 1.6) barby (~> 0.6) base58 (~> 0.1) capybara (~> 3.17.0) @@ -606,6 +628,7 @@ DEPENDENCIES parallel (~> 1.20.0) parallel_tests (~> 3.5.1) paranoia (< 2.5.0) + poirot_rails! premailer-rails (< 1.10) pry-byebug (< 3.10.0) pry-rescue From 91d370cb6ebb2a221b216a60c43dbd6f68bf5b51 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 15 Nov 2022 15:15:06 +0100 Subject: [PATCH 03/12] CI: fix coverage call command --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 937359802..402b07f3f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,7 +94,7 @@ jobs: - uses: actions/download-artifact@v3 with: name: integration_tests_coverage - - run: docker-compose run --no-deps --rm web rake coverage:report + - run: docker-compose run --no-deps --rm web bundle exec rails coverage:report - uses: actions/upload-artifact@v3 with: From c685f3e16656a5dbc96c77a52babb5d4d1e8e06d Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 5 Jan 2023 15:20:27 +0100 Subject: [PATCH 04/12] Upgrade ruby dependencies (security + location service) --- Gemfile | 2 +- Gemfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 04a506372..f063638e2 100644 --- a/Gemfile +++ b/Gemfile @@ -80,7 +80,7 @@ gem 'sidekiq-cron', '~> 0.3' # TODO: not maintained, consider sidekiq-scheduler gem 'whenever', '~> 1.0' # TODO: replace with a sidekiq-cron job # External services -gem 'location_service', git: 'https://github.com/instedd/ruby-location_service.git', ref: '99c97016c240af0d8e942add2e5ddc3e994a4d62' +gem 'location_service', git: 'https://github.com/instedd/ruby-location_service.git', branch: 'master' gem 'nuntium_api', '~> 0.21' gem 'sentry-raven', '~> 2.13' diff --git a/Gemfile.lock b/Gemfile.lock index 5710944e5..0005bed08 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,8 +37,8 @@ GIT GIT remote: https://github.com/instedd/ruby-location_service.git - revision: 99c97016c240af0d8e942add2e5ddc3e994a4d62 - ref: 99c97016c240af0d8e942add2e5ddc3e994a4d62 + revision: 736ca8174c37e1eda94f5573d614a92b6d742ab9 + branch: master specs: location_service (0.1.0) rest-client (~> 2.0) @@ -324,7 +324,7 @@ GEM rb-inotify (~> 0.9, >= 0.9.7) lodash-rails (3.10.1) railties (>= 3.1) - loofah (2.19.0) + loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) machinist (2.0) @@ -422,8 +422,8 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.3) - loofah (~> 2.3) + rails-html-sanitizer (1.4.4) + loofah (~> 2.19, >= 2.19.1) rails-i18n (5.1.3) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) From 2a1c41dab89721de91d0ece7dabae4ebdbe3ff0f Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 6 Jan 2023 13:34:01 +0100 Subject: [PATCH 05/12] Speedup integration tests + latest geckodriver --- Dockerfile.selenium | 11 ++--------- docker-compose.yml | 1 + features/support/page_objects/cdx_page_helper.rb | 4 +++- spec/spec_helper.rb | 6 +++++- spec/support/capybara.rb | 13 ++++++++++--- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Dockerfile.selenium b/Dockerfile.selenium index 9c0115770..65f95576b 100644 --- a/Dockerfile.selenium +++ b/Dockerfile.selenium @@ -1,15 +1,8 @@ FROM alpine -RUN apk add firefox-esr +RUN echo "https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories +RUN apk add firefox-esr geckodriver RUN ln -sf /usr/bin/firefox-esr /usr/bin/firefox -# FIXME: can't upgrade to 0.30 because Geckodriver only starts on loopback -RUN apk add curl && \ - curl -L https://github.com/mozilla/geckodriver/releases/download/v0.29.1/geckodriver-v0.29.1-linux64.tar.gz | tar -C /usr/local/bin -zx && \ - apk del curl - EXPOSE 4444 EXPOSE 5900 - -ENTRYPOINT ["geckodriver", "--host", "0.0.0.0"] - diff --git a/docker-compose.yml b/docker-compose.yml index 409a3cc9f..f3d999ee2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,7 @@ services: build: context: . dockerfile: Dockerfile.selenium + command: ['/usr/bin/geckodriver', '--host', '0.0.0.0'] working_dir: /src volumes: - .:/src # we mount the source to be able to attach fixture files diff --git a/features/support/page_objects/cdx_page_helper.rb b/features/support/page_objects/cdx_page_helper.rb index 9b7fd0300..1a0237e46 100644 --- a/features/support/page_objects/cdx_page_helper.rb +++ b/features/support/page_objects/cdx_page_helper.rb @@ -2,7 +2,9 @@ module CdxPageHelper # source: https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara def wait_for_ajax Timeout.timeout(Capybara.default_max_wait_time) do - loop until finished_all_ajax_requests? + until finished_all_ajax_requests? + sleep 0.01 + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3d1fb622b..25f935d8b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,11 @@ Dir[Rails.root.join("features/support/page_objects/*.rb")].each {|f| require f} Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} -WebMock.disable_net_connect!(:allow_localhost => true, allow: ALLOWED_WEBMOCK_HOSTS.compact) +WebMock.disable_net_connect!( + net_http_connect_on_start: true, + allow_localhost: true, + allow: ALLOWED_WEBMOCK_HOSTS.compact +) # This is to make machinist work with Rails 4 class ActiveRecord::Reflection::AssociationReflection diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index edb95e5ff..40c060714 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -23,6 +23,15 @@ config.server_port = ENV["SERVER_PORT"].to_i end +# Resolve domain name as IP (mainly for geckodriver to accept any connections) +begin + selenium_uri = URI(ENV["SELENIUM_URL"]) + selenium_uri.host = Addrinfo.tcp(selenium_uri.host, selenium_uri.port).ip_address + ENV["SELENIUM_URL"] = selenium_uri.to_s +rescue SocketError + STDERR.puts "WARNING: can't reach selenium server on #{ENV["SELENIUM_URL"]}" +end + if defined?(ALLOWED_WEBMOCK_HOSTS) ALLOWED_WEBMOCK_HOSTS << /http:\/\/#{Capybara.server_host}:#{Capybara.server_port}/ ALLOWED_WEBMOCK_HOSTS << /^#{Regexp.escape(ENV["SELENIUM_URL"])}/ @@ -51,8 +60,6 @@ # quickly in the DOM after submitting a form for example, by introducing an # implicit 5 seconds timeout to continuously retry the finder. # -# We must also tell SitePrism to respect this implicit wait time. -# # WARNING: this also affects matchers that want to verify that an element # doesn't exist, in which case you should override the wait time! -Capybara.default_max_wait_time = 5 +Capybara.default_max_wait_time = 2 From 80bf90261150d33424b97c000a308b6e28ea7504 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 6 Jan 2023 14:08:23 +0100 Subject: [PATCH 06/12] Fix: broken tests with webmock net http connect on start --- spec/spec_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 25f935d8b..c87a88a1e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,7 +25,6 @@ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} WebMock.disable_net_connect!( - net_http_connect_on_start: true, allow_localhost: true, allow: ALLOWED_WEBMOCK_HOSTS.compact ) From f550aad6487a11eaef3c173d1790497be361ff85 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 6 Jan 2023 14:39:46 +0100 Subject: [PATCH 07/12] Add host.docker.internal host to always target the host --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index f3d999ee2..301dadafc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,6 +27,8 @@ services: - elasticsearch - redis command: /bin/sh -c './bin/rails s -b 0.0.0.0' + extra_hosts: + - "host.docker.internal:host-gateway" selenium: build: From 5f1167e3ff6373d51aa4afe82e8c858a9f0e2170 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 6 Jan 2023 17:15:04 +0100 Subject: [PATCH 08/12] Speedup CdxSelect page object in some cases --- features/support/page_objects/cdx_select.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/features/support/page_objects/cdx_select.rb b/features/support/page_objects/cdx_select.rb index 469da5a7e..1de17ca02 100644 --- a/features/support/page_objects/cdx_select.rb +++ b/features/support/page_objects/cdx_select.rb @@ -40,13 +40,18 @@ def options def select_elem @select_elem ||= begin - elem = root_element + if root_element[:class].split(/\s+/).include?("Select") + root_element + else + # search until a parent's descendant matches '.Select' (very slow) + parent = root_element - while elem.all(".Select").empty? - elem = elem.find(:xpath, '..') - end + until element = parent.first(".Select", minimum: 0, maximum: 1) + parent = parent.find(:xpath, "..") + end - elem.find(".Select") + element + end end end end From a2089d07d4a051d24ada7dfb827ad1fd24dd71db Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 13 Jan 2023 15:29:16 +0100 Subject: [PATCH 09/12] Rework production dockerfile to not use nginx --- Dockerfile | 31 +++++++++++++++++-------------- docker/web-run | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 30f9e35de..7b9745aba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM instedd/nginx-rails:2.4 +FROM ruby:2.4 # Cleanup expired Let's Encrypt CA (Sept 30, 2021) RUN sed -i '/^mozilla\/DST_Root_CA_X3/s/^/!/' /etc/ca-certificates.conf && update-ca-certificates -f @@ -17,35 +17,38 @@ RUN curl -L https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/ dpkg -i wkhtmltopdf.deb && \ rm -f wkhtmltopdf.deb -## Create a user for the web app. -RUN \ - addgroup --gid 9999 app && \ - adduser --uid 9999 --gid 9999 --disabled-password --gecos "Application" app && \ - usermod -L app +# Create a user for the web app. +RUN addgroup --gid 9999 app && \ + adduser --uid 9999 --gid 9999 --disabled-password --gecos "Application" app && \ + usermod -L app + +# Application directory +RUN mkdir /app +WORKDIR /app +# Configuration ARG gemfile=Gemfile ENV BUNDLE_GEMFILE=${gemfile} ENV POIROT_STDOUT true ENV POIROT_SUPPRESS_RAILS_LOG true -ENV PUMA_OPTIONS "--preload -w 4" +ENV PUMA_OPTIONS "--preload -w 4 -p 3000" ENV NNDD_VERSION "cdx-0.11-pre7" +ENV RAILS_ENV=production # Install gem bundle COPY Gemfile* cdx.gemspec cdx-api-elasticsearch.gemspec /app/ COPY deps/ /app/deps/ - RUN bundle install --jobs 8 --deployment --without development test # Install the application ADD . /app # Precompile assets -RUN bundle exec rake assets:precompile RAILS_ENV=production +RUN bundle exec rake assets:precompile RAILS_ENV=${RAILS_ENV} # Download NNDD -RUN \ - mkdir -p /app/public/ && \ - curl -L https://github.com/instedd/notifiable-diseases/releases/download/$NNDD_VERSION/nndd.tar.gz | tar -xzv -C /app/public/ +RUN mkdir -p /app/public/ && \ + curl -L https://github.com/instedd/notifiable-diseases/releases/download/$NNDD_VERSION/nndd.tar.gz | tar -xzv -C /app/public/ # Configure NNDD RUN /app/docker/config-nndd @@ -53,5 +56,5 @@ RUN /app/docker/config-nndd # Set permissions for tmp and log directories RUN mkdir -p /app/tmp /app/log && chown -R app:app /app/tmp /app/log -# Add config files -ADD docker/web-run /etc/service/web/run +EXPOSE 3000 +CMD ["/app/docker/web-run"] diff --git a/docker/web-run b/docker/web-run index 8e68c5e72..ad9635a98 100755 --- a/docker/web-run +++ b/docker/web-run @@ -1,4 +1,4 @@ #!/bin/bash source /etc/envvars cd /app -exec su -p -c "bundle exec whenever --update-crontab && bundle exec puma $PUMA_OPTIONS -e $RAILS_ENV -b unix:///app/tmp/app.sock" app +exec su -p -c "bundle exec whenever --update-crontab && bundle exec puma $PUMA_OPTIONS -e $RAILS_ENV" app From 6df5db79e99b2513bc54f404d206e7fa41182901 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 13 Jan 2023 19:30:00 +0100 Subject: [PATCH 10/12] WIP: upgrade to React 0.14 --- Gemfile | 8 +++--- Gemfile.lock | 20 +++++++-------- app/assets/javascripts/box_selector.js.jsx | 14 +++-------- .../javascripts/components/bar_chart.js.jsx | 25 +++++++------------ .../javascripts/components/cdx_select.js.jsx | 2 +- .../components/encounter_form.js.jsx | 2 +- .../components/location_select.js.jsx | 2 +- .../javascripts/components/modal.js.jsx.erb | 2 +- .../javascripts/components/pie_chart.js.jsx | 19 ++++++-------- frontend_dependencies.txt | 3 +++ .../javascripts/react-input-autosize.js | 19 +++++++------- vendor/assets/javascripts/react-select.js | 15 +++++------ 12 files changed, 58 insertions(+), 73 deletions(-) create mode 100644 frontend_dependencies.txt diff --git a/Gemfile b/Gemfile index f063638e2..c7d4ac7d9 100644 --- a/Gemfile +++ b/Gemfile @@ -98,7 +98,7 @@ gem 'jquery-rails', '~> 4.0' gem 'jquery-turbolinks', '~> 2.1.0' gem 'leaflet-rails', '~> 0.7.4' gem 'lodash-rails', '~> 3.10.1' -gem 'react-rails', '~> 1.3.2' # NOTE: required for JSX templates +gem 'react-rails', '~> 1.6.2' # NOTE: required for JSX templates source 'https://rails-assets.org' do gem 'rails-assets-urijs', '~> 1.17.0' @@ -136,9 +136,9 @@ group :test do gem 'webmock', '~> 2.3.1', require: false # integration tests - gem 'capybara', '~> 3.17.0' + gem 'capybara', '~> 3.29.0' # limited by site_prism 3.6 gem 'capybara-screenshot', '~> 1.0' gem 'cucumber-rails', '~> 1.5', require: false - gem 'selenium-webdriver', '< 4.0' - gem 'site_prism', '~> 3.0' + gem 'selenium-webdriver' + gem 'site_prism', '~> 3.6.0' # last version to support ruby 2.4 end diff --git a/Gemfile.lock b/Gemfile.lock index 0005bed08..01671475d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -126,13 +126,13 @@ GEM debug_inspector (>= 0.0.1) builder (3.2.4) byebug (11.1.3) - capybara (3.17.0) + capybara (3.29.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.2) + regexp_parser (~> 1.5) xpath (~> 3.2) capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) @@ -255,7 +255,7 @@ GEM erubis (2.7.0) et-orbi (1.2.7) tzinfo - execjs (2.7.0) + execjs (2.8.1) faker (1.9.1) i18n (>= 0.7) faraday (0.17.6) @@ -438,7 +438,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) rchardet (1.8.0) - react-rails (1.3.3) + react-rails (1.6.2) babel-transpiler (>= 0.7.0) coffee-script-source (~> 1.8) connection_pool @@ -519,9 +519,9 @@ GEM docile (~> 1.1) simplecov-html (~> 0.11) simplecov-html (0.12.3) - site_prism (3.5) + site_prism (3.6) addressable (~> 2.5) - capybara (<= 3.29) + capybara (>= 3.8, <= 3.29) site_prism-all_there (>= 0.3.1, < 1.0) site_prism-all_there (0.3.2) spring (2.1.1) @@ -582,7 +582,7 @@ DEPENDENCIES aws-sdk (~> 1.6) barby (~> 0.6) base58 (~> 0.1) - capybara (~> 3.17.0) + capybara (~> 3.29.0) capybara-screenshot (~> 1.0) cdx! cdx-api-elasticsearch! @@ -641,7 +641,7 @@ DEPENDENCIES rails-i18n (~> 5.0) rake (~> 10.5.0) rchardet (~> 1.6) - react-rails (~> 1.3.2) + react-rails (~> 1.6.2) recaptcha rest-client (~> 2.1) rqrcode (~> 0.10) @@ -650,12 +650,12 @@ DEPENDENCIES rspec-rails (~> 3.3) rubyzip (>= 1.0.0) sass-rails (~> 5.0, < 5.0.8) - selenium-webdriver (< 4.0) + selenium-webdriver sentry-raven (~> 2.13) sidekiq (~> 4.2) sidekiq-cron (~> 0.3) simplecov - site_prism (~> 3.0) + site_prism (~> 3.6.0) spring spring-commands-parallel-tests spring-commands-rspec diff --git a/app/assets/javascripts/box_selector.js.jsx b/app/assets/javascripts/box_selector.js.jsx index 05f6f1ff9..4ef2fe969 100644 --- a/app/assets/javascripts/box_selector.js.jsx +++ b/app/assets/javascripts/box_selector.js.jsx @@ -40,8 +40,7 @@ var BoxSelector = React.createClass({ } - const listItems = this.state.boxes.map(renderBox) - return ({ listItems }); + return this.state.boxes.map(renderBox) }, removeBox: function(box) { @@ -178,13 +177,6 @@ var BoxSelector = React.createClass({ ) }, - renderError: function() { - let error = this.state.error; - return ( - { error } - ) - }, - render: function() { return (
@@ -193,13 +185,13 @@ var BoxSelector = React.createClass({
{this.renderBoxes()}
-
- {this.renderError()} + {this.state.error}
diff --git a/app/assets/javascripts/components/bar_chart.js.jsx b/app/assets/javascripts/components/bar_chart.js.jsx index 9f8cc1207..f3dd8b2b5 100644 --- a/app/assets/javascripts/components/bar_chart.js.jsx +++ b/app/assets/javascripts/components/bar_chart.js.jsx @@ -7,17 +7,10 @@ var BarChart = React.createClass({ } }, - componentDidMount: function() { - if (!this.props.width) { - this.setProps({ - width: this.refs.svg.getDOMNode().clientWidth - }) - } - }, - render: function() { - if (this.props.width) { - var chartWidth = this.props.width - this.props.margin.left - this.props.margin.right, + var width = this.props.width || (this.refs.svg && this.refs.svg.clientWidth); + if (width) { + var chartWidth = width - this.props.margin.left - this.props.margin.right, chartHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; var x = d3.scale.ordinal() @@ -31,7 +24,7 @@ var BarChart = React.createClass({ .orient("bottom"); var rotateLabels = function(dom) { - d3.select(dom.getDOMNode()).selectAll("text") + d3.select(ReactDOM.findDOMNode(dom)).selectAll("text") .attr("y", 0) .attr("x", 9) .attr("dy", ".35em") @@ -50,8 +43,8 @@ var BarChart = React.createClass({ } var svgProps = {} - if (this.props.width) { - svgProps.viewBox = "0 0 " + this.props.width + " " + this.props.height + if (width) { + svgProps.viewBox = "0 0 " + width + " " + this.props.height } return ( @@ -60,7 +53,7 @@ var BarChart = React.createClass({ height={this.props.height} ref="svg" {...svgProps} > - { this.props.width ? + { width ? {/* Bars */} @@ -82,12 +75,12 @@ var BarChart = React.createClass({ {/* X Axis */} + ref={function(ref) { if (ref) { d3.select(ref).call(xAxis); rotateLabels(ref); }}} /> {/* Y Axis */} + ref={function(ref) { if (ref) { d3.select(ref).call(yAxis) }}} > {this.props.y_label} diff --git a/app/assets/javascripts/components/cdx_select.js.jsx b/app/assets/javascripts/components/cdx_select.js.jsx index 6c33547ee..017dc044a 100644 --- a/app/assets/javascripts/components/cdx_select.js.jsx +++ b/app/assets/javascripts/components/cdx_select.js.jsx @@ -9,7 +9,7 @@ var CdxSelect = React.createClass({ window.setTimeout(function(){ // this is deferred so a new input with the new value // is rendered by the time the change event is triggered - $('input:hidden', this.getDOMNode()).trigger('change'); + $('input:hidden', ReactDOM.findDOMNode(this)).trigger('change'); }.bind(this), 0); if(this.props.onChange) { this.props.onChange(newValue, this); diff --git a/app/assets/javascripts/components/encounter_form.js.jsx b/app/assets/javascripts/components/encounter_form.js.jsx index f82d0043f..9a07194d5 100644 --- a/app/assets/javascripts/components/encounter_form.js.jsx +++ b/app/assets/javascripts/components/encounter_form.js.jsx @@ -37,7 +37,7 @@ var BaseEncounterForm = { }, validateAndSetManualEntry: function (event) { - var sampleId = React.findDOMNode(this.refs.manualSampleEntry).value; + var sampleId = ReactDOM.findDOMNode(this.refs.manualSampleEntry).value; if(this.state.encounter.new_samples.filter(function(el){return el.entity_id == sampleId}).length > 0) { // Error handling as done in the ajax responses alert("This sample has already been added"); diff --git a/app/assets/javascripts/components/location_select.js.jsx b/app/assets/javascripts/components/location_select.js.jsx index 1a1803fc5..ddb9b4b4a 100644 --- a/app/assets/javascripts/components/location_select.js.jsx +++ b/app/assets/javascripts/components/location_select.js.jsx @@ -65,7 +65,7 @@ var LocationSelect = React.createClass({ window.setTimeout(function(){ // this is deferred so a new input with the new value // is rendered by the time the change event is triggered - $('input:hidden', this.getDOMNode()).trigger('change'); + $('input:hidden', ReactDOM.getDOMNode(this)).trigger('change'); }.bind(this), 0); var _this = this; diff --git a/app/assets/javascripts/components/modal.js.jsx.erb b/app/assets/javascripts/components/modal.js.jsx.erb index 7ee8d24a0..1fe74363b 100644 --- a/app/assets/javascripts/components/modal.js.jsx.erb +++ b/app/assets/javascripts/components/modal.js.jsx.erb @@ -16,7 +16,7 @@ var Modal = React.createClass({ }, hideOnOuterClick: function(event) { - if (this.getDOMNode() == event.target) + if (ReactDOM.findDOMNode(this) == event.target) this.hide(); }, diff --git a/app/assets/javascripts/components/pie_chart.js.jsx b/app/assets/javascripts/components/pie_chart.js.jsx index bdc0f2699..060c4d1d4 100644 --- a/app/assets/javascripts/components/pie_chart.js.jsx +++ b/app/assets/javascripts/components/pie_chart.js.jsx @@ -7,20 +7,15 @@ var PieChart = React.createClass({ } }, - componentDidMount: function() { - if (!this.props.width) { - this.setProps({ - width: this.refs.svg.getDOMNode().clientWidth - }) - } - }, - buildColorScale: function () { return d3.scale.ordinal().range(this.props.colors); }, componentDidMount: function () { - var svg = d3.select(this.refs.svg.getDOMNode()); + if (!this.state) this.state = {}; + this.state.width = this.props.width || (this.refs.svg && this.refs.svg.clientWidth); + + var svg = d3.select(this.refs.svg); var data = this.props.data; var total = _.sum(data, 'value'); @@ -91,7 +86,7 @@ var PieChart = React.createClass({ }, render: function() { - var radius = Math.min(this.props.width || this.props.height, this.props.height) / 2; + var radius = Math.min((this.state && this.state.width) || this.props.height, this.props.height) / 2; var color = this.buildColorScale(); @@ -108,8 +103,8 @@ var PieChart = React.createClass({ .rangePoints([-25 * (this.props.data.length - 1) / 2, 25 * (this.props.data.length - 1) / 2]); var svgProps = {} - if (this.props.width) { - svgProps.viewBox = "0 0 " + this.props.width + " " + this.props.height + if (this.state && this.state.width) { + svgProps.viewBox = "0 0 " + this.state.width + " " + this.props.height } return ( diff --git a/frontend_dependencies.txt b/frontend_dependencies.txt new file mode 100644 index 000000000..5db3b8d74 --- /dev/null +++ b/frontend_dependencies.txt @@ -0,0 +1,3 @@ +react v0.14 +react-select v0.7.0 +react-input-autosize v0.6.0 diff --git a/vendor/assets/javascripts/react-input-autosize.js b/vendor/assets/javascripts/react-input-autosize.js index 121b1aed6..d321998d8 100755 --- a/vendor/assets/javascripts/react-input-autosize.js +++ b/vendor/assets/javascripts/react-input-autosize.js @@ -4,6 +4,7 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var React = (window.React); +var ReactDOM = (window.ReactDOM); var sizerStyle = { position: 'absolute', visibility: 'hidden', height: 0, width: 0, overflow: 'scroll', whiteSpace: 'nowrap' }; @@ -42,27 +43,27 @@ var AutosizeInput = React.createClass({ if (!this.isMounted() || !window.getComputedStyle) { return; } - var inputStyle = window.getComputedStyle(React.findDOMNode(this.refs.input)); - var widthNode = React.findDOMNode(this.refs.sizer); + var inputStyle = window.getComputedStyle(ReactDOM.findDOMNode(this.refs.input)); + var widthNode = ReactDOM.findDOMNode(this.refs.sizer); widthNode.style.fontSize = inputStyle.fontSize; widthNode.style.fontFamily = inputStyle.fontFamily; widthNode.style.letterSpacing = inputStyle.letterSpacing; if (this.props.placeholder) { - var placeholderNode = React.findDOMNode(this.refs.placeholderSizer); + var placeholderNode = ReactDOM.findDOMNode(this.refs.placeholderSizer); placeholderNode.style.fontSize = inputStyle.fontSize; placeholderNode.style.fontFamily = inputStyle.fontFamily; placeholderNode.style.letterSpacing = inputStyle.letterSpacing; } }, updateInputWidth: function updateInputWidth() { - if (!this.isMounted() || typeof React.findDOMNode(this.refs.sizer).scrollWidth === 'undefined') { + if (!this.isMounted() || typeof ReactDOM.findDOMNode(this.refs.sizer).scrollWidth === 'undefined') { return; } var newInputWidth; if (this.props.placeholder) { - newInputWidth = Math.max(React.findDOMNode(this.refs.sizer).scrollWidth, React.findDOMNode(this.refs.placeholderSizer).scrollWidth) + 2; + newInputWidth = Math.max(ReactDOM.findDOMNode(this.refs.sizer).scrollWidth, ReactDOM.findDOMNode(this.refs.placeholderSizer).scrollWidth) + 2; } else { - newInputWidth = React.findDOMNode(this.refs.sizer).scrollWidth + 2; + newInputWidth = ReactDOM.findDOMNode(this.refs.sizer).scrollWidth + 2; } if (newInputWidth < this.props.minWidth) { newInputWidth = this.props.minWidth; @@ -77,10 +78,10 @@ var AutosizeInput = React.createClass({ return this.refs.input; }, focus: function focus() { - React.findDOMNode(this.refs.input).focus(); + ReactDOM.findDOMNode(this.refs.input).focus(); }, select: function select() { - React.findDOMNode(this.refs.input).select(); + ReactDOM.findDOMNode(this.refs.input).select(); }, render: function render() { var escapedValue = (this.props.value || '').replace(/\&/g, '&').replace(/ /g, ' ').replace(/\/g, '>'); @@ -107,4 +108,4 @@ var AutosizeInput = React.createClass({ module.exports = AutosizeInput; },{}]},{},[1])(1) -}); \ No newline at end of file +}); diff --git a/vendor/assets/javascripts/react-select.js b/vendor/assets/javascripts/react-select.js index e132f6992..3611bef49 100755 --- a/vendor/assets/javascripts/react-select.js +++ b/vendor/assets/javascripts/react-select.js @@ -70,6 +70,7 @@ module.exports = Option; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var React = (typeof window !== "undefined" ? window['React'] : typeof global !== "undefined" ? global['React'] : null); +var ReactDOM = (typeof window !== "undefined" ? window['ReactDOM'] : typeof global !== "undefined" ? global['ReactDOM'] : null); var Input = (typeof window !== "undefined" ? window['AutosizeInput'] : typeof global !== "undefined" ? global['AutosizeInput'] : null); var classes = (typeof window !== "undefined" ? window['classNames'] : typeof global !== "undefined" ? global['classNames'] : null); var Value = require('./Value'); @@ -192,8 +193,8 @@ var Select = React.createClass({ if (!_this.state.isOpen) { return; } - var menuElem = React.findDOMNode(_this.refs.selectMenuContainer); - var controlElem = React.findDOMNode(_this.refs.control); + var menuElem = ReactDOM.findDOMNode(_this.refs.selectMenuContainer); + var controlElem = ReactDOM.findDOMNode(_this.refs.control); var eventOccuredOutsideMenu = _this.clickedOutsideElement(menuElem, event); var eventOccuredOutsideControl = _this.clickedOutsideElement(controlElem, event); @@ -273,8 +274,8 @@ var Select = React.createClass({ } if (this._focusedOptionReveal) { if (this.refs.focused && this.refs.menu) { - var focusedDOM = React.findDOMNode(this.refs.focused); - var menuDOM = React.findDOMNode(this.refs.menu); + var focusedDOM = ReactDOM.findDOMNode(this.refs.focused); + var menuDOM = ReactDOM.findDOMNode(this.refs.menu); var focusedRect = focusedDOM.getBoundingClientRect(); var menuRect = menuDOM.getBoundingClientRect(); @@ -420,7 +421,7 @@ var Select = React.createClass({ getInputNode: function getInputNode() { var input = this.refs.input; - return this.props.searchable ? input : React.findDOMNode(input); + return this.props.searchable ? input : ReactDOM.findDOMNode(input); }, fireChangeEvent: function fireChangeEvent(newState) { @@ -979,7 +980,7 @@ var Select = React.createClass({ module.exports = Select; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./Option":1,"./SingleValue":3,"./Value":4}],3:[function(require,module,exports){ +},{"./Option":1,"./SingleValue":3,"./Value":4,"react-dom":undefined}],3:[function(require,module,exports){ (function (global){ 'use strict'; @@ -1098,4 +1099,4 @@ module.exports = Value; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}]},{},[2])(2) -}); \ No newline at end of file +}); From 3f27d81410c4e857732c67ad392f5e8fbc5cf505 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Mon, 16 Jan 2023 11:21:16 +0100 Subject: [PATCH 11/12] CI: save capybara screenshots when integration tests fail --- .github/workflows/test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 402b07f3f..755fc19e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,6 +78,13 @@ jobs: path: resultset.*.json retention-days: 1 + - uses: actions/upload-artifact@v3 + if: ${{ failure() }} + with: + name: capybara_screenshots + path: tmp/capybara/* + retention-days: 7 + analysis: runs-on: ubuntu-latest needs: From d709143d74d0e7fa9b0b8a516a21f5ff93abcbc4 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Mon, 16 Jan 2023 13:34:22 +0100 Subject: [PATCH 12/12] CI: debug JS logs (with tweak for Firefox) --- .github/workflows/test.yml | 2 +- app/assets/javascripts/cdx_console_logs.js | 24 ++++++++++++++++++++++ app/views/layouts/application.html.haml | 1 + app/views/layouts/clean.haml | 1 + app/views/layouts/devise.haml | 1 + spec/support/feature_spec_helpers.rb | 13 ++++++++++++ 6 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/cdx_console_logs.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 755fc19e7..62c227002 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,7 +65,7 @@ jobs: run: docker-compose up -d selenium - name: Run specs (capybara) - run: docker-compose run --rm -e RAILS_ENV=test -e COVERAGE=true -e FEATURES=true -e SELENIUM_URL web bundle exec rspec + run: docker-compose run --rm -e RAILS_ENV=test -e COVERAGE=true -e FEATURES=true -e SELENIUM_URL web bundle exec rspec --format=documentation - name: Run specs (cucumber) run: docker-compose run --rm -e RAILS_ENV=test -e COVERAGE=true -e SELENIUM_URL web bundle exec cucumber diff --git a/app/assets/javascripts/cdx_console_logs.js b/app/assets/javascripts/cdx_console_logs.js new file mode 100644 index 000000000..4d546a8b6 --- /dev/null +++ b/app/assets/javascripts/cdx_console_logs.js @@ -0,0 +1,24 @@ +(function() { + var originals = {}; + window.__cdx_logs = []; + + function capture_logs(name) { + originals[name] = window.console[name]; + + window.console[name] = function () { + var args = Array.prototype.map.call(arguments, function (x) { + return x.toString(); + }); + window.__cdx_logs.push([name.toUpperCase()].concat(args)); + + return originals[name].apply(null, arguments); + } + } + + capture_logs("debug"); + capture_logs("error"); + capture_logs("info"); + capture_logs("log"); + capture_logs("trace"); + capture_logs("warn"); +})(); diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index a1ea5f1d8..31d42afa8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -4,6 +4,7 @@ %title Connected Diagnostics Platform = Gon::Base.render_data({}) = stylesheet_link_tag "application", :media => "all" + = javascript_include_tag "cdx_console_logs" if Rails.env.test? && defined?(Capybara) && ENV["BROWSER"] != "chrome" = javascript_include_tag "application" = csrf_meta_tags %body{class: @body_class} diff --git a/app/views/layouts/clean.haml b/app/views/layouts/clean.haml index 3843468db..38d0eba79 100644 --- a/app/views/layouts/clean.haml +++ b/app/views/layouts/clean.haml @@ -4,6 +4,7 @@ %title Connected Diagnostics Platform = Gon::Base.render_data({}) = stylesheet_link_tag "application", :media => "all" + = javascript_include_tag "cdx_console_logs" if Rails.env.test? && defined?(Capybara) && ENV["BROWSER"] != "chrome" = javascript_include_tag "application" = csrf_meta_tags %body.devise{class: @body_class} diff --git a/app/views/layouts/devise.haml b/app/views/layouts/devise.haml index 15a49c61f..79599c88e 100644 --- a/app/views/layouts/devise.haml +++ b/app/views/layouts/devise.haml @@ -4,6 +4,7 @@ %title Connected Diagnostics Platform = Gon::Base.render_data({}) = stylesheet_link_tag "application", :media => "all" + = javascript_include_tag "cdx_console_logs" if Rails.env.test? && defined?(Capybara) && ENV["BROWSER"] != "chrome" = javascript_include_tag "application" = csrf_meta_tags %body.devise{class: @body_class} diff --git a/spec/support/feature_spec_helpers.rb b/spec/support/feature_spec_helpers.rb index 5bc61a50f..9265b8c93 100644 --- a/spec/support/feature_spec_helpers.rb +++ b/spec/support/feature_spec_helpers.rb @@ -4,6 +4,19 @@ module FeatureSpecHelpers included do metadata[:js] = true metadata[:elasticsearch] = true + + after(:each) do + next unless session = Capybara.current_session + + if session.driver.respond_to?(:log) + session.driver.browser.manage.logs.get(:browser) + .each { |log| puts "JS: #{log.level} #{log.message}" } + else + # geckodriver doesn't implement the log interface (sigh) + session.execute_script("return window.__cdx_logs;") + &.each { |log| puts "JS: #{log.join(" ")}" } + end + end end def process(args = {})