From d2ccea700bf8264fc05f0697e0d0fd0b0ceefb82 Mon Sep 17 00:00:00 2001 From: mfo Date: Mon, 10 Jun 2024 09:35:29 +0200 Subject: [PATCH 1/2] bug(TypesDeChamp::ConditionValidator): should allow to use types_de_champ_public on condition for types_de_champ_private --- spec/models/procedure_spec.rb | 59 +++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 0589985bb..080612253 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -352,24 +352,12 @@ describe Procedure do end describe 'draft_types_de_champ validations' do - let(:repetition) { repetition = procedure.draft_revision.types_de_champ_public.find(&:repetition?) } - let(:text_field) { build(:type_de_champ_text) } - 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(:invalid_drop_down_error_message) { 'Le champ « Civilité » doit comporter au moins un choix sélectionnable' } - - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text }, { type: :integer_number }] }]) } - let(:draft) { procedure.draft_revision } - - before do - draft.revision_types_de_champ.create(type_de_champ: drop_down, position: 100) - - repetition.update(libelle: 'Enfants') - draft.children_of(repetition).destroy_all - end + let(:procedure) { create(:procedure, types_de_champ_public:, types_de_champ_private:) } context 'on a draft procedure' do + let(:types_de_champ_private) { [] } + let(:types_de_champ_public) { [{ type: :repetition, libelle: 'Enfants', children: [] }] } + it 'doesn’t validate the types de champs' do procedure.validate expect(procedure.errors[:draft_types_de_champ_public]).not_to be_present @@ -377,12 +365,22 @@ describe Procedure do end context 'when validating for publication' do + let(:types_de_champ_public) do + [ + { type: :repetition, libelle: 'Enfants', children: [] }, + { type: :drop_down_list, libelle: 'Civilité', options: [] } + ] + end + let(:types_de_champ_private) { [] } + let(:invalid_repetition_error_message) { 'Le champ « Enfants » doit comporter au moins un champ répétable' } + let(:invalid_drop_down_error_message) { 'Le champ « 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_public)).to include(invalid_repetition_error_message) new_draft = procedure.draft_revision - + repetition = procedure.draft_revision.types_de_champ_public.find(&:repetition?) parent_coordinate = new_draft.revision_types_de_champ.find_by(type_de_champ: repetition) new_draft.revision_types_de_champ.create(type_de_champ: create(:type_de_champ), position: 0, parent: parent_coordinate) @@ -394,6 +392,7 @@ describe Procedure do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).to include(invalid_drop_down_error_message) + drop_down = procedure.draft_revision.types_de_champ_public.find(&:drop_down_list?) drop_down.update!(drop_down_list_value: "--title--\r\nsome value") procedure.reload.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).not_to include(invalid_drop_down_error_message) @@ -401,10 +400,13 @@ describe Procedure do end context 'when the champ is private' do - before do - repetition.update(private: true) - drop_down.update(private: true) + let(:types_de_champ_private) do + [ + { type: :repetition, libelle: 'Enfants', children: [] }, + { type: :drop_down_list, libelle: 'Civilité', options: [] } + ] end + let(:types_de_champ_public) { [] } let(:invalid_repetition_error_message) { 'L’annotation privée « Enfants » doit comporter au moins un champ répétable' } let(:invalid_drop_down_error_message) { 'L’annotation privée « Civilité » doit comporter au moins un choix sélectionnable' } @@ -418,6 +420,23 @@ describe Procedure do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to include(invalid_drop_down_error_message) end + + it 'validates that types de champ private condition works types de champ public and private' do + end + end + + context 'when condition on champ private use public champ' do + include Logic + let(:types_de_champ_private) { [{ type: :text, condition: ds_eq(champ_value(1), constant(2)) }] } + let(:types_de_champ_public) { [{ type: :number, stable_id: 1 }] } + + it 'validate without context' do + expect(procedure.validate).to be_truthy + end + + it 'validate with types_de_champ_private_editor' do + expect(procedure.validate(:types_de_champ_private_editor)).to be_falsey + end end end From 27b86f9848be5fcb73c4d04b7c1e99ca97b6a79a Mon Sep 17 00:00:00 2001 From: mfo Date: Mon, 10 Jun 2024 09:57:34 +0200 Subject: [PATCH 2/2] fix(TypesDeChamp::ConditionValidator): allow to use types_de_champ_public on condition for types_de_champ_private --- .../types_de_champ/condition_validator.rb | 35 ++++++++++--------- spec/models/procedure_spec.rb | 35 +++++++++++++------ .../procedure_export_service_zip_spec.rb | 3 +- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/app/validators/types_de_champ/condition_validator.rb b/app/validators/types_de_champ/condition_validator.rb index 74b57c3d5..65e1e887a 100644 --- a/app/validators/types_de_champ/condition_validator.rb +++ b/app/validators/types_de_champ/condition_validator.rb @@ -1,21 +1,24 @@ class TypesDeChamp::ConditionValidator < ActiveModel::EachValidator def validate_each(procedure, attribute, types_de_champ) - public_tdcs = types_de_champ.to_a - .flat_map { _1.repetition? ? procedure.draft_revision.children_of(_1) : _1 } + return if types_de_champ.empty? - public_tdcs - .map.with_index - .filter_map { |tdc, i| tdc.condition? ? [tdc, i] : nil } - .map do |tdc, i| - [tdc, tdc.condition.errors(public_tdcs.take(i))] - end - .filter { |_tdc, errors| errors.present? } - .each do |tdc, _error_hash| - procedure.errors.add( - attribute, - procedure.errors.generate_message(attribute, :invalid_condition, { value: tdc.libelle }), - type_de_champ: tdc - ) - end + tdcs = if attribute == :draft_types_de_champ_private + procedure.draft_revision.types_de_champ_for + else + procedure.draft_revision.types_de_champ_for(scope: :public) + end + + tdcs.each_with_index do |tdc, i| + next unless tdc.condition? + + errors = tdc.condition.errors(tdcs.take(i)) + next if errors.blank? + + procedure.errors.add( + attribute, + procedure.errors.generate_message(attribute, :invalid_condition, { value: tdc.libelle }), + type_de_champ: tdc + ) + end end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 080612253..7c4c8c1e6 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -211,7 +211,7 @@ describe Procedure do it { is_expected.to allow_value('text').on(:publication).for(:cadre_juridique) } context 'with deliberation' do - let(:procedure) { build(:procedure, cadre_juridique: nil) } + let(:procedure) { build(:procedure, cadre_juridique: nil, revisions: [build(:procedure_revision)]) } it { expect(procedure.valid?(:publication)).to eq(false) } @@ -420,22 +420,37 @@ describe Procedure do procedure.validate(:publication) expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to include(invalid_drop_down_error_message) end - - it 'validates that types de champ private condition works types de champ public and private' do - end end context 'when condition on champ private use public champ' do include Logic - let(:types_de_champ_private) { [{ type: :text, condition: ds_eq(champ_value(1), constant(2)) }] } - let(:types_de_champ_public) { [{ type: :number, stable_id: 1 }] } - + let(:types_de_champ_public) { [{ type: :decimal_number, stable_id: 1 }] } + let(:types_de_champ_private) { [{ type: :text, condition: ds_eq(champ_value(1), constant(2)), stable_id: 2 }] } it 'validate without context' do - expect(procedure.validate).to be_truthy + procedure.validate + expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to be_empty end - it 'validate with types_de_champ_private_editor' do - expect(procedure.validate(:types_de_champ_private_editor)).to be_falsey + it 'validate allows condition' do + procedure.validate(:types_de_champ_private_editor) + expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to be_empty + end + end + + context 'when condition on champ public use private champ' do + include Logic + let(:types_de_champ_public) { [{ type: :text, libelle: 'condition', condition: ds_eq(champ_value(1), constant(2)), stable_id: 2 }] } + let(:types_de_champ_private) { [{ type: :decimal_number, stable_id: 1 }] } + let(:error_on_condition) { "Le champ « condition » a une logique conditionnelle invalide" } + + it 'validate without context' do + procedure.validate + expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).to be_empty + end + + it 'validate prevent condition' do + procedure.validate(:types_de_champ_public_editor) + expect(procedure.errors.full_messages_for(:draft_types_de_champ_public)).to include(error_on_condition) end end end diff --git a/spec/services/procedure_export_service_zip_spec.rb b/spec/services/procedure_export_service_zip_spec.rb index 14e5ceaad..e4daaf3ee 100644 --- a/spec/services/procedure_export_service_zip_spec.rb +++ b/spec/services/procedure_export_service_zip_spec.rb @@ -39,8 +39,7 @@ describe ProcedureExportService do ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do subject end - - expect(sql_count <= 58).to be_truthy + expect(sql_count <= 62).to be_truthy dossier = dossiers.first