Merge pull request #7608 from tchak/test-better-factories

Amelioration des factories de procedure/types_de_champ
This commit is contained in:
Paul Chavard 2022-08-02 18:03:31 +02:00 committed by GitHub
commit 5110077587
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 280 additions and 62 deletions

View file

@ -21,6 +21,7 @@ FactoryBot.define do
administrateur {}
instructeurs { [] }
types_de_champ { [] }
types_de_champ_public { [] }
types_de_champ_private { [] }
updated_at { nil }
attestation_template { nil }
@ -29,8 +30,18 @@ FactoryBot.define do
after(:build) do |procedure, evaluator|
initial_revision = build(:procedure_revision, procedure: procedure, attestation_template: evaluator.attestation_template, dossier_submitted_message: evaluator.dossier_submitted_message)
if evaluator.types_de_champ_public.present?
build_types_de_champ(evaluator.types_de_champ_public, revision: initial_revision, scope: :public)
end
add_types_de_champs(evaluator.types_de_champ, to: initial_revision, scope: :public)
add_types_de_champs(evaluator.types_de_champ_private, to: initial_revision, scope: :private)
if evaluator.types_de_champ_private.present?
if evaluator.types_de_champ_private.first.is_a?(Hash)
build_types_de_champ(evaluator.types_de_champ_private, revision: initial_revision, scope: :private)
else
add_types_de_champs(evaluator.types_de_champ_private, to: initial_revision, scope: :private)
end
end
if procedure.brouillon?
procedure.draft_revision = initial_revision
@ -41,6 +52,14 @@ FactoryBot.define do
end
end
before(:create) do |procedure, _evaluator|
procedure.revisions.each do |revision|
revision.association(:types_de_champ).reset
revision.association(:types_de_champ_public).reset
revision.association(:types_de_champ_private).reset
end
end
after(:create) do |procedure, evaluator|
evaluator.instructeurs.each { |i| i.assign_to_procedure(procedure) }
@ -349,6 +368,75 @@ FactoryBot.define do
end
end
def build_types_de_champ(types_de_champ, revision:, scope: :public, parent: nil)
types_de_champ.deep_dup.each.with_index do |type_de_champ_attributes, i|
type = TypeDeChamp.type_champs.fetch(type_de_champ_attributes.delete(:type) || :text)
position = type_de_champ_attributes.delete(:position) || i
children = type_de_champ_attributes.delete(:children)
options = type_de_champ_attributes.delete(:options)
layers = type_de_champ_attributes.delete(:layers)
if options.present?
if type == :drop_down_list
type_de_champ_attributes[:drop_down_other] = options.delete(:other).present?
end
if type.in?([:drop_down_list, :multiple_drop_down_list, :linked_drop_down_list])
type_de_champ_attributes[:drop_down_options] = options
end
end
if type == :linked_drop_down_list
type_de_champ_attributes[:drop_down_secondary_libelle] = type_de_champ_attributes.delete(:secondary_libelle)
type_de_champ_attributes[:drop_down_secondary_description] = type_de_champ_attributes.delete(:secondary_description)
end
if type == :carte && layers.present?
type_de_champ_attributes[:editable_options] = layers.index_with { '1' }
end
type_de_champ = if scope == :private
build(:"type_de_champ_#{type}", :private, no_coordinate: true, **type_de_champ_attributes)
else
build(:"type_de_champ_#{type}", no_coordinate: true, **type_de_champ_attributes)
end
coordinate = build(:procedure_revision_type_de_champ,
revision: revision,
type_de_champ: type_de_champ,
position: position,
parent: parent)
revision.association(:revision_types_de_champ).target << coordinate
if parent.present?
parent.association(:revision_types_de_champ).target << coordinate
end
if type_de_champ.repetition? && children.present?
build_types_de_champ(children, revision: revision, scope: scope, parent: coordinate)
end
end
if parent.present?
parent.association(:revision_types_de_champ).target.sort_by!(&:position)
else
revision_types_de_champ_private, revision_types_de_champ_public = revision.revision_types_de_champ.partition(&:private?)
root_revision_types_de_champ_public, child_revision_types_de_champ_public = revision_types_de_champ_public.partition { |coordinate| coordinate.parent.nil? }
root_revision_types_de_champ_private, child_revision_types_de_champ_private = revision_types_de_champ_private.partition { |coordinate| coordinate.parent.nil? }
revision.association(:revision_types_de_champ_public).target = root_revision_types_de_champ_public.sort_by(&:position)
revision.association(:revision_types_de_champ_private).target = root_revision_types_de_champ_private.sort_by(&:position)
revision.association(:revision_types_de_champ).target = revision.revision_types_de_champ_public +
revision.revision_types_de_champ_private +
child_revision_types_de_champ_public.sort_by(&:parent).sort_by(&:position) +
child_revision_types_de_champ_private.sort_by(&:parent).sort_by(&:position)
revision.association(:types_de_champ).target = revision.revision_types_de_champ.map(&:type_de_champ)
revision.association(:types_de_champ_public).target = revision.revision_types_de_champ_public.map(&:type_de_champ)
revision.association(:types_de_champ_private).target = revision.revision_types_de_champ_private.map(&:type_de_champ)
end
end
def add_types_de_champs(types_de_champ, to: nil, scope: :public)
revision = to
association_name = scope == :private ? :revision_types_de_champ_private : :revision_types_de_champ_public

