Merge pull request #6840 from betagouv/fix-champs-validation-for-private-champs

Administrateur : sépare la validation des champs publics et des annotations privées
This commit is contained in:
Pierre de La Morinerie 2022-02-01 12:41:23 +01:00 committed by GitHub
commit 70cbdcacc9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 38 deletions

View file

@ -245,9 +245,13 @@ class Procedure < ApplicationRecord
validates :description, presence: true, allow_blank: false, allow_nil: false validates :description, presence: true, allow_blank: false, allow_nil: false
validates :administrateurs, presence: true validates :administrateurs, presence: true
validates :lien_site_web, presence: true, if: :publiee? validates :lien_site_web, presence: true, if: :publiee?
validates :draft_revision, validates :draft_types_de_champ,
'revisions/no_empty_repetition': true, 'types_de_champ/no_empty_repetition': true,
'revisions/no_empty_drop_down': true, 'types_de_champ/no_empty_drop_down': true,
if: :validate_for_publication?
validates :draft_types_de_champ_private,
'types_de_champ/no_empty_repetition': true,
'types_de_champ/no_empty_drop_down': true,
if: :validate_for_publication? if: :validate_for_publication?
validate :check_juridique 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 :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }

View file

@ -1,10 +1,6 @@
class Revisions::NoEmptyDropDownValidator < ActiveModel::EachValidator class TypesDeChamp::NoEmptyDropDownValidator < ActiveModel::EachValidator
def validate_each(procedure, attribute, revision) def validate_each(procedure, attribute, types_de_champ)
return if revision.nil? types_de_champ.filter(&:drop_down_list?).each do |drop_down|
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) validate_drop_down_not_empty(procedure, attribute, drop_down)
end end
end end

View file

@ -1,10 +1,6 @@
class Revisions::NoEmptyRepetitionValidator < ActiveModel::EachValidator class TypesDeChamp::NoEmptyRepetitionValidator < ActiveModel::EachValidator
def validate_each(procedure, attribute, revision) def validate_each(procedure, attribute, types_de_champ)
return if revision.nil? types_de_champ.filter(&:repetition?).each do |repetition|
revision_tdcs = revision.types_de_champ + revision.types_de_champ_private
repetitions = revision_tdcs.filter(&:repetition?)
repetitions.each do |repetition|
validate_repetition_not_empty(procedure, attribute, repetition) validate_repetition_not_empty(procedure, attribute, repetition)
end end
end end

View file

@ -53,7 +53,7 @@
- if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions) - if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions)
- @procedure.validate(:publication) - @procedure.validate(:publication)
- error_messages = @procedure.errors.messages_for(:draft_revision).to_sentence - error_messages = @procedure.errors.messages_for(:draft_types_de_champ).to_sentence
= link_to champs_admin_procedure_path(@procedure), class: 'card-admin', title: error_messages do = link_to champs_admin_procedure_path(@procedure), class: 'card-admin', title: error_messages do
- if @procedure.draft_types_de_champ.count == 0 - if @procedure.draft_types_de_champ.count == 0
@ -171,11 +171,18 @@
%p.button Modifier %p.button Modifier
- if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions) - if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions)
= link_to annotations_admin_procedure_path(@procedure), class: 'card-admin' do - @procedure.validate(:publication)
- error_messages = @procedure.errors.messages_for(:draft_types_de_champ_private).to_sentence
= link_to annotations_admin_procedure_path(@procedure), class: 'card-admin', title: error_messages do
- if @procedure.draft_types_de_champ_private.present? - if @procedure.draft_types_de_champ_private.present?
%div %div
%span.icon.accept %span.icon.accept
%p.card-admin-status-accept Validé %p.card-admin-status-accept Validé
- elsif error_messages.present?
%div
%span.icon.refuse
%p.card-admin-status-error À modifier
- else - else
%div %div
%span.icon.clock %span.icon.clock

View file

@ -328,8 +328,8 @@ fr:
connexion: "Erreur lors de la connexion à France Connect." connexion: "Erreur lors de la connexion à France Connect."
forbidden_html: "Seul-e-s les usagers peuvent se connecter via France Connect. En tant quinstructeur ou administrateur, nous vous invitons à <a href='%{reset_link}'>réininitialiser votre mot de passe</a>." forbidden_html: "Seul-e-s les usagers peuvent se connecter via France Connect. En tant quinstructeur ou administrateur, nous vous invitons à <a href='%{reset_link}'>réininitialiser votre mot de passe</a>."
procedure_archived: "Cette démarche en ligne a été close, il nest plus possible de déposer de dossier." procedure_archived: "Cette démarche en ligne a été close, il nest plus possible de déposer de dossier."
empty_repetition: 'Le bloc répétable « %{value} » doit comporter au moins un champ' empty_repetition: '« %{value} » doit comporter au moins un champ répétable'
empty_drop_down: 'La liste de choix « %{value} » doit comporter au moins un choix sélectionnable' empty_drop_down: '« %{value} » doit comporter au moins un choix sélectionnable'
# procedure_not_draft: "Cette démarche nest maintenant plus en brouillon." # procedure_not_draft: "Cette démarche nest maintenant plus en brouillon."
cadastres_empty: cadastres_empty:
one: "Aucune parcelle cadastrale sur la zone sélectionnée" one: "Aucune parcelle cadastrale sur la zone sélectionnée"

View file

@ -23,5 +23,7 @@ fr:
attributes: attributes:
api_particulier_token: api_particulier_token:
invalid: 'na pas le bon format' invalid: 'na pas le bon format'
draft_revision: draft_types_de_champ:
format: '%{message}' format: 'Le champ %{message}'
draft_types_de_champ_private:
format: 'Lannotation privée %{message}'

