From 6328011f607928b7a90b0da90870fea39e73af06 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 20 Jul 2020 15:18:44 +0000 Subject: [PATCH] models: require belong_to associations on champ - Make `champ.dossier` a requirement; - Move the dossier_id assignation to `before_validation` (otherwise the record is invalid, and never gets saved); - Allow specs to only build the champ (instead of saving it to the database), which bypasses the requirement to have a dossier. --- app/models/champ.rb | 6 +- .../champs/siret_controller_spec.rb | 15 +- .../instructeurs/dossiers_controller_spec.rb | 21 +- spec/factories/champ.rb | 377 ++++++++++-------- spec/factories/dossier.rb | 4 +- spec/factories/procedure.rb | 10 +- spec/factories/type_de_champ.rb | 4 +- spec/jobs/virus_scanner_job_spec.rb | 2 +- .../active_storage/downloadable_file_spec.rb | 15 +- spec/models/champ_shared_example.rb | 5 +- spec/models/champ_spec.rb | 105 +++-- spec/serializers/dossier_serializer_spec.rb | 10 +- .../shared/dossiers/_champs.html.haml_spec.rb | 18 +- 13 files changed, 318 insertions(+), 274 deletions(-) diff --git a/app/models/champ.rb b/app/models/champ.rb index 63a16fdc8..f6861b498 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -15,8 +15,8 @@ # type_de_champ_id :integer # class Champ < ApplicationRecord - belongs_to :dossier, -> { with_discarded }, inverse_of: :champs, touch: true - belongs_to :type_de_champ, inverse_of: :champ + belongs_to :dossier, -> { with_discarded }, inverse_of: :champs, touch: true, optional: false + belongs_to :type_de_champ, inverse_of: :champ, optional: false belongs_to :parent, class_name: 'Champ', optional: true has_many :commentaires has_one_attached :piece_justificative_file @@ -49,7 +49,7 @@ class Champ < ApplicationRecord scope :ordered, -> { includes(:type_de_champ).order(:row, 'types_de_champ.order_place') } scope :root, -> { where(parent_id: nil) } - before_create :set_dossier_id, if: :needs_dossier_id? + before_validation :set_dossier_id, if: :needs_dossier_id? validates :type_de_champ_id, uniqueness: { scope: [:dossier_id, :row] } diff --git a/spec/controllers/champs/siret_controller_spec.rb b/spec/controllers/champs/siret_controller_spec.rb index 545872192..89def8eff 100644 --- a/spec/controllers/champs/siret_controller_spec.rb +++ b/spec/controllers/champs/siret_controller_spec.rb @@ -1,14 +1,14 @@ describe Champs::SiretController, type: :controller do let(:user) { create(:user) } - let(:procedure) { create(:procedure, :published) } + let(:procedure) do + tdc_siret = build(:type_de_champ_siret, procedure: nil) + create(:procedure, :published, types_de_champ: [tdc_siret]) + end describe '#show' do let(:dossier) { create(:dossier, user: user, procedure: procedure) } - let(:champ) do - d = dossier - type_de_champ = create(:type_de_champ_siret, procedure: procedure) - type_de_champ.champ.create(dossier: d, value: nil, etablissement: nil) - end + let(:champ) { dossier.champs.first } + let(:params) do { champ_id: champ.id, @@ -27,6 +27,7 @@ describe Champs::SiretController, type: :controller do let(:api_etablissement_status) { 200 } let(:api_etablissement_body) { File.read('spec/fixtures/files/api_entreprise/etablissements.json') } let(:token_expired) { false } + before do sign_in user stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}?.*token=/) @@ -112,7 +113,7 @@ describe Champs::SiretController, type: :controller do champ.reload expect(champ.value).to eq(siret) expect(champ.etablissement.siret).to eq(siret) - expect(champ.reload.etablissement.naf).to eq("6202A") + expect(champ.etablissement.naf).to eq("6202A") expect(dossier.reload.etablissement).to eq(nil) end end diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 2213f501b..31c8a377e 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -561,32 +561,35 @@ describe Instructeurs::DossiersController, type: :controller do describe "#update_annotations" do let(:champ_multiple_drop_down_list) do - create(:type_de_champ_multiple_drop_down_list, :private, libelle: 'libelle').champ.create + tdc = create(:type_de_champ_multiple_drop_down_list, :private, procedure: procedure, libelle: 'libelle') + create(:champ_multiple_drop_down_list, :private, type_de_champ: tdc, dossier: dossier) end let(:champ_linked_drop_down_list) do - create(:type_de_champ_linked_drop_down_list, :private, libelle: 'libelle').champ.create + tdc = create(:type_de_champ_linked_drop_down_list, :private, procedure: procedure, libelle: 'libelle') + create(:champ_linked_drop_down_list, :private, type_de_champ: tdc, dossier: dossier) end let(:champ_datetime) do - create(:type_de_champ_datetime, :private, libelle: 'libelle').champ.create + tdc = create(:type_de_champ_datetime, :private, procedure: procedure, libelle: 'libelle') + create(:champ_datetime, :private, type_de_champ: tdc, dossier: dossier) end let(:champ_repetition) do - tdc = create(:type_de_champ_repetition, :private, libelle: 'libelle') - tdc.types_de_champ << create(:type_de_champ_text, libelle: 'libelle') - champ = tdc.champ.create + tdc = create(:type_de_champ_repetition, :private, :with_types_de_champ, procedure: procedure, libelle: 'libelle') + tdc.types_de_champ << create(:type_de_champ_text, procedure: procedure, libelle: 'libelle') + champ = create(:champ_repetition, :private, type_de_champ: tdc, dossier: dossier) champ.add_row champ end - let(:dossier) do - create(:dossier, :en_construction, procedure: procedure, champs_private: [champ_multiple_drop_down_list, champ_linked_drop_down_list, champ_datetime, champ_repetition]) - end + let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:now) { Time.zone.parse('01/01/2100') } before do + dossier.champs_private << [champ_multiple_drop_down_list, champ_linked_drop_down_list, champ_datetime, champ_repetition] + Timecop.freeze(now) patch :update_annotations, params: params diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 4b10f249f..1a20f4e50 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -1,197 +1,220 @@ FactoryBot.define do factory :champ do - type_de_champ { create(:type_de_champ) } + add_attribute(:private) { false } - trait :checkbox do - type_de_champ { create(:type_de_champ_checkbox) } - end + dossier { association :dossier } + type_de_champ { association :type_de_champ, procedure: dossier.procedure } - trait :header_section do - type_de_champ { create(:type_de_champ_header_section) } - end - - trait :explication do - type_de_champ { create(:type_de_champ_explication) } - end - - trait :dossier_link do - type_de_champ { create(:type_de_champ_dossier_link) } - end - - trait :piece_justificative do - type_de_champ { create(:type_de_champ_piece_justificative) } + trait :private do + add_attribute(:private) { true } end trait :with_piece_justificative_file do - after(:create) do |champ, _evaluator| + after(:build) do |champ, _evaluator| champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") end end - end - factory :champ_text, class: 'Champs::TextChamp' do - type_de_champ { create(:type_de_champ_text) } - value { 'text' } - end - - factory :champ_textarea, class: 'Champs::TextareaChamp' do - type_de_champ { create(:type_de_champ_textarea) } - value { 'textarea' } - end - - factory :champ_date, class: 'Champs::DateChamp' do - type_de_champ { create(:type_de_champ_date) } - value { '2019-07-10' } - end - - factory :champ_datetime, class: 'Champs::DatetimeChamp' do - type_de_champ { create(:type_de_champ_datetime) } - value { '15/09/1962 15:35' } - end - - factory :champ_number, class: 'Champs::NumberChamp' do - type_de_champ { create(:type_de_champ_number) } - value { '42' } - end - - factory :champ_decimal_number, class: 'Champs::DecimalNumberChamp' do - type_de_champ { create(:type_de_champ_decimal_number) } - value { '42.1' } - end - - factory :champ_integer_number, class: 'Champs::IntegerNumberChamp' do - type_de_champ { create(:type_de_champ_integer_number) } - value { '42' } - end - - factory :champ_checkbox, class: 'Champs::CheckboxChamp' do - type_de_champ { create(:type_de_champ_checkbox) } - value { 'on' } - end - - factory :champ_civilite, class: 'Champs::CiviliteChamp' do - type_de_champ { create(:type_de_champ_civilite) } - value { 'M.' } - end - - factory :champ_email, class: 'Champs::EmailChamp' do - type_de_champ { create(:type_de_champ_email) } - value { 'yoda@beta.gouv.fr' } - end - - factory :champ_phone, class: 'Champs::PhoneChamp' do - type_de_champ { create(:type_de_champ_phone) } - value { '0666666666' } - end - - factory :champ_address, class: 'Champs::AddressChamp' do - type_de_champ { create(:type_de_champ_address) } - value { '2 rue des Démarches' } - end - - factory :champ_yes_no, class: 'Champs::YesNoChamp' do - type_de_champ { create(:type_de_champ_yes_no) } - value { 'true' } - end - - factory :champ_drop_down_list, class: 'Champs::DropDownListChamp' do - type_de_champ { create(:type_de_champ_drop_down_list) } - value { 'choix 1' } - end - - factory :champ_multiple_drop_down_list, class: 'Champs::MultipleDropDownListChamp' do - type_de_champ { create(:type_de_champ_multiple_drop_down_list) } - value { '["choix 1", "choix 2"]' } - end - - factory :champ_linked_drop_down_list, class: 'Champs::LinkedDropDownListChamp' do - type_de_champ { create(:type_de_champ_linked_drop_down_list) } - value { '["categorie 1", "choix 1"]' } - end - - factory :champ_pays, class: 'Champs::PaysChamp' do - type_de_champ { create(:type_de_champ_pays) } - value { 'France' } - end - - factory :champ_regions, class: 'Champs::RegionChamp' do - type_de_champ { create(:type_de_champ_regions) } - value { 'Guadeloupe' } - end - - factory :champ_departements, class: 'Champs::DepartementChamp' do - type_de_champ { create(:type_de_champ_departements) } - value { '971 - Guadeloupe' } - end - - factory :champ_communes, class: 'Champs::CommuneChamp' do - type_de_champ { create(:type_de_champ_communes) } - value { 'Paris' } - end - - factory :champ_engagement, class: 'Champs::EngagementChamp' do - type_de_champ { create(:type_de_champ_engagement) } - value { 'true' } - end - - factory :champ_header_section, class: 'Champs::HeaderSectionChamp' do - type_de_champ { create(:type_de_champ_header_section) } - value { 'une section' } - end - - factory :champ_explication, class: 'Champs::ExplicationChamp' do - type_de_champ { create(:type_de_champ_explication) } - value { '' } - end - - factory :champ_dossier_link, class: 'Champs::DossierLinkChamp' do - type_de_champ { create(:type_de_champ_dossier_link) } - value { create(:dossier).id } - end - - factory :champ_piece_justificative, class: 'Champs::PieceJustificativeChamp' do - type_de_champ { create(:type_de_champ_piece_justificative) } - - after(:build) do |champ, _evaluator| - champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") + factory :champ_text, class: 'Champs::TextChamp' do + type_de_champ { association :type_de_champ_text, procedure: dossier.procedure } + value { 'text' } end - end - factory :champ_carte, class: 'Champs::CarteChamp' do - type_de_champ { create(:type_de_champ_carte) } - end - - factory :champ_siret, class: 'Champs::SiretChamp' do - association :type_de_champ, factory: [:type_de_champ_siret] - association :etablissement, factory: [:etablissement] - value { '44011762001530' } - end - - factory :champ_repetition, class: 'Champs::RepetitionChamp' do - type_de_champ { create(:type_de_champ_repetition) } - - after(:build) do |champ_repetition, _evaluator| - type_de_champ_text = create(:type_de_champ_text, order_place: 0, parent: champ_repetition.type_de_champ, libelle: 'Nom') - type_de_champ_number = create(:type_de_champ_number, order_place: 1, parent: champ_repetition.type_de_champ, libelle: 'Age') - - create(:champ_text, row: 0, type_de_champ: type_de_champ_text, parent: champ_repetition) - create(:champ_number, row: 0, type_de_champ: type_de_champ_number, parent: champ_repetition) - create(:champ_text, row: 1, type_de_champ: type_de_champ_text, parent: champ_repetition) - create(:champ_number, row: 1, type_de_champ: type_de_champ_number, parent: champ_repetition) + factory :champ_textarea, class: 'Champs::TextareaChamp' do + type_de_champ { association :type_de_champ_textarea, procedure: dossier.procedure } + value { 'textarea' } end - end - factory :champ_repetition_with_piece_jointe, class: 'Champs::RepetitionChamp' do - type_de_champ { create(:type_de_champ_repetition) } + factory :champ_date, class: 'Champs::DateChamp' do + type_de_champ { association :type_de_champ_date, procedure: dossier.procedure } + value { '2019-07-10' } + end - after(:build) do |champ_repetition, _evaluator| - type_de_champ_pj0 = create(:type_de_champ_piece_justificative, order_place: 0, parent: champ_repetition.type_de_champ, libelle: 'Justificatif de domicile') - type_de_champ_pj1 = create(:type_de_champ_piece_justificative, order_place: 1, parent: champ_repetition.type_de_champ, libelle: 'Carte d\'identité') + factory :champ_datetime, class: 'Champs::DatetimeChamp' do + type_de_champ { association :type_de_champ_datetime, procedure: dossier.procedure } + value { '15/09/1962 15:35' } + end - create(:champ_piece_justificative, row: 0, type_de_champ: type_de_champ_pj0, parent: champ_repetition) - create(:champ_piece_justificative, row: 0, type_de_champ: type_de_champ_pj1, parent: champ_repetition) - create(:champ_piece_justificative, row: 1, type_de_champ: type_de_champ_pj0, parent: champ_repetition) - create(:champ_piece_justificative, row: 1, type_de_champ: type_de_champ_pj1, parent: champ_repetition) + factory :champ_number, class: 'Champs::NumberChamp' do + type_de_champ { association :type_de_champ_number, procedure: dossier.procedure } + value { '42' } + end + + factory :champ_decimal_number, class: 'Champs::DecimalNumberChamp' do + type_de_champ { association :type_de_champ_decimal_number, procedure: dossier.procedure } + value { '42.1' } + end + + factory :champ_integer_number, class: 'Champs::IntegerNumberChamp' do + type_de_champ { association :type_de_champ_integer_number, procedure: dossier.procedure } + value { '42' } + end + + factory :champ_checkbox, class: 'Champs::CheckboxChamp' do + type_de_champ { association :type_de_champ_checkbox, procedure: dossier.procedure } + value { 'on' } + end + + factory :champ_civilite, class: 'Champs::CiviliteChamp' do + type_de_champ { association :type_de_champ_civilite, procedure: dossier.procedure } + value { 'M.' } + end + + factory :champ_email, class: 'Champs::EmailChamp' do + type_de_champ { association :type_de_champ_email, procedure: dossier.procedure } + value { 'yoda@beta.gouv.fr' } + end + + factory :champ_phone, class: 'Champs::PhoneChamp' do + type_de_champ { association :type_de_champ_phone, procedure: dossier.procedure } + value { '0666666666' } + end + + factory :champ_address, class: 'Champs::AddressChamp' do + type_de_champ { association :type_de_champ_address, procedure: dossier.procedure } + value { '2 rue des Démarches' } + end + + factory :champ_yes_no, class: 'Champs::YesNoChamp' do + type_de_champ { association :type_de_champ_yes_no, procedure: dossier.procedure } + value { 'true' } + end + + factory :champ_drop_down_list, class: 'Champs::DropDownListChamp' do + type_de_champ { association :type_de_champ_drop_down_list, procedure: dossier.procedure } + value { 'choix 1' } + end + + factory :champ_multiple_drop_down_list, class: 'Champs::MultipleDropDownListChamp' do + type_de_champ { association :type_de_champ_multiple_drop_down_list, procedure: dossier.procedure } + value { '["choix 1", "choix 2"]' } + end + + factory :champ_linked_drop_down_list, class: 'Champs::LinkedDropDownListChamp' do + type_de_champ { association :type_de_champ_linked_drop_down_list, procedure: dossier.procedure } + value { '["categorie 1", "choix 1"]' } + end + + factory :champ_pays, class: 'Champs::PaysChamp' do + type_de_champ { association :type_de_champ_pays, procedure: dossier.procedure } + value { 'France' } + end + + factory :champ_regions, class: 'Champs::RegionChamp' do + type_de_champ { association :type_de_champ_regions, procedure: dossier.procedure } + value { 'Guadeloupe' } + end + + factory :champ_departements, class: 'Champs::DepartementChamp' do + type_de_champ { association :type_de_champ_departements, procedure: dossier.procedure } + value { '971 - Guadeloupe' } + end + + factory :champ_communes, class: 'Champs::CommuneChamp' do + type_de_champ { association :type_de_champ_communes, procedure: dossier.procedure } + value { 'Paris' } + end + + factory :champ_engagement, class: 'Champs::EngagementChamp' do + type_de_champ { association :type_de_champ_engagement, procedure: dossier.procedure } + value { 'true' } + end + + factory :champ_header_section, class: 'Champs::HeaderSectionChamp' do + type_de_champ { association :type_de_champ_header_section, procedure: dossier.procedure } + value { 'une section' } + end + + factory :champ_explication, class: 'Champs::ExplicationChamp' do + type_de_champ { association :type_de_champ_explication, procedure: dossier.procedure } + value { '' } + end + + factory :champ_dossier_link, class: 'Champs::DossierLinkChamp' do + type_de_champ { association :type_de_champ_dossier_link, procedure: dossier.procedure } + value { create(:dossier).id } + end + + factory :champ_piece_justificative, class: 'Champs::PieceJustificativeChamp' do + type_de_champ { association :type_de_champ_piece_justificative, procedure: dossier.procedure } + + after(:build) do |champ, _evaluator| + champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") + end + end + + factory :champ_carte, class: 'Champs::CarteChamp' do + type_de_champ { association :type_de_champ_carte, procedure: dossier.procedure } + end + + factory :champ_siret, class: 'Champs::SiretChamp' do + association :type_de_champ, factory: [:type_de_champ_siret] + association :etablissement, factory: [:etablissement] + value { '44011762001530' } + end + + factory :champ_repetition, class: 'Champs::RepetitionChamp' do + type_de_champ { association :type_de_champ_repetition, procedure: dossier.procedure } + + after(:build) do |champ_repetition, _evaluator| + types_de_champ = champ_repetition.type_de_champ.types_de_champ + existing_type_de_champ_text = types_de_champ.find { |tdc| tdc.libelle == 'Nom' } + type_de_champ_text = existing_type_de_champ_text || build( + :type_de_champ_text, + order_place: 0, + procedure: champ_repetition.dossier.procedure, + parent: champ_repetition.type_de_champ, + libelle: 'Nom' + ) + + types_de_champ << type_de_champ_text + existing_type_de_champ_number = types_de_champ.find { |tdc| tdc.libelle == 'Age' } + type_de_champ_number = existing_type_de_champ_number || build( + :type_de_champ_number, + order_place: 1, + procedure: champ_repetition.dossier.procedure, + parent: champ_repetition.type_de_champ, + libelle: 'Age' + ) + types_de_champ << type_de_champ_number + + champ_repetition.champs << [ + build(:champ_text, dossier: champ_repetition.dossier, row: 0, type_de_champ: type_de_champ_text, parent: champ_repetition), + build(:champ_number, dossier: champ_repetition.dossier, row: 0, type_de_champ: type_de_champ_number, parent: champ_repetition), + build(:champ_text, dossier: champ_repetition.dossier, row: 1, type_de_champ: type_de_champ_text, parent: champ_repetition), + build(:champ_number, dossier: champ_repetition.dossier, row: 1, type_de_champ: type_de_champ_number, parent: champ_repetition) + ] + end + + trait :without_champs do + after(:build) do |champ_repetition, _evaluator| + champ_repetition.champs = [] + end + end + end + + factory :champ_repetition_with_piece_jointe, class: 'Champs::RepetitionChamp' do + type_de_champ { association :type_de_champ_repetition, procedure: dossier.procedure } + + after(:build) do |champ_repetition, _evaluator| + type_de_champ_pj0 = build(:type_de_champ_piece_justificative, + procedure: champ_repetition.dossier.procedure, + order_place: 0, + parent: champ_repetition.type_de_champ, + libelle: 'Justificatif de domicile') + type_de_champ_pj1 = build(:type_de_champ_piece_justificative, + procedure: champ_repetition.dossier.procedure, + order_place: 1, + parent: champ_repetition.type_de_champ, + libelle: 'Carte d\'identité') + + champ_repetition.champs << [ + build(:champ_piece_justificative, dossier: champ_repetition.dossier, row: 0, type_de_champ: type_de_champ_pj0), + build(:champ_piece_justificative, dossier: champ_repetition.dossier, row: 0, type_de_champ: type_de_champ_pj1), + build(:champ_piece_justificative, dossier: champ_repetition.dossier, row: 1, type_de_champ: type_de_champ_pj0), + build(:champ_piece_justificative, dossier: champ_repetition.dossier, row: 1, type_de_champ: type_de_champ_pj1) + ] + end end end end diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 370e85855..c5aa95d98 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -203,7 +203,7 @@ FactoryBot.define do trait :with_all_champs do after(:create) do |dossier, _evaluator| dossier.champs = dossier.procedure.types_de_champ.map do |type_de_champ| - build(:"champ_#{type_de_champ.type_champ}", type_de_champ: type_de_champ) + build(:"champ_#{type_de_champ.type_champ}", dossier: dossier, type_de_champ: type_de_champ) end dossier.save! end @@ -212,7 +212,7 @@ FactoryBot.define do trait :with_all_annotations do after(:create) do |dossier, _evaluator| dossier.champs = dossier.procedure.types_de_champ.map do |type_de_champ| - build(:"champ_#{type_de_champ.type_champ}", type_de_champ: type_de_champ) + build(:"champ_#{type_de_champ.type_champ}", dossier: dossier, type_de_champ: type_de_champ) end dossier.save! end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 112e39ed4..54cce7d48 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -232,10 +232,10 @@ FactoryBot.define do if libelle == 'drop_down_list' libelle = 'simple_drop_down_list' end - build(:"type_de_champ_#{type_champ}", mandatory: true, libelle: libelle, order_place: index) + build(:"type_de_champ_#{type_champ}", procedure: procedure, mandatory: true, libelle: libelle, order_place: index) end - procedure.types_de_champ << build(:type_de_champ_drop_down_list, :long, mandatory: true, libelle: 'simple_choice_drop_down_list_long') - procedure.types_de_champ << build(:type_de_champ_multiple_drop_down_list, :long, mandatory: true, libelle: 'multiple_choice_drop_down_list_long') + procedure.types_de_champ << build(:type_de_champ_drop_down_list, :long, procedure: procedure, mandatory: true, libelle: 'simple_choice_drop_down_list_long') + procedure.types_de_champ << build(:type_de_champ_multiple_drop_down_list, :long, procedure: procedure, mandatory: true, libelle: 'multiple_choice_drop_down_list_long') end end @@ -245,7 +245,7 @@ FactoryBot.define do if libelle == 'drop_down_list' libelle = 'simple_drop_down_list' end - build(:"type_de_champ_#{type_champ}", libelle: libelle, order_place: index) + build(:"type_de_champ_#{type_champ}", procedure: procedure, libelle: libelle, order_place: index) end end end @@ -256,7 +256,7 @@ FactoryBot.define do if libelle == 'drop_down_list' libelle = 'simple_drop_down_list' end - build(:"type_de_champ_#{type_champ}", private: true, libelle: libelle, order_place: index) + build(:"type_de_champ_#{type_champ}", procedure: procedure, private: true, libelle: libelle, order_place: index) end end end diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index fa340d847..12264375f 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -94,7 +94,7 @@ FactoryBot.define do factory :type_de_champ_piece_justificative do type_champ { TypeDeChamp.type_champs.fetch(:piece_justificative) } - after(:create) do |tc, _evaluator| + after(:build) do |tc, _evaluator| tc.piece_justificative_template.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") end end @@ -109,7 +109,7 @@ FactoryBot.define do trait :with_types_de_champ do after(:build) do |type_de_champ, _evaluator| - type_de_champ.types_de_champ << create(:type_de_champ, libelle: 'sub type de champ') + type_de_champ.types_de_champ << build(:type_de_champ, procedure: type_de_champ.procedure, libelle: 'sub type de champ') end end end diff --git a/spec/jobs/virus_scanner_job_spec.rb b/spec/jobs/virus_scanner_job_spec.rb index 8b0885ad4..0550f9f1b 100644 --- a/spec/jobs/virus_scanner_job_spec.rb +++ b/spec/jobs/virus_scanner_job_spec.rb @@ -2,7 +2,7 @@ RSpec.describe VirusScannerJob, type: :job do include ActiveJob::TestHelper let(:champ) do - champ = create(:champ, :piece_justificative) + champ = create(:champ_piece_justificative) champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") champ.save champ diff --git a/spec/lib/active_storage/downloadable_file_spec.rb b/spec/lib/active_storage/downloadable_file_spec.rb index a95396e1a..c3675ea3d 100644 --- a/spec/lib/active_storage/downloadable_file_spec.rb +++ b/spec/lib/active_storage/downloadable_file_spec.rb @@ -1,5 +1,5 @@ describe ActiveStorage::DownloadableFile do - let(:dossier) { create(:dossier) } + let(:dossier) { create(:dossier, :en_construction) } subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) } @@ -10,7 +10,7 @@ describe ActiveStorage::DownloadableFile do context 'when there is a piece_justificative' do before do - dossier.champs << create(:champ, :piece_justificative, :with_piece_justificative_file) + dossier.champs << create(:champ_piece_justificative, :with_piece_justificative_file, dossier: dossier) end it { expect(list.length).to eq 1 } @@ -18,15 +18,16 @@ describe ActiveStorage::DownloadableFile do context 'when there is a private piece_justificative' do before do - dossier.champs_private << create(:champ, :piece_justificative, :with_piece_justificative_file, private: true) + dossier.champs_private << create(:champ_piece_justificative, :with_piece_justificative_file, private: true, dossier: dossier) end it { expect(list.length).to eq 1 } end context 'when there is a repetition bloc' do - let(:champ) { build(:champ_repetition_with_piece_jointe) } - let(:dossier) { create(:dossier, :en_construction, champs: [champ]) } + before do + dossier.champs << build(:champ_repetition_with_piece_jointe, dossier: dossier) + end it 'should have 4 piece_justificatives' do expect(list.size).to eq 4 @@ -35,7 +36,7 @@ describe ActiveStorage::DownloadableFile do context 'when there is a message with no attachment' do before do - dossier.commentaires << build(:commentaire, dossier: dossier) + dossier.commentaires << create(:commentaire, dossier: dossier) end it { expect(list.length).to eq 0 } @@ -43,7 +44,7 @@ describe ActiveStorage::DownloadableFile do context 'when there is a message with an attachment' do before do - dossier.commentaires << build(:commentaire, :with_file, dossier: dossier) + dossier.commentaires << create(:commentaire, :with_file, dossier: dossier) end it { expect(list.length).to eq 1 } diff --git a/spec/models/champ_shared_example.rb b/spec/models/champ_shared_example.rb index d00af9504..8ed267e37 100644 --- a/spec/models/champ_shared_example.rb +++ b/spec/models/champ_shared_example.rb @@ -1,7 +1,7 @@ shared_examples 'champ_spec' do describe 'mandatory_and_blank?' do let(:type_de_champ) { build(:type_de_champ, mandatory: mandatory) } - let(:champ) { type_de_champ.champ.build(value: value) } + let(:champ) { build(:champ, type_de_champ: type_de_champ, value: value) } let(:value) { '' } let(:mandatory) { true } @@ -33,8 +33,7 @@ shared_examples 'champ_spec' do end context "when type_champ=date" do - let(:type_de_champ) { create(:type_de_champ_date) } - let(:champ) { type_de_champ.champ.create } + let(:champ) { build(:champ_date) } it "should convert %d/%m/%Y format to ISO" do champ.value = "31/12/2017" diff --git a/spec/models/champ_spec.rb b/spec/models/champ_spec.rb index 119be869d..88a030e87 100644 --- a/spec/models/champ_spec.rb +++ b/spec/models/champ_spec.rb @@ -47,7 +47,7 @@ describe Champ do let(:public_champ) { dossier.champs.first } let(:private_champ) { dossier.champs_private.first } let(:champ_in_repetition) { dossier.champs.find(&:repetition?).champs.first } - let(:standalone_champ) { create(:champ, dossier: nil) } + let(:standalone_champ) { build(:champ, type_de_champ: build(:type_de_champ), dossier: nil) } it 'returns the sibling champs of a champ' do expect(public_champ.siblings).to eq(dossier.champs) @@ -58,10 +58,9 @@ describe Champ do end describe '#format_datetime' do - let(:type_de_champ) { build(:type_de_champ_datetime) } - let(:champ) { type_de_champ.champ.build(value: value) } + let(:champ) { build(:champ_datetime, value: value) } - before { champ.save } + before { champ.save! } context 'when the value is sent by a modern browser' do let(:value) { '2017-12-31 10:23' } @@ -77,10 +76,9 @@ describe Champ do end describe '#multiple_select_to_string' do - let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list) } - let(:champ) { type_de_champ.champ.build(value: value) } + let(:champ) { build(:champ_multiple_drop_down_list, value: value) } - before { champ.save } + before { champ.save! } # when using the old form, and the ChampsService Class # TODO: to remove @@ -412,7 +410,7 @@ describe Champ do end describe '#enqueue_virus_check' do - let(:champ) { type_de_champ.champ.build(value: nil) } + let(:champ) { build(:champ_piece_justificative, type_de_champ: type_de_champ) } context 'when type_champ is type_de_champ_piece_justificative' do let(:type_de_champ) { create(:type_de_champ_piece_justificative) } @@ -420,7 +418,7 @@ describe Champ do context 'and there is a blob' do before do champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") - champ.save + champ.save! end it { expect(champ.piece_justificative_file.virus_scanner.started?).to be_truthy } @@ -428,50 +426,69 @@ describe Champ do end end - describe "repetition" do - let(:dossier) { create(:dossier) } - let(:champ) { Champs::RepetitionChamp.create(dossier: dossier) } - let(:champ_text) { create(:champ_text, row: 0) } - let(:champ_integer_number) { create(:champ_integer_number, row: 0) } - let(:champ_text_attrs) { attributes_for(:champ_text, row: 1) } - let(:champ_text_row_1) { create(:champ_text, row: 1, parent: champ) } + describe 'repetition' do + let(:procedure) { build(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private) } + let(:tdc_text) { build(:type_de_champ_text, procedure: procedure) } + let(:tdc_integer) { build(:type_de_champ_integer_number, procedure: procedure) } + let(:tdc_repetition) { build(:type_de_champ_repetition, procedure: procedure, types_de_champ: [tdc_text, tdc_integer]) } - it "associates nested champs to the parent dossier" do - expect(champ.rows.size).to eq(0) - dossier.reload - expect(dossier.champs.size).to eq(2) + let(:dossier) { create(:dossier, procedure: procedure) } + let(:champ) { dossier.champs.find(&:repetition?) } + let(:champ_text) { champ.champs.find { |c| c.type_champ == 'text' } } + let(:champ_integer) { champ.champs.find { |c| c.type_champ == 'integer_number' } } + let(:champ_text_attrs) { attributes_for(:champ_text, type_de_champ: tdc_text, row: 1) } - dossier.update(champs_attributes: [ - { - id: champ.id, - champs_attributes: [champ_text_attrs] - } - ]) + before do + procedure.types_de_champ << tdc_repetition + procedure.save! + procedure.reload + end - champ.reload - dossier.reload - expect(dossier.champs.size).to eq(2) - expect(champ.rows.size).to eq(1) + context 'when creating the model directly' do + let(:champ_text_row_1) { create(:champ_text, type_de_champ: tdc_text, row: 2, parent: champ, dossier: nil) } - expect(champ.champs.first.dossier).to eq(dossier) + it 'associates nested champs to the parent dossier' do + expect(champ_text_row_1.dossier_id).to eq(champ.dossier_id) + end + end - # Make champs ordered - champ_integer_number.type_de_champ.update(order_place: 0) - champ_text.type_de_champ.update(order_place: 1) + context 'when updating using nested attributes' do + subject do + dossier.update!(champs_attributes: [ + { + id: champ.id, + champs_attributes: [champ_text_attrs] + } + ]) + champ.reload + dossier.reload + end - champ.champs << champ_integer_number - row = champ.reload.rows.first - expect(row.size).to eq(1) - expect(row.first).to eq(champ_integer_number) + it 'associates nested champs to the parent dossier' do + subject - champ.champs << champ_text - row = champ.reload.rows.first - expect(row.size).to eq(2) - expect(row.second).to eq(champ_text) + expect(dossier.champs.size).to eq(2) + expect(champ.rows.size).to eq(2) + second_row = champ.rows.second + expect(second_row.size).to eq(1) + expect(second_row.first.dossier).to eq(dossier) - expect(champ.rows.size).to eq(2) + # Make champs ordered + champ_integer.type_de_champ.update(order_place: 0) + champ_text.type_de_champ.update(order_place: 1) - expect(champ_text_row_1.dossier_id).to eq(champ.dossier_id) + champ.champs << champ_integer + first_row = champ.reload.rows.first + expect(first_row.size).to eq(2) + expect(first_row.first).to eq(champ_integer) + + champ.champs << champ_text + first_row = champ.reload.rows.first + expect(first_row.size).to eq(2) + expect(first_row.second).to eq(champ_text) + + expect(champ.rows.size).to eq(2) + end end end end diff --git a/spec/serializers/dossier_serializer_spec.rb b/spec/serializers/dossier_serializer_spec.rb index f40d45186..69c8f624c 100644 --- a/spec/serializers/dossier_serializer_spec.rb +++ b/spec/serializers/dossier_serializer_spec.rb @@ -21,11 +21,11 @@ describe DossierSerializer do let(:dossier) { create(:dossier, :en_construction, procedure: create(:procedure, :published, :with_type_de_champ)) } before do - dossier.champs << create(:champ_carte) - dossier.champs << create(:champ_siret) - dossier.champs << create(:champ_integer_number) - dossier.champs << create(:champ_decimal_number) - dossier.champs << create(:champ_linked_drop_down_list) + dossier.champs << build(:champ_carte) + dossier.champs << build(:champ_siret) + dossier.champs << build(:champ_integer_number) + dossier.champs << build(:champ_decimal_number) + dossier.champs << build(:champ_linked_drop_down_list) end it { diff --git a/spec/views/shared/dossiers/_champs.html.haml_spec.rb b/spec/views/shared/dossiers/_champs.html.haml_spec.rb index 3f42039b9..14fedde0b 100644 --- a/spec/views/shared/dossiers/_champs.html.haml_spec.rb +++ b/spec/views/shared/dossiers/_champs.html.haml_spec.rb @@ -13,11 +13,11 @@ describe 'shared/dossiers/champs.html.haml', type: :view do context "there are some champs" do let(:dossier) { create(:dossier) } let(:avis) { create :avis, dossier: dossier, instructeur: instructeur } - let(:champ1) { create(:champ, :checkbox, value: "on") } - let(:champ2) { create(:champ, :header_section, value: "Section") } - let(:champ3) { create(:champ, :explication, value: "mazette") } - let(:champ4) { create(:champ, :dossier_link, value: dossier.id) } - let(:champ5) { create(:champ_textarea, value: "Some long text in a textarea.") } + let(:champ1) { create(:champ_checkbox, dossier: dossier, value: "on") } + let(:champ2) { create(:champ_header_section, dossier: dossier, value: "Section") } + let(:champ3) { create(:champ_explication, dossier: dossier, value: "mazette") } + let(:champ4) { create(:champ_dossier_link, dossier: dossier, value: dossier.id) } + let(:champ5) { create(:champ_textarea, dossier: dossier, value: "Some long text in a textarea.") } let(:champs) { [champ1, champ2, champ3, champ4, champ5] } before { dossier.avis << avis } @@ -71,7 +71,7 @@ describe 'shared/dossiers/champs.html.haml', type: :view do context "with seen_at" do let(:dossier) { create(:dossier) } let(:nouveau_groupe_instructeur) { create(:groupe_instructeur, procedure: dossier.procedure) } - let(:champ1) { create(:champ, :checkbox, value: "on") } + let(:champ1) { create(:champ_checkbox, dossier: dossier, value: "on") } let(:champs) { [champ1] } context "with a demande_seen_at after groupe_instructeur_updated_at" do @@ -96,7 +96,7 @@ describe 'shared/dossiers/champs.html.haml', type: :view do context "with a dossier champ, but we are not authorized to acces the dossier" do let(:dossier) { create(:dossier) } - let(:champ) { create(:champ, :dossier_link, value: dossier.id) } + let(:champ) { create(:champ_dossier_link, dossier: dossier, value: dossier.id) } let(:champs) { [champ] } it { is_expected.not_to have_link("Dossier nº #{dossier.id}") } @@ -106,7 +106,7 @@ describe 'shared/dossiers/champs.html.haml', type: :view do context "with a dossier_link champ but without value" do let(:dossier) { create(:dossier) } - let(:champ) { create(:champ, :dossier_link, value: nil) } + let(:champ) { create(:champ_dossier_link, dossier: dossier, value: nil) } let(:champs) { [champ] } it { is_expected.to include("Pas de dossier associé") } @@ -114,7 +114,7 @@ describe 'shared/dossiers/champs.html.haml', type: :view do context "with seen_at" do let(:dossier) { create(:dossier) } - let(:champ1) { create(:champ, :checkbox, value: "on") } + let(:champ1) { create(:champ_checkbox, dossier: dossier, value: "on") } let(:champs) { [champ1] } context "with a demande_seen_at after champ updated_at" do