View file

@ -6,17 +6,36 @@ FactoryBot.define do
after(:build) do |revision, evaluator|
if evaluator.from_original
original = evaluator.from_original
from_revision = evaluator.from_original
revision.procedure = original.procedure
revision.attestation_template_id = original.attestation_template_id
revision.dossier_submitted_message_id = original.dossier_submitted_message_id
original.revision_types_de_champ_public.each do |r_tdc|
revision.revision_types_de_champ_public << build(:procedure_revision_type_de_champ, from_original: r_tdc)
end
original.revision_types_de_champ_private.each do |r_tdc|
revision.revision_types_de_champ_private << build(:procedure_revision_type_de_champ, from_original: r_tdc)
revision.procedure = from_revision.procedure
revision.attestation_template_id = from_revision.attestation_template_id
revision.dossier_submitted_message_id = from_revision.dossier_submitted_message_id
coordinate_map = {}
revision.association(:revision_types_de_champ).target = from_revision.revision_types_de_champ.map do |from_coordinate|
parent = from_coordinate.parent.present? ? coordinate_map[from_coordinate.parent] : nil
coordinate = build(:procedure_revision_type_de_champ,
revision: revision,
type_de_champ: from_coordinate.type_de_champ,
position: from_coordinate.position,
parent: parent)
if parent.present?
parent.association(:revision_types_de_champ).target << coordinate
elsif from_coordinate.private?
revision.association(:revision_types_de_champ_private).target << coordinate
else
revision.association(:revision_types_de_champ_public).target << coordinate
end
coordinate_map[from_coordinate] = coordinate
coordinate
end
revision.association(:types_de_champ).target = revision.revision_types_de_champ.map(&:type_de_champ)
revision.association(:types_de_champ_public).target = revision.revision_types_de_champ_public.map(&:type_de_champ)
revision.association(:types_de_champ_private).target = revision.revision_types_de_champ_private.map(&:type_de_champ)
end
end
end

View file

