diff --git a/app/models/procedure.rb b/app/models/procedure.rb index aacf4783e..e85548ce1 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -234,7 +234,10 @@ class Procedure < ApplicationRecord validates :description, presence: true, allow_blank: false, allow_nil: false validates :administrateurs, presence: true validates :lien_site_web, presence: true, if: :publiee? - validates :draft_revision, 'revisions/no_empty_repetition': true, if: :validate_for_publication? + validates :draft_revision, + 'revisions/no_empty_repetition': true, + 'revisions/no_empty_drop_down': true, + if: :validate_for_publication? validate :check_juridique validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false } validates :duree_conservation_dossiers_dans_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION } diff --git a/app/validators/revisions/no_empty_drop_down_validator.rb b/app/validators/revisions/no_empty_drop_down_validator.rb new file mode 100644 index 000000000..86e257e34 --- /dev/null +++ b/app/validators/revisions/no_empty_drop_down_validator.rb @@ -0,0 +1,22 @@ +class Revisions::NoEmptyDropDownValidator < ActiveModel::EachValidator + def validate_each(procedure, attribute, revision) + return if revision.nil? + + tdcs = revision.types_de_champ + revision.types_de_champ_private + drop_downs = tdcs.filter(&:drop_down_list?) + drop_downs.each do |drop_down| + validate_drop_down_not_empty(procedure, attribute, drop_down) + end + end + + private + + def validate_drop_down_not_empty(procedure, attribute, drop_down) + if drop_down.drop_down_list_enabled_non_empty_options.empty? + procedure.errors.add( + attribute, + procedure.errors.generate_message(attribute, :empty_drop_down, { value: drop_down.libelle }) + ) + end + end +end diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a777d405a..b44788763 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -311,6 +311,7 @@ fr: forbidden_html: "Seul-e-s les usagers peuvent se connecter via France Connect. En tant qu’instructeur ou administrateur, nous vous invitons à réininitialiser votre mot de passe." procedure_archived: "Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier." empty_repetition: 'Le bloc répétable « %{value} » doit comporter au moins un champ' + empty_drop_down: 'La liste de choix « %{value} » doit comporter au moins un choix sélectionnable' # procedure_not_draft: "Cette démarche n’est maintenant plus en brouillon." cadastres_empty: one: "Aucune parcelle cadastrale sur la zone sélectionnée" diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index 9f5223e86..ea9ea2a8c 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -89,6 +89,9 @@ FactoryBot.define do trait :long do drop_down_list_value { "alpha\r\nbravo\r\n--separateur--\r\ncharly\r\ndelta\r\necho\r\nfox-trot\r\ngolf" } end + trait :without_selectable_values do + drop_down_list_value { "\r\n--separateur--\r\n--separateur 2--\r\n \r\n" } + end end factory :type_de_champ_multiple_drop_down_list do type_champ { TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) } diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 2ff4a2ff9..146c98926 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -282,13 +282,17 @@ describe Procedure do describe 'draft_revision' do let(:repetition) { build(:type_de_champ_repetition, libelle: 'Enfants') } let(:text_field) { build(:type_de_champ_text) } - let(:procedure) { create(:procedure, types_de_champ: [repetition]) } let(:invalid_repetition_error_message) { 'Le bloc répétable « Enfants » doit comporter au moins un champ' } + let(:drop_down) { build(:type_de_champ_drop_down_list, :without_selectable_values, libelle: 'Civilité') } + let(:invalid_drop_down_error_message) { 'La liste de choix « Civilité » doit comporter au moins un choix sélectionnable' } + + let(:procedure) { create(:procedure, types_de_champ: [repetition, drop_down]) } + context 'on a draft procedure' do - it 'doesn’t validate repetitions' do + it 'doesn’t validate the draft revision' do procedure.validate - expect(procedure.errors[:draft_revision]).not_to include(invalid_repetition_error_message) + expect(procedure.errors[:draft_revision]).not_to be_present end end @@ -306,6 +310,15 @@ describe Procedure do procedure.validate expect(procedure.errors.full_messages_for(:draft_revision)).not_to include(invalid_repetition_error_message) end + + it 'validates that no drop-down type de champ is empty' do + procedure.validate + expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_drop_down_error_message) + + drop_down.update!(drop_down_list_value: "--title--\r\nsome value") + procedure.reload.validate + expect(procedure.errors.full_messages_for(:draft_revision)).not_to include(invalid_drop_down_error_message) + end end context 'when validating for publication' do @@ -313,6 +326,11 @@ describe Procedure do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_repetition_error_message) end + + it 'validates that no drop-down type de champ is empty' do + procedure.validate(:publication) + expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_drop_down_error_message) + end end end end