From 0ce1919cfb40121187ab7d77cbff06d5fec86fdb Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 27 Jan 2020 16:21:54 +0000 Subject: [PATCH 01/22] ci: save yarn cache after installing it --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 331c4b4b4..a6a4214e9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,12 +30,12 @@ bundle_install: &bundle_install yarn_restore_cache: &yarn_restore_cache restore_cache: name: Restore Yarn Package Cache - key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }} + key: yarn-install-v2-{{ arch }}-{{ checksum "yarn.lock" }} yarn_save_cache: &yarn_save_cache save_cache: name: Save Yarn Package Cache - key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }} + key: yarn-install-v2-{{ arch }}-{{ checksum "yarn.lock" }} paths: - ~/.cache/yarn @@ -53,8 +53,8 @@ jobs: - *bundle_install - *bundle_save_cache - *yarn_restore_cache - - *yarn_save_cache - *yarn_install + - *yarn_save_cache test: <<: *defaults parallelism: 3 From 9e7371d19a903c5a476c0c822498accf31e525d7 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 27 Jan 2020 16:37:09 +0000 Subject: [PATCH 02/22] ci: don't allow yarn to update the lockfile during CI builds Yarn will now throw an error if there's a mismatch between the package.json and the package.lock content. --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a6a4214e9..293a7ba74 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,19 +30,19 @@ bundle_install: &bundle_install yarn_restore_cache: &yarn_restore_cache restore_cache: name: Restore Yarn Package Cache - key: yarn-install-v2-{{ arch }}-{{ checksum "yarn.lock" }} + key: yarn-install-v3-{{ arch }}-{{ checksum "yarn.lock" }} yarn_save_cache: &yarn_save_cache save_cache: name: Save Yarn Package Cache - key: yarn-install-v2-{{ arch }}-{{ checksum "yarn.lock" }} + key: yarn-install-v3-{{ arch }}-{{ checksum "yarn.lock" }} paths: - ~/.cache/yarn yarn_install: &yarn_install run: name: Install JS Dependencies - command: yarn install --non-interactive || yarn install --non-interactive + command: yarn install --frozen-lockfile --non-interactive || yarn install --frozen-lockfile --non-interactive jobs: build: From bd1a2caad94cbce1056add81094782dd7ea4e36f Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 27 Jan 2020 16:55:23 +0000 Subject: [PATCH 03/22] ci: save webpacker cache --- .circleci/config.yml | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 293a7ba74..f6bc51c0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,6 +44,29 @@ yarn_install: &yarn_install name: Install JS Dependencies command: yarn install --frozen-lockfile --non-interactive || yarn install --frozen-lockfile --non-interactive +webpacker_restore_cache: &webpacker_restore_cache + restore_cache: + name: Restore Webpacker Cache + keys: + - webpacker-v1-{{ .Branch }}-{{ .Revision }} + - webpacker-v1-{{ .Branch }} + - webpacker-v1 + +webpacker_save_cache: &webpacker_save_cache + save_cache: + name: Save Webpacker Cache + key: webpacker-v1-{{ .Branch }}-{{ .Revision }} + paths: + - public/packs-test + - tmp/cache/webpacker + +webpacker_precompile: &webpacker_precompile + run: + environment: + RAILS_ENV: test + name: Precompile Webpack assets + command: bin/webpack + jobs: build: <<: *defaults @@ -64,16 +87,15 @@ jobs: - *bundle_install - *yarn_restore_cache - *yarn_install + - *webpacker_restore_cache + - *webpacker_precompile + - *webpacker_save_cache - run: environment: DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test" - name: Create DB + name: Create Database command: bundle exec rake db:create db:schema:load db:migrate RAILS_ENV=test - - run: - environment: - RAILS_ENV: test - name: Precompile Webpack assets - command: bin/webpack + - run: environment: DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test" From 9a18f4072006f059d75b6b51fac694c5ff34e8dd Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 27 Jan 2020 18:12:29 +0100 Subject: [PATCH 04/22] ci: increase the change of cache hits See https://circleci.com/docs/2.0/caching/#restoring-cache --- .circleci/config.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f6bc51c0d..b28ae8fe2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,10 @@ defaults: &defaults bundle_restore_cache: &bundle_restore_cache restore_cache: name: Restore Bundler Package Cache - key: bundle-install-v9-{{ arch }}-{{ checksum "Gemfile.lock" }} + keys: + - bundle-install-v9-{{ arch }}-{{ checksum "Gemfile.lock" }} + - bundle-install-v9-{{ arch }} + - bundle-install-v9 bundle_save_cache: &bundle_save_cache save_cache: @@ -30,7 +33,10 @@ bundle_install: &bundle_install yarn_restore_cache: &yarn_restore_cache restore_cache: name: Restore Yarn Package Cache - key: yarn-install-v3-{{ arch }}-{{ checksum "yarn.lock" }} + keys: + - yarn-install-v3-{{ arch }}-{{ checksum "yarn.lock" }} + - yarn-install-v3-{{ arch }} + - yarn-install-v3 yarn_save_cache: &yarn_save_cache save_cache: From aaaf6f393b3b6c20e462658c0d96992b52cd7f09 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 28 Jan 2020 12:00:34 +0100 Subject: [PATCH 05/22] specs: add an actual test case to the Exercice spec This fixes the "No timing found for 'spec/models/exercice_spec.rb'" warning message during specs. --- spec/models/exercice_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/models/exercice_spec.rb b/spec/models/exercice_spec.rb index cf64c6cfc..9a906ed7a 100644 --- a/spec/models/exercice_spec.rb +++ b/spec/models/exercice_spec.rb @@ -1,4 +1,7 @@ require 'spec_helper' describe Exercice do + describe 'validations' do + it { is_expected.to validate_presence_of(:ca) } + end end From 12c02a602f2cd424ccd35ab8e5da3a3f439da64a Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Jan 2020 12:33:37 +0100 Subject: [PATCH 06/22] Update rspec --- Gemfile.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c3d75309d..21057c302 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -117,7 +117,7 @@ GEM railties (>= 3.0) brakeman (4.3.1) browser (2.5.3) - builder (3.2.3) + builder (3.2.4) byebug (10.0.2) capybara (3.29.0) addressable @@ -155,7 +155,7 @@ GEM connection_pool (2.2.2) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.5) + crass (1.0.6) css_parser (1.6.0) addressable curb (0.9.10) @@ -305,7 +305,7 @@ GEM domain_name (~> 0.5) http_parser.rb (0.6.0) httpclient (2.8.3) - i18n (1.7.0) + i18n (1.8.2) concurrent-ruby (~> 1.0) ipaddress (0.8.3) jaro_winkler (1.5.2) @@ -350,7 +350,7 @@ GEM railties (>= 4) request_store (~> 1.0) logstash-event (1.2.02) - loofah (2.3.1) + loofah (2.4.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) lumberjack (1.0.13) @@ -369,7 +369,7 @@ GEM mimemagic (0.3.3) mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.13.0) + minitest (5.14.0) momentjs-rails (2.20.1) railties (>= 3.1) multi_json (1.14.1) @@ -379,7 +379,7 @@ GEM nenv (0.3.0) netrc (0.11.0) nio4r (2.5.2) - nokogiri (1.10.5) + nokogiri (1.10.7) mini_portile2 (~> 2.4.0) notiffany (0.1.1) nenv (~> 0.1) @@ -450,7 +450,7 @@ GEM puma (3.12.2) pundit (2.0.1) activesupport (>= 3.0.0) - rack (2.0.8) + rack (2.1.2) rack-attack (6.0.0) rack (>= 1.0, < 3) rack-mini-profiler (1.0.1) @@ -528,27 +528,27 @@ GEM builder (>= 3.0) rubyzip (>= 1.0) rouge (3.9.0) - rspec (3.8.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) - rspec-core (3.8.0) - rspec-support (~> 3.8.0) - rspec-expectations (3.8.2) + rspec (3.9.0) + rspec-core (~> 3.9.0) + rspec-expectations (~> 3.9.0) + rspec-mocks (~> 3.9.0) + rspec-core (3.9.1) + rspec-support (~> 3.9.1) + rspec-expectations (3.9.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-mocks (3.8.0) + rspec-support (~> 3.9.0) + rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-rails (3.8.1) + rspec-support (~> 3.9.0) + rspec-rails (3.9.0) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) - rspec-support (~> 3.8.0) - rspec-support (3.8.0) + rspec-core (~> 3.9.0) + rspec-expectations (~> 3.9.0) + rspec-mocks (~> 3.9.0) + rspec-support (~> 3.9.0) + rspec-support (3.9.2) rspec_junit_formatter (0.4.1) rspec-core (>= 2, < 4, != 2.12.0) rubocop (0.62.0) @@ -644,7 +644,7 @@ GEM httpclient (>= 2.4) sysexits (1.2.0) temple (0.8.0) - thor (0.20.3) + thor (1.0.1) thread_safe (0.3.6) tilt (2.0.9) timecop (0.9.1) @@ -654,7 +654,7 @@ GEM turbolinks-source (5.2.0) typhoeus (1.3.1) ethon (>= 0.9.0) - tzinfo (1.2.5) + tzinfo (1.2.6) thread_safe (~> 0.1) unf (0.1.4) unf_ext From 04ad829e3f9372c20eca0c17b13c22b631c44538 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 4 Dec 2019 16:58:26 +0100 Subject: [PATCH 07/22] Add canonical_procedure_id to procedures --- ...0200114113700_add_canonical_procedure_id_to_procedures.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20200114113700_add_canonical_procedure_id_to_procedures.rb diff --git a/db/migrate/20200114113700_add_canonical_procedure_id_to_procedures.rb b/db/migrate/20200114113700_add_canonical_procedure_id_to_procedures.rb new file mode 100644 index 000000000..a2ae37565 --- /dev/null +++ b/db/migrate/20200114113700_add_canonical_procedure_id_to_procedures.rb @@ -0,0 +1,5 @@ +class AddCanonicalProcedureIdToProcedures < ActiveRecord::Migration[5.2] + def change + add_column :procedures, :canonical_procedure_id, :bigint + end +end diff --git a/db/schema.rb b/db/schema.rb index e7ad727d2..e998f6be4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_12_18_103727) do +ActiveRecord::Schema.define(version: 2020_01_14_113700) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -502,6 +502,7 @@ ActiveRecord::Schema.define(version: 2019_12_18_103727) do t.boolean "ods_export_queued" t.datetime "closed_at" t.datetime "unpublished_at" + t.bigint "canonical_procedure_id" t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state" t.index ["hidden_at"], name: "index_procedures_on_hidden_at" t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id" From 517ab2578312e9b77c8656f6a223bb2f3ac2fbcc Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 4 Dec 2019 16:58:36 +0100 Subject: [PATCH 08/22] Register canonical procedure when demarche republished under existing path --- app/models/procedure.rb | 10 ++++--- spec/models/procedure_spec.rb | 49 +++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 8697e3d5d..a2f6126fe 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -16,6 +16,7 @@ class Procedure < ApplicationRecord has_one :attestation_template, dependent: :destroy belongs_to :parent_procedure, class_name: 'Procedure' + belongs_to :canonical_procedure, class_name: 'Procedure' belongs_to :service has_many :administrateurs_procedures @@ -129,9 +130,10 @@ class Procedure < ApplicationRecord other_procedure = other_procedure_with_path(path) if other_procedure.present? && administrateur.owns?(other_procedure) other_procedure.unpublish! + publish!(other_procedure.canonical_procedure || other_procedure) + else + publish! end - - publish! end end @@ -615,8 +617,8 @@ class Procedure < ApplicationRecord update!(closed_at: nil, unpublished_at: nil) end - def after_publish - update!(published_at: Time.zone.now) + def after_publish(canonical_procedure = nil) + update!(published_at: Time.zone.now, canonical_procedure: canonical_procedure) end def after_close diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 099dd91a6..6fdc5a0d0 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -536,38 +536,77 @@ describe Procedure do after { Timecop.return } - context "without parent procedure" do + context "without canonical procedure" do before do Timecop.freeze(now) procedure.publish! end it do + expect(procedure.canonical_procedure).to be_nil expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) expect(Procedure.find_by(path: "example-path")).to eq(procedure) expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs) end end + + context "with canonical procedure" do + let(:canonical_procedure) { create(:procedure, :published) } + + before do + Timecop.freeze(now) + procedure.publish!(canonical_procedure) + end + + it do + expect(procedure.canonical_procedure).to eq(canonical_procedure) + expect(procedure.closed_at).to be_nil + expect(procedure.published_at).to eq(now) + end + end end describe "#publish_or_reopen!" do - let(:published_procedure) { create(:procedure, :published) } - let(:administrateur) { published_procedure.administrateurs.first } + let(:canonical_procedure) { create(:procedure, :published) } + let(:administrateur) { canonical_procedure.administrateurs.first } let(:procedure) { create(:procedure, administrateurs: [administrateur]) } let(:now) { Time.zone.now.beginning_of_minute } - context "without parent procedure" do + context "without canonical procedure" do before do Timecop.freeze(now) - procedure.path = published_procedure.path + procedure.path = canonical_procedure.path procedure.publish_or_reopen!(administrateur) + canonical_procedure.reload end it do + expect(procedure.canonical_procedure).to eq(canonical_procedure) expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) + expect(canonical_procedure.unpublished_at).to eq(now) + end + end + + context "with canonical procedure" do + let(:canonical_procedure) { create(:procedure, :closed) } + let(:parent_procedure) { create(:procedure, :published, administrateurs: [administrateur]) } + + before do + parent_procedure.update!(path: canonical_procedure.path, canonical_procedure: canonical_procedure) + Timecop.freeze(now) + procedure.path = canonical_procedure.path + procedure.publish_or_reopen!(administrateur) + parent_procedure.reload + end + + it do + expect(procedure.canonical_procedure).to eq(canonical_procedure) + expect(procedure.closed_at).to be_nil + expect(procedure.published_at).to eq(now) + expect(parent_procedure.unpublished_at).to eq(now) end end end From 67dea1ee50ef346abb93ee844b5b4c68424d1427 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 7 Jan 2020 17:44:45 +0100 Subject: [PATCH 09/22] Update spec/models/procedure_spec.rb Co-Authored-By: Pierre de La Morinerie --- spec/models/procedure_spec.rb | 69 +++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 6fdc5a0d0..36d654b99 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -534,16 +534,18 @@ describe Procedure do let(:procedure) { create(:procedure, path: 'example-path') } let(:now) { Time.zone.now.beginning_of_minute } - after { Timecop.return } - - context "without canonical procedure" do + context 'when publishing a new procedure' do before do - Timecop.freeze(now) - procedure.publish! + Timecop.freeze(now) do + procedure.publish! + end end - it do + it 'no reference to the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to be_nil + end + + it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) expect(Procedure.find_by(path: "example-path")).to eq(procedure) @@ -551,16 +553,20 @@ describe Procedure do end end - context "with canonical procedure" do + context 'when publishing over a previous canonical procedure' do let(:canonical_procedure) { create(:procedure, :published) } before do - Timecop.freeze(now) - procedure.publish!(canonical_procedure) + Timecop.freeze(now) do + procedure.publish!(canonical_procedure) + end end - it do + it 'references the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to eq(canonical_procedure) + end + + it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) end @@ -574,38 +580,53 @@ describe Procedure do let(:procedure) { create(:procedure, administrateurs: [administrateur]) } let(:now) { Time.zone.now.beginning_of_minute } - context "without canonical procedure" do + context 'when publishing over a previous canonical procedure' do before do - Timecop.freeze(now) procedure.path = canonical_procedure.path - procedure.publish_or_reopen!(administrateur) + Timecop.freeze(now) do + procedure.publish_or_reopen!(administrateur) + end canonical_procedure.reload end - it do + it 'references the canonical procedure on the published procedure' do expect(procedure.canonical_procedure).to eq(canonical_procedure) + end + + it 'changes the procedure state to published' do expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) + end + + it 'unpublishes the canonical procedure' do expect(canonical_procedure.unpublished_at).to eq(now) end end - context "with canonical procedure" do + context 'when publishing over a previous procedure with canonical procedure' do let(:canonical_procedure) { create(:procedure, :closed) } let(:parent_procedure) { create(:procedure, :published, administrateurs: [administrateur]) } before do parent_procedure.update!(path: canonical_procedure.path, canonical_procedure: canonical_procedure) - Timecop.freeze(now) procedure.path = canonical_procedure.path - procedure.publish_or_reopen!(administrateur) + Timecop.freeze(now) do + procedure.publish_or_reopen!(administrateur) + end parent_procedure.reload end - it do + it 'references the canonical procedure on the published procedure' do + expect(procedure.canonical_procedure).to eq(canonical_procedure) + end + + it 'changes the procedure state to published' do expect(procedure.canonical_procedure).to eq(canonical_procedure) expect(procedure.closed_at).to be_nil expect(procedure.published_at).to eq(now) + end + + it 'unpublishes parent procedure' do expect(parent_procedure.unpublished_at).to eq(now) end end @@ -616,10 +637,10 @@ describe Procedure do let(:now) { Time.zone.now.beginning_of_minute } before do - Timecop.freeze(now) - procedure.unpublish! + Timecop.freeze(now) do + procedure.unpublish! + end end - after { Timecop.return } it { expect(procedure.closed_at).to eq(nil) @@ -692,11 +713,11 @@ describe Procedure do let(:procedure) { create(:procedure, :published) } let(:now) { Time.zone.now.beginning_of_minute } before do - Timecop.freeze(now) - procedure.close! + Timecop.freeze(now) do + procedure.close! + end procedure.reload end - after { Timecop.return } it { expect(procedure.close?).to be_truthy } it { expect(procedure.closed_at).to eq(now) } From 65b4bcf3a15d57f2d3ac9dadec149e8c4ac051dc Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 27 Jan 2020 10:49:01 +0100 Subject: [PATCH 10/22] =?UTF-8?q?Permet=20de=20d=C3=A9poser=20un=20dossier?= =?UTF-8?q?=20lorsqu'un=20menu=20d=C3=A9roulant=20li=C3=A9=20obligatoire?= =?UTF-8?q?=20n'a=20pas=20de=20valeur=20(car=20la=20liste=20est=20l=C3=A9g?= =?UTF-8?q?itimement=20vide)=20dans=20le=20second=20champ.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/champs/linked_drop_down_list_champ.rb | 9 +++++++-- spec/models/champs/linked_drop_down_list_champ_spec.rb | 9 ++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/models/champs/linked_drop_down_list_champ.rb b/app/models/champs/linked_drop_down_list_champ.rb index 46011439c..7be6f01c9 100644 --- a/app/models/champs/linked_drop_down_list_champ.rb +++ b/app/models/champs/linked_drop_down_list_champ.rb @@ -45,8 +45,9 @@ class Champs::LinkedDropDownListChamp < Champ value.present? ? { primary: primary_value, secondary: secondary_value } : nil end - def mandatory_and_blank? - mandatory? && (primary_value.blank? || secondary_value.blank?) + def blank? + primary_value.blank? || + (has_secondary_options_for_primary? && secondary_value.blank?) end def search_terms @@ -58,4 +59,8 @@ class Champs::LinkedDropDownListChamp < Champ def pack_value(primary, secondary) self.value = JSON.generate([primary, secondary]) end + + def has_secondary_options_for_primary? + primary_value.present? && secondary_options[primary_value].any?(&:present?) + end end diff --git a/spec/models/champs/linked_drop_down_list_champ_spec.rb b/spec/models/champs/linked_drop_down_list_champ_spec.rb index 64ec6d092..204015030 100644 --- a/spec/models/champs/linked_drop_down_list_champ_spec.rb +++ b/spec/models/champs/linked_drop_down_list_champ_spec.rb @@ -91,7 +91,14 @@ describe Champs::LinkedDropDownListChamp do end context 'when there is a secondary value' do - before { subject.secondary_value = 'Primary' } + before { subject.secondary_value = 'Secondary' } + + it { is_expected.not_to be_mandatory_and_blank } + end + + context 'when there is nothing to select for the secondary value' do + let(:drop_down_list) { build(:drop_down_list, value: "--A--\nAbbott\nAbelard\n--B--\n--C--\nCynthia") } + before { subject.primary_value = 'B' } it { is_expected.not_to be_mandatory_and_blank } end From 696e39761fc1d1560c5a4c0949a03dbadd09b234 Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Tue, 28 Jan 2020 09:18:54 +0100 Subject: [PATCH 11/22] deal with secondary_options that can be nil --- app/models/champs/linked_drop_down_list_champ.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/champs/linked_drop_down_list_champ.rb b/app/models/champs/linked_drop_down_list_champ.rb index 7be6f01c9..53e02d880 100644 --- a/app/models/champs/linked_drop_down_list_champ.rb +++ b/app/models/champs/linked_drop_down_list_champ.rb @@ -61,6 +61,6 @@ class Champs::LinkedDropDownListChamp < Champ end def has_secondary_options_for_primary? - primary_value.present? && secondary_options[primary_value].any?(&:present?) + primary_value.present? && secondary_options[primary_value]&.any?(&:present?) end end From eb9388d59f118e1a5b54086b9a9ea5f355036831 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 28 Jan 2020 13:54:00 +0000 Subject: [PATCH 12/22] dossier: ensure the routing dropdown must be selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A blank routing dropdown nullify the groupe_instructeur – which also removes the link between the dossier and the procedure. Fix #4717 --- app/views/shared/dossiers/_edit.html.haml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index b0b353520..6523e05cc 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -30,11 +30,22 @@ %hr - if dossier.procedure.routee? - = f.label :groupe_instructeur_id, dossier.procedure.routing_criteria_name + = f.label :groupe_instructeur_id do + = dossier.procedure.routing_criteria_name + %span.mandatory * + -# The routing dropdown has 'include_blank: false', because otherwise a blank + -# value may nullify the groupe_instructeur – and thus the link between the dossier + -# and its procedure. + -# + -# If, one day, we need to make clearer to the user that they must actually choose an + -# option, THINK TWICE before adding a blank option, and what would happen if the form is + -# saved when the blank option is selected. + -# Instead please consider other possibilities; like using CSS to gray out the default option, + -# or adding some "(please select an option)" wording aside the label of the default group. + -# CSS = f.select :groupe_instructeur_id, dossier.procedure.groupe_instructeurs.order(:label).map { |gi| [gi.label, gi.id] }, - {}, - required: true + { include_blank: false } = f.fields_for :champs, dossier.champs do |champ_form| - champ = champ_form.object From 9da4fa9f4dd96235f5c3c51c4b915c48911e4af3 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 28 Jan 2020 14:58:24 +0100 Subject: [PATCH 13/22] dossier: ensure the dossier groupe_instructeur is always present Otherwise we loose the link to the dossier's procedure, which is definitely a bad thing. --- app/models/dossier.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index a7c237bbc..bc03925bc 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -216,6 +216,7 @@ class Dossier < ApplicationRecord validates :user, presence: true validates :individual, presence: true, if: -> { procedure.for_individual? } + validates :groupe_instructeur, presence: true def update_search_terms self.search_terms = [ From eec38bad73d7094ca0bf9e5c7d60d50a688a6c5e Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Jan 2020 12:42:30 +0100 Subject: [PATCH 14/22] =?UTF-8?q?Ajouter=20les=20informations=20de=20l?= =?UTF-8?q?=E2=80=99instructeur=20et=20de=20l=E2=80=99expert=20dans=20l?= =?UTF-8?q?=E2=80=99export=20des=20avis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #4667 --- app/models/avis.rb | 4 +++- spec/services/procedure_export_service_spec.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/avis.rb b/app/models/avis.rb index 2c1051a65..26b9bbf5a 100644 --- a/app/models/avis.rb +++ b/app/models/avis.rb @@ -43,7 +43,9 @@ class Avis < ApplicationRecord ['Question / Introduction', :introduction], ['Réponse', :answer], ['Créé le', :created_at], - ['Répondu le', :updated_at] + ['Répondu le', :updated_at], + ['Instructeur', claimant&.email], + ['Expert', instructeur&.email] ] end diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 7d24508c8..a3ba1a43d 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -295,7 +295,9 @@ describe ProcedureExportService do "Question / Introduction", "Réponse", "Créé le", - "Répondu le" + "Répondu le", + "Instructeur", + "Expert" ]) end From 745b00366f7e3289ba0e48fe5479f47d4e2bb05f Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 28 Jan 2020 11:28:12 +0100 Subject: [PATCH 15/22] Revert "app: hide IE11 deprecation banner during the strike" This reverts commit c2882b6cc356ecfb74a86072916539c2a187acb8. --- config/initializers/browser.rb | 1 - spec/features/outdated_browser_spec.rb | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/config/initializers/browser.rb b/config/initializers/browser.rb index 798e342ed..f9d22d40b 100644 --- a/config/initializers/browser.rb +++ b/config/initializers/browser.rb @@ -2,7 +2,6 @@ Browser.modern_rules.clear Browser.modern_rules << -> b { b.chrome? && b.version.to_i >= 50 && !b.platform.ios? } Browser.modern_rules << -> b { b.edge? && b.version.to_i >= 14 && !b.compatibility_view? } -Browser.modern_rules << -> b { b.ie? && b.version.to_i >= 11 && !b.compatibility_view? } Browser.modern_rules << -> b { b.firefox? && b.version.to_i >= 50 && !b.platform.ios? } Browser.modern_rules << -> b { b.opera? && b.version.to_i >= 40 } Browser.modern_rules << -> b { b.safari? && b.version.to_i >= 8 } diff --git a/spec/features/outdated_browser_spec.rb b/spec/features/outdated_browser_spec.rb index 57fa4815a..e6cb28efb 100644 --- a/spec/features/outdated_browser_spec.rb +++ b/spec/features/outdated_browser_spec.rb @@ -3,29 +3,29 @@ require 'spec_helper' feature 'Outdated browsers support:' do context 'when the user browser is outdated' do before(:each) do - ie_10_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)' - Capybara.page.driver.header('user-agent', ie_10_user_agent) + ie_11_user_agent = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko' + Capybara.page.driver.header('user-agent', ie_11_user_agent) end scenario 'a banner is displayed' do visit new_user_session_path - expect(page).to have_content('Internet Explorer 10 est trop ancien') + expect(page).to have_content('Internet Explorer 11 est trop ancien') end scenario 'the banner can be dismissed' do visit new_user_session_path - expect(page).to have_content('Internet Explorer 10 est trop ancien') + expect(page).to have_content('Internet Explorer 11 est trop ancien') # The banner is hidden immediately within '#outdated-browser-banner' do click_on 'Ignorer' end - expect(page).not_to have_content('Internet Explorer 10 est trop ancien') + expect(page).not_to have_content('Internet Explorer 11 est trop ancien') expect(page).to have_current_path(new_user_session_path) # The banner is hidden after a refresh page.refresh - expect(page).not_to have_content('Internet Explorer 10 est trop ancien') + expect(page).not_to have_content('Internet Explorer 11 est trop ancien') end end end From 6eaf0f780495b39c4255868d57c6b2affd130efb Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 8 Jan 2020 14:53:07 +0000 Subject: [PATCH 16/22] dossiers: fix the sticky bar on IE 11 This allows IE 11 users to see the draft autosave indicator. The layout on IE 11 is still buggy though - but it's better than nothing. --- .../stylesheets/new_design/dossier_edit.scss | 39 ++++++++++----- app/views/shared/dossiers/_edit.html.haml | 47 ++++++++++--------- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/app/assets/stylesheets/new_design/dossier_edit.scss b/app/assets/stylesheets/new_design/dossier_edit.scss index 05096311e..4e5c4ca75 100644 --- a/app/assets/stylesheets/new_design/dossier_edit.scss +++ b/app/assets/stylesheets/new_design/dossier_edit.scss @@ -1,6 +1,8 @@ @import "colors"; @import "constants"; +$dossier-actions-bar-border-width: 1px; + .dossier-header { .container { padding-bottom: $default-padding; @@ -46,29 +48,40 @@ border-radius: 4px; } - .send-dossier-actions-bar { - // scss-lint:disable VendorPrefix + .dossier-edit-sticky-footer { + // scss-lint:disable VendorPrefix DuplicateProperty + position: fixed; // Fallback for IE 11, and other browser that don't support sticky position: -webkit-sticky; // This is needed on Safari (tested on 12.1) - // scss-lint:enable VendorPrefix position: sticky; + // scss-lint:enable VendorPrefix DuplicateProperty + + // IE 11 uses `position:fixed` – and thus needs an explicit width, content-box for better layout, etc. + width: 100%; + max-width: $page-width + 2 * $default-padding; + box-sizing: content-box; + bottom: 0; - display: flex; - flex-direction: row; - align-items: center; margin-top: $default-padding; margin-left: -$default-padding; margin-right: -$default-padding; margin-bottom: 0; - padding-top: 0; - padding-bottom: $default-spacer; - padding-right: $default-padding; - padding-left: $default-padding; + + padding-right: $default-padding - $dossier-actions-bar-border-width; + padding-left: $default-padding - $dossier-actions-bar-border-width; + background: #FFFFFF; - border: 1px solid #CCCCCC; + + border: $dossier-actions-bar-border-width solid #CCCCCC; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom: none; + } + + .send-dossier-actions-bar { + display: flex; + flex-direction: row; + align-items: center; .button:not(:small) { min-height: 38px; @@ -81,13 +94,13 @@ } // Normal layout - @media (min-width: 500px) { + @media (min-width: 620px) { padding-top: $default-spacer * 2; padding-bottom: $default-spacer * 2; } // Compact layout - @media (max-width: 500px) { + @media (max-width: 620px) { padding-top: $default-spacer; padding-bottom: $default-spacer; } diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index 6523e05cc..bda7f5c05 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -53,31 +53,32 @@ locals: { champ: champ, form: champ_form } - if !apercu - .send-dossier-actions-bar - - if dossier.brouillon? - - if autosave_available?(dossier) - = render partial: 'users/dossiers/autosave' + .dossier-edit-sticky-footer + .send-dossier-actions-bar + - if dossier.brouillon? + - if autosave_available?(dossier) + = render partial: 'users/dossiers/autosave' + - else + = f.button 'Enregistrer le brouillon', + formnovalidate: true, + class: 'button send secondary', + data: { 'disable-with': "Envoi en cours…" } + + - if dossier.can_transition_to_en_construction? + = f.button 'Déposer le dossier', + name: :submit_draft, + value: true, + class: 'button send primary', + disabled: !current_user.owns?(dossier), + data: { 'disable-with': "Envoi en cours…" } + - else - = f.button 'Enregistrer le brouillon', - formnovalidate: true, - class: 'button send secondary', - data: { 'disable-with': "Envoi en cours…" } - - - if dossier.can_transition_to_en_construction? - = f.button 'Déposer le dossier', - name: :submit_draft, - value: true, + = f.button 'Enregistrer les modifications du dossier', class: 'button send primary', - disabled: !current_user.owns?(dossier), data: { 'disable-with': "Envoi en cours…" } - - else - = f.button 'Enregistrer les modifications du dossier', - class: 'button send primary', - data: { 'disable-with': "Envoi en cours…" } + - if dossier.brouillon? && !current_user.owns?(dossier) + .send-notice.invite-cannot-submit + En tant qu’invité, vous pouvez remplir ce formulaire – mais le titulaire du dossier doit le déposer lui-même. - - if dossier.brouillon? && !current_user.owns?(dossier) - .send-notice.invite-cannot-submit - En tant qu’invité, vous pouvez remplir ce formulaire – mais le titulaire du dossier doit le déposer lui-même. - - = render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier } + = render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier } From f7128e81dacb2c09cff691cf3398ed60c46212a7 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 28 Jan 2020 15:38:32 +0100 Subject: [PATCH 17/22] workflows: update auto-rebase bot Update to latest version of the auto-rebase bot --- .github/workflows/rebase.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index c23336d4d..9025bf482 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -1,7 +1,7 @@ -on: +on: issue_comment: types: [created] -name: Rebase automatique +name: Automatic Rebase jobs: rebase: name: Rebase @@ -9,13 +9,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master + with: + fetch-depth: 0 - name: Automatic Rebase - uses: cirrus-actions/rebase@master + uses: cirrus-actions/rebase@1.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://github.community/t5/GitHub-Actions/Workflow-is-failing-if-no-job-can-be-ran-due-to-condition/m-p/38186#M3250 always_job: - name: Aways run job + name: Always run job runs-on: ubuntu-latest steps: - name: Always run From 7478a51846ec268bd41f7373bb30bae5e6f6b848 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 5 Dec 2019 08:48:51 +0100 Subject: [PATCH 18/22] [GraphQL] use official skylight support --- Gemfile.lock | 6 +++--- app/graphql/api/v2/schema.rb | 1 - config/application.rb | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 21057c302..c7152284f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -614,9 +614,9 @@ GEM rack (~> 2.0) rack-protection (= 2.0.5) tilt (~> 2.0) - skylight (3.1.2) - skylight-core (= 3.1.2) - skylight-core (3.1.2) + skylight (4.2.1) + skylight-core (= 4.2.1) + skylight-core (4.2.1) activesupport (>= 4.2.0) smart_listing (1.2.2) coffee-rails diff --git a/app/graphql/api/v2/schema.rb b/app/graphql/api/v2/schema.rb index 26089ef11..70f937b08 100644 --- a/app/graphql/api/v2/schema.rb +++ b/app/graphql/api/v2/schema.rb @@ -73,5 +73,4 @@ class Api::V2::Schema < GraphQL::Schema end use GraphQL::Batch - use GraphQL::Tracing::SkylightTracing end diff --git a/config/application.rb b/config/application.rb index 65080acaf..be10d163e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -49,5 +49,7 @@ module TPS debounce_delay: 3000, status_visible_duration: 6000 } + + config.skylight.probes += [:graphql] end end From 0a928b2d6bc924c6cd53768be563f036099e93f4 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 5 Dec 2019 09:33:48 +0100 Subject: [PATCH 19/22] [GraphQL] use Execution::Interpreter GraphQL-Ruby 1.9.0 includes a new runtime module which you may use for your schema. Eventually, it will become the default. --- Gemfile.lock | 2 +- app/graphql/api/v2/schema.rb | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c7152284f..e72a25cae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -242,7 +242,7 @@ GEM graphiql-rails (1.7.0) railties sprockets-rails - graphql (1.9.15) + graphql (1.9.16) graphql-batch (0.4.1) graphql (>= 1.3, < 2) promise.rb (~> 0.7.2) diff --git a/app/graphql/api/v2/schema.rb b/app/graphql/api/v2/schema.rb index 70f937b08..10b1bfa13 100644 --- a/app/graphql/api/v2/schema.rb +++ b/app/graphql/api/v2/schema.rb @@ -59,18 +59,29 @@ class Api::V2::Schema < GraphQL::Schema raise GraphQL::ExecutionError.new("An object of type #{error.type.graphql_name} was hidden due to permissions", extensions: { code: :unauthorized }) end - middleware(GraphQL::Schema::TimeoutMiddleware.new(max_seconds: 5) do |_, query| - Rails.logger.info("GraphQL Timeout: #{query.query_string}") - end) + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST + use GraphQL::Schema::Timeout, max_seconds: 5 + use GraphQL::Batch + use GraphQL::Backtrace if Rails.env.development? - query_analyzer(GraphQL::Analysis::QueryComplexity.new do |_, complexity| - Rails.logger.info("[GraphQL Query Complexity] #{complexity}") - end) - query_analyzer(GraphQL::Analysis::QueryDepth.new do |_, depth| - Rails.logger.info("[GraphQL Query Depth] #{depth}") - end) - end + class LogQueryDepth < GraphQL::Analysis::AST::QueryDepth + def result + Rails.logger.info("[GraphQL Query Depth] #{super}") + end + end - use GraphQL::Batch + class LogQueryComplexity < GraphQL::Analysis::AST::QueryComplexity + def result + Rails.logger.info("[GraphQL Query Complexity] #{super}") + end + end + + query_analyzer(LogQueryComplexity) + query_analyzer(LogQueryDepth) + else + query_analyzer(GraphQL::Analysis::AST::MaxQueryComplexity) + query_analyzer(GraphQL::Analysis::AST::MaxQueryDepth) + end end From f7c59cffc06d60010dc816fed074fb5bd32d2f94 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 9 Jan 2020 10:29:29 +0100 Subject: [PATCH 20/22] Fix tests for skylight --- spec/controllers/application_controller_spec.rb | 7 +++++++ spec/controllers/manager/application_controller_spec.rb | 3 +++ 2 files changed, 10 insertions(+) diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index dffb9d849..990c55961 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -21,6 +21,7 @@ describe ApplicationController, type: :controller do let(:payload) { {} } before do + allow(@controller).to receive(:content_type).and_return('') allow(@controller).to receive(:current_user).and_return(current_user) expect(@controller).to receive(:current_instructeur).and_return(current_instructeur) expect(@controller).to receive(:current_administrateur).and_return(current_administrateur) @@ -42,6 +43,8 @@ describe ApplicationController, type: :controller do payload.delete(key) end expect(payload).to eq({ + sk_rendered_format: nil, + sk_variant: [], user_agent: 'Rails Testing', user_roles: 'Guest' }) @@ -61,6 +64,8 @@ describe ApplicationController, type: :controller do payload.delete(key) end expect(payload).to eq({ + sk_rendered_format: nil, + sk_variant: [], user_agent: 'Rails Testing', user_id: current_user.id, user_email: current_user.email, @@ -85,6 +90,8 @@ describe ApplicationController, type: :controller do payload.delete(key) end expect(payload).to eq({ + sk_rendered_format: nil, + sk_variant: [], user_agent: 'Rails Testing', user_id: current_user.id, user_email: current_user.email, diff --git a/spec/controllers/manager/application_controller_spec.rb b/spec/controllers/manager/application_controller_spec.rb index 3af6744c7..45d8d995f 100644 --- a/spec/controllers/manager/application_controller_spec.rb +++ b/spec/controllers/manager/application_controller_spec.rb @@ -4,6 +4,7 @@ describe Manager::ApplicationController, type: :controller do let(:payload) { {} } before do + allow(@controller).to receive(:content_type).and_return('') allow(@controller).to receive(:current_user).and_return(current_user) @controller.send(:append_info_to_payload, payload) end @@ -13,6 +14,8 @@ describe Manager::ApplicationController, type: :controller do payload.delete(key) end expect(payload).to eq({ + sk_rendered_format: nil, + sk_variant: [], user_agent: 'Rails Testing', user_id: current_user.id, user_email: current_user.email From 4eb7d854a9343831fd8b45aa9a3c5352bd3f827c Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 28 Jan 2020 15:48:02 +0100 Subject: [PATCH 21/22] workflows: fix trailing whitespace --- .github/workflows/rebase.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index 9025bf482..229855fc6 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -1,4 +1,4 @@ -on: +on: issue_comment: types: [created] name: Automatic Rebase From 5f65665b07ad530cb3408959b37521cd8032fe1a Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 13 Jan 2020 14:35:25 +0100 Subject: [PATCH 22/22] added a method for modifying a user email --- app/controllers/manager/users_controller.rb | 13 ++++++++ app/dashboards/user_dashboard.rb | 4 ++- app/views/manager/users/show.html.erb | 4 ++- config/routes.rb | 2 +- .../manager/users_controller_spec.rb | 30 +++++++++++++++++++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/app/controllers/manager/users_controller.rb b/app/controllers/manager/users_controller.rb index 9e1a9fc54..9bdbb0449 100644 --- a/app/controllers/manager/users_controller.rb +++ b/app/controllers/manager/users_controller.rb @@ -1,5 +1,18 @@ module Manager class UsersController < Manager::ApplicationController + def update + user = User.find(params[:id]) + new_email = params[:user][:email] + user.skip_reconfirmation! + user.update(email: new_email) + if (user.valid?) + flash[:notice] = "L'email a été modifié en « #{new_email} » sans notification ni validation par email." + else + flash[:error] = "« #{new_email} » n'est pas une adresse valide." + end + redirect_to edit_manager_user_path(user) + end + def resend_confirmation_instructions user = User.find(params[:id]) user.resend_confirmation_instructions diff --git a/app/dashboards/user_dashboard.rb b/app/dashboards/user_dashboard.rb index 2ad8886c8..2a3762f86 100644 --- a/app/dashboards/user_dashboard.rb +++ b/app/dashboards/user_dashboard.rb @@ -41,7 +41,9 @@ class UserDashboard < Administrate::BaseDashboard # FORM_ATTRIBUTES # an array of attributes that will be displayed # on the model's form (`new` and `edit`) pages. - FORM_ATTRIBUTES = [].freeze + FORM_ATTRIBUTES = [ + :email + ].freeze # Overwrite this method to customize how users are displayed # across all pages of the admin dashboard. diff --git a/app/views/manager/users/show.html.erb b/app/views/manager/users/show.html.erb index ab51789a6..bf49d4076 100644 --- a/app/views/manager/users/show.html.erb +++ b/app/views/manager/users/show.html.erb @@ -24,9 +24,11 @@ as well as a link to its edit page. <%= content_for(:title) %> +
+ <%= button_to "modifier", edit_manager_user_path(page.resource), method: :get, class: "button" %> +
<%= button_to "supprimer", delete_manager_user_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'utilisateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet utilisateur a des dossiers dont l'instruction a commencé et ne peut être supprimé" %> -
<% if !user.confirmed? %> diff --git a/config/routes.rb b/config/routes.rb index 09f0d8fb8..7feeaadb4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,7 +24,7 @@ Rails.application.routes.draw do delete 'delete', on: :member end - resources :users, only: [:index, :show] do + resources :users, only: [:index, :show, :edit, :update] do delete 'delete', on: :member post 'resend_confirmation_instructions', on: :member put 'enable_feature', on: :member diff --git a/spec/controllers/manager/users_controller_spec.rb b/spec/controllers/manager/users_controller_spec.rb index 9a703a82d..4aacf54a1 100644 --- a/spec/controllers/manager/users_controller_spec.rb +++ b/spec/controllers/manager/users_controller_spec.rb @@ -1,6 +1,36 @@ describe Manager::UsersController, type: :controller do let(:administration) { create(:administration) } + describe '#update' do + let!(:user) { create(:user, email: 'ancien.email@domaine.fr') } + + before { + sign_in administration + } + subject { patch :update, params: { id: user.id, user: { email: nouvel_email } } } + + describe 'with a valid email' do + let(:nouvel_email) { 'nouvel.email@domaine.fr' } + + it 'updates the user email' do + subject + + expect(User.find_by(id: user.id).email).to eq(nouvel_email) + end + end + + describe 'with an invalid email' do + let(:nouvel_email) { 'plop' } + + it 'does not update the user email' do + subject + + expect(User.find_by(id: user.id).email).not_to eq(nouvel_email) + expect(flash[:error]).to match("« #{nouvel_email} » n'est pas une adresse valide.") + end + end + end + describe '#delete' do let!(:user) { create(:user) }