@ -1,5 +1,5 @@
describe TagsSubstitutionConcern, type: :model do
let(:types_de_champ) { [] }
let(:types_de_champ_public) { [] }
let(:types_de_champ_private) { [] }
let(:for_individual) { false }
let(:state) { Dossier.states.fetch(:accepte) }
@ -10,7 +10,7 @@ describe TagsSubstitutionConcern, type: :model do
create(:procedure,
:published,
libelle: 'Une magnifique démarche',
types_de_champ: types_de_champ,
types_de_champ_public: types_de_champ_public,
types_de_champ_private: types_de_champ_private,
for_individual: for_individual,
service: service,
@ -96,10 +96,10 @@ describe TagsSubstitutionConcern, type: :model do
end
context 'when the procedure has a type de champ named libelleA et libelleB' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ, libelle: 'libelleA'),
build(:type_de_champ, libelle: 'libelleB')
{ libelle: 'libelleA' },
{ libelle: 'libelleB' }
]
end
@ -138,9 +138,9 @@ describe TagsSubstitutionConcern, type: :model do
end
context 'when the procedure has a type de champ with apostrophes' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ, libelle: "Intitulé de l'‘«\"évènement\"»’")
{ libelle: "Intitulé de l'‘«\"évènement\"»’" }
]
end
@ -191,10 +191,8 @@ describe TagsSubstitutionConcern, type: :model do
end
context 'when the procedure has a linked drop down menus type de champ' do
let(:type_de_champ) do
build(:type_de_champ_linked_drop_down_list, libelle: 'libelle')
end
let(:types_de_champ) { [type_de_champ] }
let(:type_de_champ) { procedure.draft_revision.types_de_champ.first }
let(:types_de_champ_public) { [{ type: :linked_drop_down_list, libelle: 'libelle' }] }
let(:template) { 'tout : --libelle--, primaire : --libelle/primaire--, secondaire : --libelle/secondaire--' }
context 'and the champ has no value' do
@ -218,10 +216,10 @@ describe TagsSubstitutionConcern, type: :model do
it { is_expected.to eq('tout : primo / secundo, primaire : primo, secondaire : secundo') }
context 'and the same libelle is used by a header' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
type_de_champ,
build(:type_de_champ_header_section, libelle: 'libelle')
{ type: :linked_drop_down_list, libelle: 'libelle' },
{ type: :header_section, libelle: 'libelle' }
]
end
@ -255,7 +253,7 @@ describe TagsSubstitutionConcern, type: :model do
end
context 'when the procedure has a type de champ prive named libelleA' do
let(:types_de_champ_private) { [build(:type_de_champ, :private, libelle: 'libelleA')] }
let(:types_de_champ_private) { [{ libelle: 'libelleA' }] }
context 'and it is used in the template' do
let(:template) { '--libelleA--' }
@ -276,13 +274,13 @@ describe TagsSubstitutionConcern, type: :model do
# The dossier just transitionned from brouillon to en construction,
# so champs private are not valid tags yet
let(:types_de_champ_private) { [build(:type_de_champ, :private, libelle: 'libelleA')] }
let(:types_de_champ_private) { [{ libelle: 'libelleA' }] }
it { is_expected.to eq('--libelleA--') }
end
context 'champs publics are valid tags' do
let(:types_de_champ) { [build(:type_de_champ, libelle: 'libelleA')] }
let(:types_de_champ_public) { [{ libelle: 'libelleA' }] }
before { dossier.champs.first.update(value: 'libelle1') }
@ -291,10 +289,10 @@ describe TagsSubstitutionConcern, type: :model do
end
context 'when the procedure has 2 types de champ date and datetime' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ_date, libelle: TypeDeChamp.type_champs.fetch(:date)),
build(:type_de_champ_datetime, libelle: TypeDeChamp.type_champs.fetch(:datetime))
{ type: :date, libelle: TypeDeChamp.type_champs.fetch(:date) },
{ type: :datetime, libelle: TypeDeChamp.type_champs.fetch(:datetime) }
]
end
@ -358,13 +356,13 @@ describe TagsSubstitutionConcern, type: :model do
shared_examples "treat all kinds of space as equivalent" do
context 'and the champ has a non breaking space' do
let(:types_de_champ) { [build(:type_de_champ, libelle: 'mon tag')] }
let(:types_de_champ_public) { [{ libelle: 'mon tag' }] }
it { is_expected.to eq('valeur') }
end
context 'and the champ has an ordinary space' do
let(:types_de_champ) { [build(:type_de_champ, libelle: 'mon tag')] }
let(:types_de_champ_public) { [{ libelle: 'mon tag' }] }
it { is_expected.to eq('valeur') }
end
@ -396,8 +394,8 @@ describe TagsSubstitutionConcern, type: :model do
end
context 'when procedure has revisions' do
let(:types_de_champ) { [build(:type_de_champ, libelle: 'mon ancien libellé')] }
let(:draft_type_de_champ) { procedure.draft_revision.find_and_ensure_exclusive_use(types_de_champ[0].stable_id) }
let(:types_de_champ_public) { [{ libelle: 'mon ancien libellé' }] }
let(:draft_type_de_champ) { procedure.draft_revision.find_and_ensure_exclusive_use(procedure.draft_revision.types_de_champ.first.stable_id) }
before do
draft_type_de_champ.update(libelle: 'mon nouveau libellé')
@ -426,14 +424,14 @@ describe TagsSubstitutionConcern, type: :model do
describe 'tags' do
subject { template_concern.tags }
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ, libelle: 'public'),
build(:type_de_champ_header_section, libelle: 'entête de section'),
build(:type_de_champ_explication, libelle: 'explication')
{ libelle: 'public' },
{ type: :header_section, libelle: 'entête de section' },
{ type: :explication, libelle: 'explication' }
]
end
let(:types_de_champ_private) { [build(:type_de_champ, :private, libelle: 'privé')] }
let(:types_de_champ_private) { [{ libelle: 'privé' }] }
context 'do not generate tags for champs that cannot have usager content' do
it { is_expected.not_to include(include({ libelle: 'entête de section' })) }
@ -472,29 +470,29 @@ describe TagsSubstitutionConcern, type: :model do
let(:text) { 'hello world --public--, --numéro du dossier--, --yolo--' }
subject { template_concern.used_tags_for(text) }
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ, libelle: 'public'),
build(:type_de_champ_header_section, libelle: 'entête de section'),
build(:type_de_champ_explication, libelle: 'explication')
{ libelle: 'public' },
{ type: :header_section, libelle: 'entête de section' },
{ type: :explication, libelle: 'explication' }
]
end
it { is_expected.to eq(["tdc#{types_de_champ.first.stable_id}", 'numéro du dossier', 'yolo']) }
it { is_expected.to eq(["tdc#{procedure.draft_revision.types_de_champ.first.stable_id}", 'numéro du dossier', 'yolo']) }
end
describe 'used_type_de_champ_tags' do
let(:text) { 'hello world --public--, --numéro du dossier--, --yolo--' }
subject { template_concern.used_type_de_champ_tags(text) }
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ, libelle: 'public'),
build(:type_de_champ_header_section, libelle: 'entête de section'),
build(:type_de_champ_explication, libelle: 'explication')
{ libelle: 'public' },
{ type: :header_section, libelle: 'entête de section' },
{ type: :explication, libelle: 'explication' }
]
end
it { is_expected.to eq([["public", types_de_champ.first.stable_id], ['yolo']]) }
it { is_expected.to eq([["public", procedure.draft_revision.types_de_champ.first.stable_id], ['yolo']]) }
end
end