View file

@ -299,20 +299,20 @@ describe Procedure do
it_behaves_like 'duree de conservation' it_behaves_like 'duree de conservation'
end end
describe 'draft_revision' do describe 'draft_types_de_champ validations' do
let(:repetition) { build(:type_de_champ_repetition, libelle: 'Enfants') } let(:repetition) { build(:type_de_champ_repetition, libelle: 'Enfants') }
let(:text_field) { build(:type_de_champ_text) } let(:text_field) { build(:type_de_champ_text) }
let(:invalid_repetition_error_message) { 'Le bloc répétable « Enfants » doit comporter au moins un champ' } let(:invalid_repetition_error_message) { 'Le champ « Enfants » doit comporter au moins un champ répétable' }
let(:drop_down) { build(:type_de_champ_drop_down_list, :without_selectable_values, libelle: 'Civilité') } 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(:invalid_drop_down_error_message) { 'Le champ « Civilité » doit comporter au moins un choix sélectionnable' }
let(:procedure) { create(:procedure, types_de_champ: [repetition, drop_down]) } let(:procedure) { create(:procedure, types_de_champ: [repetition, drop_down]) }
context 'on a draft procedure' do context 'on a draft procedure' do
it 'doesnt validate the draft revision' do it 'doesnt validate the types de champs' do
procedure.validate procedure.validate
expect(procedure.errors[:draft_revision]).not_to be_present expect(procedure.errors[:draft_types_de_champ]).not_to be_present
end end
end end
@ -321,35 +321,52 @@ describe Procedure do
it 'validates that no repetition type de champ is empty' do it 'validates that no repetition type de champ is empty' do
procedure.validate procedure.validate
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_repetition_error_message) expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_repetition_error_message)
text_field.revision = repetition.revision text_field.revision = repetition.revision
text_field.order_place = repetition.types_de_champ.size text_field.order_place = repetition.types_de_champ.size
procedure.draft_revision.types_de_champ.find(&:repetition?).types_de_champ << text_field procedure.draft_types_de_champ.find(&:repetition?).types_de_champ << text_field
procedure.validate procedure.validate
expect(procedure.errors.full_messages_for(:draft_revision)).not_to include(invalid_repetition_error_message) expect(procedure.errors.full_messages_for(:draft_types_de_champ)).not_to include(invalid_repetition_error_message)
end end
it 'validates that no drop-down type de champ is empty' do it 'validates that no drop-down type de champ is empty' do
procedure.validate procedure.validate
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_drop_down_error_message) expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_drop_down_error_message)
drop_down.update!(drop_down_list_value: "--title--\r\nsome value") drop_down.update!(drop_down_list_value: "--title--\r\nsome value")
procedure.reload.validate procedure.reload.validate
expect(procedure.errors.full_messages_for(:draft_revision)).not_to include(invalid_drop_down_error_message) expect(procedure.errors.full_messages_for(:draft_types_de_champ)).not_to include(invalid_drop_down_error_message)
end end
end end
context 'when validating for publication' do context 'when validating for publication' do
it 'validates that no repetition type de champ is empty' do it 'validates that no repetition type de champ is empty' do
procedure.validate(:publication) procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_repetition_error_message) expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_repetition_error_message)
end end
it 'validates that no drop-down type de champ is empty' do it 'validates that no drop-down type de champ is empty' do
procedure.validate(:publication) procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_drop_down_error_message) expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_drop_down_error_message)
end
end
context 'when the champ is private' do
let(:procedure) { create(:procedure, types_de_champ_private: [repetition, drop_down]) }
let(:invalid_repetition_error_message) { 'Lannotation privée « Enfants » doit comporter au moins un champ répétable' }
let(:invalid_drop_down_error_message) { 'Lannotation privée « Civilité » doit comporter au moins un choix sélectionnable' }
it 'validates that no repetition type de champ is empty' do
procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).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_types_de_champ_private)).to include(invalid_drop_down_error_message)
end end
end end
end end

View file

@ -47,19 +47,23 @@ describe 'Publishing a procedure', js: true do
end end
context 'when the procedure has invalid champs' do context 'when the procedure has invalid champs' do
let(:empty_repetition) { build(:type_de_champ_repetition, types_de_champ: []) } let(:empty_repetition) { build(:type_de_champ_repetition, types_de_champ: [], libelle: 'Enfants') }
let(:empty_drop_down) { build(:type_de_champ_drop_down_list, :without_selectable_values, libelle: 'Civilité') }
let!(:procedure) do let!(:procedure) do
create(:procedure, create(:procedure,
:with_path, :with_path,
:with_service, :with_service,
instructeurs: instructeurs, instructeurs: instructeurs,
administrateur: administrateur, administrateur: administrateur,
types_de_champ: [empty_repetition]) types_de_champ: [empty_repetition],
types_de_champ_private: [empty_drop_down])
end end
scenario 'an error message prevents the publication' do scenario 'an error message prevents the publication' do
expect(page).to have_content('Des problèmes empêchent la publication de la démarche') expect(page).to have_content('Des problèmes empêchent la publication de la démarche')
expect(page).to have_content("Le bloc répétable « #{empty_repetition.libelle} » doit comporter au moins un champ") expect(page).to have_content("Le champ « Enfants » doit comporter au moins un champ répétable")
expect(page).to have_content("Lannotation privée « Civilité » doit comporter au moins un choix sélectionnable")
expect(find_field('procedure_path').value).to eq procedure.path expect(find_field('procedure_path').value).to eq procedure.path
fill_in 'lien_site_web', with: 'http://some.website' fill_in 'lien_site_web', with: 'http://some.website'