View file

@ -657,14 +657,14 @@ describe ProcedureRevision do
describe '#estimated_fill_duration' do
let(:mandatory) { true }
let(:types_de_champ) do
let(:types_de_champ_public) do
[
build(:type_de_champ_text, position: 1, mandatory: true),
build(:type_de_champ_siret, position: 2, mandatory: true),
build(:type_de_champ_piece_justificative, position: 3, mandatory: mandatory)
{ mandatory: true },
{ type: :siret, mandatory: true },
{ type: :piece_justificative, mandatory: mandatory }
]
end
let(:procedure) { create(:procedure, types_de_champ: types_de_champ) }
let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ_public) }
subject { procedure.active_revision.estimated_fill_duration }
@ -687,13 +687,17 @@ describe ProcedureRevision do
end
context 'when there are repetitions' do
let(:procedure) do
procedure = create(:procedure, types_de_champ: [])
create(:type_de_champ_repetition, position: 1, mandatory: true, procedure: procedure, types_de_champ: [
build(:type_de_champ_text, position: 1, mandatory: true),
build(:type_de_champ_piece_justificative, position: 2, mandatory: true)
])
procedure
let(:types_de_champ_public) do
[
{
type: :repetition,
mandatory: true,
children: [
{ mandatory: true },
{ type: :piece_justificative, position: 2, mandatory: true }
]
}
]
end
it 'estimates that between 2 and 3 rows will be filled for each repetition' do
@ -703,7 +707,7 @@ describe ProcedureRevision do
end
describe 'caching behavior' do
let(:procedure) { create(:procedure, :published, types_de_champ: types_de_champ) }
let(:procedure) { create(:procedure, :published, types_de_champ_public: types_de_champ_public) }
before { Rails.cache = ActiveSupport::Cache::MemoryStore.new }
after { Rails.cache = ActiveSupport::Cache::NullStore.new }

View file

@ -1275,6 +1275,115 @@ describe Procedure do
it { expect(build(:procedure, lien_dpo: 'askjdlad l akdj asd ').valid?).to be(false) }
end
describe 'factory' do
let(:types_de_champ) { [{ type: :yes_no }, { type: :integer_number }] }
context 'create' do
let(:types_de_champ) { [{ type: :yes_no }, { type: :repetition, children: [{ type: :integer_number }] }] }
let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ) }
context 'with brouillon procedure' do
it do
expect(procedure.draft_revision.types_de_champ_public.count).to eq(2)
expect(procedure.draft_revision.types_de_champ.count).to eq(3)
end
end
context 'with published procedure' do
let(:procedure) { create(:procedure, :published, types_de_champ_public: types_de_champ) }
it do
expect(procedure.draft_revision.types_de_champ_public.count).to eq(2)
expect(procedure.draft_revision.types_de_champ.count).to eq(3)
expect(procedure.published_revision.types_de_champ_public.count).to eq(2)
expect(procedure.published_revision.types_de_champ.count).to eq(3)
end
end
end
context 'with bouillon procedure' do
let(:procedure) { build(:procedure, types_de_champ_public: types_de_champ, types_de_champ_private: types_de_champ) }
it do
expect(procedure.revisions.size).to eq(1)
expect(procedure.draft_revision.types_de_champ.size).to eq(4)
expect(procedure.draft_revision.types_de_champ_public.size).to eq(2)
expect(procedure.published_revision).to be_nil
end
end
context 'with published procedure' do
let(:procedure) { build(:procedure, :published, types_de_champ_public: types_de_champ, types_de_champ_private: types_de_champ) }
it do
expect(procedure.revisions.size).to eq(2)
expect(procedure.draft_revision.types_de_champ.size).to eq(4)
expect(procedure.draft_revision.types_de_champ_public.size).to eq(2)
expect(procedure.published_revision.types_de_champ.size).to eq(4)
expect(procedure.published_revision.types_de_champ_public.size).to eq(2)
end
end
context 'repetition' do
let(:types_de_champ) do
[
{ type: :yes_no },
{
type: :repetition,
children: [
{ libelle: 'Nom', mandatory: true },
{ libelle: 'Prénom', mandatory: true },
{ libelle: 'Age', type: :integer_number }
]
}
]
end
let(:revision) { procedure.draft_revision }
let(:repetition) { revision.revision_types_de_champ_public.last }
context 'with bouillon procedure' do
let(:procedure) { build(:procedure, types_de_champ_public: types_de_champ) }
it do
expect(revision.types_de_champ.size).to eq(5)
expect(revision.types_de_champ_public.size).to eq(2)
expect(revision.types_de_champ_public.map(&:type_champ)).to eq(['yes_no', 'repetition'])
expect(repetition.revision_types_de_champ.size).to eq(3)
expect(repetition.revision_types_de_champ.map(&:type_champ)).to eq(['text', 'text', 'integer_number'])
expect(repetition.revision_types_de_champ.map(&:mandatory?)).to eq([true, true, false])
end
end
context 'with published procedure' do
let(:procedure) { build(:procedure, :published, types_de_champ_public: types_de_champ) }
context 'draft revision' do
it do
expect(revision.types_de_champ.size).to eq(5)
expect(revision.types_de_champ_public.size).to eq(2)
expect(revision.types_de_champ_public.map(&:type_champ)).to eq(['yes_no', 'repetition'])
expect(repetition.revision_types_de_champ.size).to eq(3)
expect(repetition.revision_types_de_champ.map(&:type_champ)).to eq(['text', 'text', 'integer_number'])
expect(repetition.revision_types_de_champ.map(&:mandatory?)).to eq([true, true, false])
end
end
context 'published revision' do
let(:revision) { procedure.published_revision }
it do
expect(revision.types_de_champ.size).to eq(5)
expect(revision.types_de_champ_public.size).to eq(2)
expect(revision.types_de_champ_public.map(&:type_champ)).to eq(['yes_no', 'repetition'])
expect(repetition.revision_types_de_champ.size).to eq(3)
expect(repetition.revision_types_de_champ.map(&:type_champ)).to eq(['text', 'text', 'integer_number'])
expect(repetition.revision_types_de_champ.map(&:mandatory?)).to eq([true, true, false])
end
end
end
end
end
private
def create_dossier_with_pj_of_size(size, procedure)