Merge pull request #10569 from demarches-simplifiees/refactor-remove-champ-factories

refactor: remove champ factories
This commit is contained in:
Paul Chavard 2024-07-22 13:56:55 +00:00 committed by GitHub
commit bf1a057ac3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
120 changed files with 1144 additions and 1540 deletions

View file

@ -476,7 +476,7 @@ module Administrateurs
procedures_result = procedures_result.where(aasm_state: filter.statuses) if filter.statuses.present?
procedures_result = procedures_result.where("tags @> ARRAY[?]::text[]", filter.tags) if filter.tags.present?
procedures_result = procedures_result.where(template: true) if filter.template?
procedures_result = procedures_result.where('published_at >= ?', filter.from_publication_date) if filter.from_publication_date.present?
procedures_result = procedures_result.where(published_at: filter.from_publication_date..) if filter.from_publication_date.present?
procedures_result = procedures_result.where(service: service) if filter.service_siret.present?
procedures_result = procedures_result.where(service: services) if services
procedures_result = procedures_result.where(for_individual: filter.for_individual) if filter.for_individual.present?

View file

@ -19,9 +19,7 @@ module Loaders
private
def query(keys)
::Champ.where(@where)
.includes(:type_de_champ)
.where(types_de_champ: { stable_id: keys })
::Champ.where(@where).where(stable_id: keys)
end
end
end

View file

@ -7,10 +7,10 @@ class Migrations::BackfillDossierRepetitionJob < ApplicationJob
.revision
.types_de_champ
.filter do |type_de_champ|
type_de_champ.type_champ == 'repetition' && dossier.champs.none? { _1.type_de_champ_id == type_de_champ.id }
type_de_champ.type_champ == 'repetition' && dossier.champs.none? { _1.stable_id == type_de_champ.stable_id }
end
.each do |type_de_champ|
dossier.champs << type_de_champ.champ.build
dossier.champs << type_de_champ.build_champ
end
end
end

View file

@ -1,69 +0,0 @@
class Recovery::AlignChampWithDossierRevision
def initialize(dossiers, progress: nil)
@dossiers = dossiers
@progress = progress
@logs = []
end
attr_reader :logs
def run(destroy_extra_champs: false)
@logs = []
bad_dossier_ids = find_broken_dossier_ids
Dossier
.where(id: bad_dossier_ids)
.includes(:procedure, champs: { type_de_champ: :revisions })
.find_each do |dossier|
bad_champs = dossier.champs.filter { !dossier.revision_id.in?(_1.type_de_champ.revisions.ids) }
bad_champs.each do |champ|
type_de_champ = dossier.revision.types_de_champ.find { _1.stable_id == champ.stable_id }
state = {
champ_id: champ.id,
champ_type_de_champ_id: champ.type_de_champ_id,
dossier_id: dossier.id,
dossier_revision_id: dossier.revision_id,
procedure_id: dossier.procedure.id
}
if type_de_champ.present?
logs << state.merge(status: :updated, type_de_champ_id: type_de_champ.id)
champ.update_column(:type_de_champ_id, type_de_champ.id)
else
logs << state.merge(status: :not_found)
champ.destroy! if destroy_extra_champs
end
end
end
end
def find_broken_dossier_ids
bad_dossier_ids = []
@dossiers.in_batches(of: 15_000) do |dossiers|
dossier_ids_revision_ids = dossiers.pluck(:id, :revision_id)
dossier_ids = dossier_ids_revision_ids.map(&:first)
dossier_ids_type_de_champ_ids = Champ.where(dossier_id: dossier_ids).pluck(:dossier_id, :type_de_champ_id)
type_de_champ_ids = dossier_ids_type_de_champ_ids.map(&:second).uniq
revision_ids_by_type_de_champ_id = ProcedureRevisionTypeDeChamp
.where(type_de_champ_id: type_de_champ_ids)
.pluck(:type_de_champ_id, :revision_id)
.group_by(&:first).transform_values { _1.map(&:second).uniq }
type_de_champ_ids_by_dossier_id = dossier_ids_type_de_champ_ids
.group_by(&:first)
.transform_values { _1.map(&:second).uniq }
bad_dossier_ids += dossier_ids_revision_ids.filter do |(dossier_id, revision_id)|
type_de_champ_ids_by_dossier_id.fetch(dossier_id, []).any? do |type_de_champ_id|
!revision_id.in?(revision_ids_by_type_de_champ_id.fetch(type_de_champ_id, []))
end
end.map(&:first)
@progress.inc(dossiers.count) if @progress
end
@progress.finish if @progress
bad_dossier_ids
end
end

View file

@ -2,8 +2,9 @@ class Champ < ApplicationRecord
include ChampConditionalConcern
include ChampsValidateConcern
self.ignored_columns += [:type_de_champ_id]
belongs_to :dossier, inverse_of: false, touch: true, optional: false
belongs_to :type_de_champ, inverse_of: :champ, optional: false
belongs_to :parent, class_name: 'Champ', optional: true
has_many_attached :piece_justificative_file
@ -15,6 +16,12 @@ class Champ < ApplicationRecord
delegate :procedure, to: :dossier
def type_de_champ
@type_de_champ ||= dossier.revision
.types_de_champ
.find(-> { raise "Type De Champ #{stable_id} not found in Revision #{dossier.revision_id}" }) { _1.stable_id == stable_id }
end
delegate :libelle,
:type_champ,
:description,
@ -222,7 +229,7 @@ class Champ < ApplicationRecord
end
def clone(fork = false)
champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_id, :stable_id, :stream]
champ_attributes = [:parent_id, :private, :row_id, :type, :stable_id, :stream]
value_attributes = fork || !private? ? [:value, :value_json, :data, :external_id] : []
relationships = fork || !private? ? [:etablissement, :geo_areas] : []

View file

@ -12,13 +12,11 @@ module ChampsValidateConcern
private
def validate_champ_value?
return false unless visible?
case validation_context
when :champs_public_value
public?
public? && visible?
when :champs_private_value
private?
private? && visible?
else
false
end

View file

@ -97,7 +97,7 @@ module DossierChampsConcern
attributes = type_de_champ.params_for_champ
# TODO: Once we have the right index in place, we should change this to use `create_or_find_by` instead of `find_or_create_by`
champ = champs
.create_with(type_de_champ:, **attributes)
.create_with(**attributes)
.find_or_create_by!(stable_id: type_de_champ.stable_id, row_id:)
attributes[:id] = champ.id
@ -113,7 +113,7 @@ module DossierChampsConcern
parent = revision.parent_of(type_de_champ)
if parent.present?
attributes[:parent] = champs.find { _1.type_de_champ_id == parent.id }
attributes[:parent] = champs.find { _1.stable_id == parent.stable_id }
else
attributes[:parent] = nil
end

View file

@ -50,7 +50,7 @@ module DossierRebaseConcern
# index published types de champ coordinates by stable_id
target_coordinates_by_stable_id = target_revision
.revision_types_de_champ
.includes(:type_de_champ, :parent)
.includes(:parent)
.index_by(&:stable_id)
changes_by_op = pending_changes
@ -58,7 +58,6 @@ module DossierRebaseConcern
.tap { _1.default = [] }
champs_by_stable_id = champs
.includes(:type_de_champ)
.group_by(&:stable_id)
.transform_values { Champ.where(id: _1) }
.tap { _1.default = Champ.none }
@ -78,14 +77,6 @@ module DossierRebaseConcern
# update champ
changes_by_op[:update].each { apply(_1, champs_by_stable_id[_1.stable_id]) }
# due to repetition tdc clone on update or erase
# we must reassign tdc to the latest version
champs_by_stable_id.each do |stable_id, champs|
if target_coordinates_by_stable_id[stable_id].present? && champs.present?
champs.update_all(type_de_champ_id: target_coordinates_by_stable_id[stable_id].type_de_champ_id)
end
end
# update dossier revision
update_column(:revision_id, target_revision.id)
end
@ -134,7 +125,7 @@ module DossierRebaseConcern
champ_repetition.champs.map(&:row_id).uniq.each do |row_id|
champs << create_champ(target_coordinate, champ_repetition, row_id:)
end
elsif champ_repetition.mandatory?
elsif target_coordinate.parent.mandatory?
champs << create_champ(target_coordinate, champ_repetition, row_id: ULID.generate)
end
end

View file

@ -242,10 +242,7 @@ class Dossier < ApplicationRecord
scope :hidden_by_administration_since, -> (since) { where('dossiers.hidden_by_administration_at IS NOT NULL AND dossiers.hidden_by_administration_at >= ?', since) }
scope :hidden_since, -> (since) { hidden_by_user_since(since).or(hidden_by_administration_since(since)) }
scope :with_type_de_champ, -> (stable_id) {
joins('INNER JOIN champs ON champs.dossier_id = dossiers.id INNER JOIN types_de_champ ON types_de_champ.id = champs.type_de_champ_id')
.where(types_de_champ: { stable_id: })
}
scope :with_type_de_champ, -> (stable_id) { joins(:champs).where(champs: { stream: 'main', stable_id: }) }
scope :all_state, -> { not_archived.state_not_brouillon }
scope :en_construction, -> { not_archived.state_en_construction }
@ -271,20 +268,18 @@ class Dossier < ApplicationRecord
scope :with_followers, -> { left_outer_joins(:follows).where.not(follows: { id: nil }) }
scope :with_champs, -> {
includes(champs_public: [
:type_de_champ,
:geo_areas,
piece_justificative_file_attachments: :blob,
champs: [:type_de_champ, piece_justificative_file_attachments: :blob]
champs: [piece_justificative_file_attachments: :blob]
])
}
scope :brouillons_recently_updated, -> { updated_since(2.days.ago).state_brouillon.order_by_updated_at }
scope :with_annotations, -> {
includes(champs_private: [
:type_de_champ,
:geo_areas,
piece_justificative_file_attachments: :blob,
champs: [:type_de_champ, piece_justificative_file_attachments: :blob]
champs: [piece_justificative_file_attachments: :blob]
])
}
scope :for_api, -> {
@ -493,10 +488,10 @@ class Dossier < ApplicationRecord
end
def build_default_champs_for_new_dossier
revision.build_champs_public.each do |champ|
revision.build_champs_public(self).each do |champ|
champs_public << champ
end
revision.build_champs_private.each do |champ|
revision.build_champs_private(self).each do |champ|
champs_private << champ
end
champs_public.filter { _1.repetition? && _1.mandatory? }.each do |champ|

View file

@ -15,7 +15,7 @@ class DossierPreloader
def in_batches_with_block(size = DEFAULT_BATCH_SIZE, &block)
@dossiers.in_batches(of: size) do |batch|
data = Dossier.where(id: batch.ids).includes(:individual, :traitement, :etablissement, user: :france_connect_informations, avis: :expert, commentaires: [:instructeur, :expert], revision: :revision_types_de_champ)
data = Dossier.where(id: batch.ids).includes(:individual, :traitement, :etablissement, user: :france_connect_informations, avis: :expert, commentaires: [:instructeur, :expert])
dossiers = data.to_a
load_dossiers(dossiers)
@ -30,39 +30,32 @@ class DossierPreloader
end
def self.load_one(dossier, pj_template: false)
DossierPreloader.new([dossier]).all(pj_template: pj_template).first
DossierPreloader.new([dossier]).all(pj_template:).first
end
private
# returns: { revision_id : { type_de_champ_id : position } }
def revisions
@revisions ||= ProcedureRevision.where(id: @dossiers.pluck(:revision_id).uniq)
.includes(types_de_champ: { piece_justificative_template_attachment: :blob })
.index_by(&:id)
end
# returns: { revision_id : { stable_id : position } }
def positions
@positions ||= ProcedureRevisionTypeDeChamp
.where(revision_id: @dossiers.pluck(:revision_id).uniq)
.select(:revision_id, :type_de_champ_id, :position)
.group_by(&:revision_id)
.transform_values do |coordinates|
coordinates.index_by(&:type_de_champ_id).transform_values(&:position)
end
@positions ||= revisions
.transform_values { |revision| revision.revision_types_de_champ.map { [_1.stable_id, _1.position] }.to_h }
end
def load_dossiers(dossiers, pj_template: false)
to_include = @includes_for_champ.dup
to_include << [piece_justificative_file_attachments: :blob]
if pj_template
to_include << { type_de_champ: { piece_justificative_template_attachment: :blob } }
else
to_include << :type_de_champ
end
all_champs = Champ
.includes(to_include)
.where(dossier_id: dossiers)
.to_a
load_etablissements(all_champs)
children_champs, root_champs = all_champs.partition(&:child?)
champs_by_dossier = root_champs.group_by(&:dossier_id)
champs_by_dossier_by_parent = children_champs
@ -74,6 +67,8 @@ class DossierPreloader
dossiers.each do |dossier|
load_dossier(dossier, champs_by_dossier[dossier.id] || [], champs_by_dossier_by_parent[dossier.id] || {})
end
load_etablissements(all_champs)
end
def load_etablissements(champs)
@ -90,6 +85,11 @@ class DossierPreloader
end
def load_dossier(dossier, champs, children_by_parent = {})
revision = revisions[dossier.revision_id]
if revision.present?
dossier.association(:revision).target = revision
end
champs_public, champs_private = champs.partition(&:public?)
dossier.association(:champs).target = []
@ -121,8 +121,8 @@ class DossierPreloader
dossier.association(:champs).target += champs
parent.association(name).target = champs
.filter { positions[dossier.revision_id][_1.type_de_champ_id].present? }
.sort_by { [_1.row_id, positions[dossier.revision_id][_1.type_de_champ_id]] }
.filter { positions[dossier.revision_id][_1.stable_id].present? }
.sort_by { [_1.row_id, positions[dossier.revision_id][_1.stable_id]] }
# Load children champs
champs.filter(&:block?).each do |parent_champ|

View file

@ -41,7 +41,6 @@ class Logic::ChampValue < Logic::Term
return nil if !targeted_champ.visible?
return nil if targeted_champ.blank? & !targeted_champ.drop_down_other?
# on dépense 22ms ici, à cause du map, mais on doit pouvoir passer par un champ type
case targeted_champ.type
when "Champs::YesNoChamp",
"Champs::CheckboxChamp"

View file

@ -35,14 +35,14 @@ class ProcedureRevision < ApplicationRecord
serialize :ineligibilite_rules, LogicSerializer
def build_champs_public
def build_champs_public(dossier)
# reload: it can be out of sync in test if some tdcs are added wihtout using add_tdc
types_de_champ_public.reload.map(&:build_champ)
types_de_champ_public.reload.map { _1.build_champ(dossier:) }
end
def build_champs_private
def build_champs_private(dossier)
# reload: it can be out of sync in test if some tdcs are added wihtout using add_tdc
types_de_champ_private.reload.map(&:build_champ)
types_de_champ_private.reload.map { _1.build_champ(dossier:) }
end
def add_type_de_champ(params)

View file

@ -179,16 +179,6 @@ class TypeDeChamp < ApplicationRecord
.where(type_champ: [TypeDeChamp.type_champs.fetch(:text), TypeDeChamp.type_champs.fetch(:textarea)])
}
has_many :champ, inverse_of: :type_de_champ, dependent: :destroy do
def build(params = {})
super(params.merge(proxy_association.owner.params_for_champ))
end
def create(params = {})
super(params.merge(proxy_association.owner.params_for_champ))
end
end
has_one_attached :piece_justificative_template
validates :piece_justificative_template, size: { less_than: FILE_MAX_SIZE }, on: :update
validates :piece_justificative_template, content_type: AUTHORIZED_CONTENT_TYPES, on: :update
@ -257,7 +247,7 @@ class TypeDeChamp < ApplicationRecord
end
def build_champ(params = {})
champ.build(params)
self.class.type_champ_to_champ_class_name(type_champ).constantize.new(params_for_champ.merge(params))
end
def check_mandatory
@ -740,7 +730,6 @@ class TypeDeChamp < ApplicationRecord
return true if type_champ_to_champ_class_name(type_champ) != champ.type
# special case for linked drop down champ it's blank implementation is not what you think
return champ.value.blank? if type_champ == TypeDeChamp.type_champs.fetch(:linked_drop_down_list)
champ.blank?
end
end

View file

@ -1,19 +0,0 @@
namespace :after_party do
desc 'Deployment task: fix_champs_revisions'
task fix_champs_revisions: :environment do
puts "Running deploy task 'fix_champs_revisions'"
progress = ProgressReport.new(Dossier.count)
fixer = Recovery::AlignChampWithDossierRevision.new(Dossier, progress:)
fixer.run
fixer.logs.each do |log|
puts JSON.dump(log)
end
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end

View file

@ -1,5 +1,8 @@
RSpec.describe Attachment::EditComponent, type: :component do
let(:champ) { create(:champ_titre_identite, dossier: create(:dossier)) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :titre_identite }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:attached_file) { champ.piece_justificative_file }
let(:attachment) { attached_file.attachments.first }
let(:filename) { attachment.filename.to_s }
@ -98,8 +101,6 @@ RSpec.describe Attachment::EditComponent, type: :component do
end
context 'when watermark is pending' do
let(:champ) { create(:champ_titre_identite) }
it 'displays the filename, but doesnt allow to download the file' do
expect(attachment.watermark_pending?).to be_truthy
expect(subject).to have_text(filename)

View file

@ -1,5 +1,9 @@
RSpec.describe Attachment::MultipleComponent, type: :component do
let(:champ) { create(:champ_titre_identite) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :titre_identite }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:attached_file) { champ.piece_justificative_file }
let(:kwargs) { {} }
@ -14,14 +18,11 @@ RSpec.describe Attachment::MultipleComponent, type: :component do
subject { render_inline(component).to_html }
context 'when there is no attachment yet' do
let(:champ) { create(:champ_titre_identite, skip_default_attachment: true) }
let(:dossier) { create(:dossier, procedure:) }
it 'renders a form field for uploading a file' do
it 'renders a form field for uploading a file and max attachment size' do
expect(subject).to have_no_selector('.hidden input[type=file]')
expect(subject).to have_selector('input[type=file]:not(.hidden)')
end
it 'renders max size' do
expect(subject).to have_content(/Taille maximale :\s+20 Mo/)
end
end

View file

@ -3,15 +3,16 @@
require "rails_helper"
RSpec.describe Attachment::PendingPollComponent, type: :component do
let(:champ) { create(:champ_titre_identite) }
let(:attachment) { champ.piece_justificative_file.attachments.first }
let(:component) {
described_class.new(poll_url: "poll-here", attachment:)
}
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :titre_identite }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
subject {
render_inline(component).to_html
}
let(:attachment) { champ.piece_justificative_file.attachments.first }
let(:component) { described_class.new(poll_url: "poll-here", attachment:) }
subject { render_inline(component).to_html }
context "when watermark is pending" do
it "renders turbo poll attributes" do
@ -48,7 +49,8 @@ RSpec.describe Attachment::PendingPollComponent, type: :component do
end
context "when antivirus is in progress on pj" do
let(:champ) { create(:champ_piece_justificative) }
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
before do
attachment.blob.virus_scan_result = ActiveStorage::VirusScanner::PENDING
end

View file

@ -1,16 +1,14 @@
RSpec.describe Attachment::ShowComponent, type: :component do
let(:champ) { create(:champ_piece_justificative) }
let(:virus_scan_result) { nil }
let(:attachment) {
champ.piece_justificative_file.attachments.first
}
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:attachment) { champ.piece_justificative_file.attachments.first }
let(:filename) { attachment.filename.to_s }
let(:component) do
described_class.new(attachment:)
end
let(:virus_scan_result) { nil }
let(:component) { described_class.new(attachment:) }
subject { render_inline(component).to_html }

View file

@ -1,25 +1,29 @@
describe EditableChamp::DatetimeComponent, type: :component do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :datetime, stable_id: 99 }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:component) {
described_class.new(form: instance_double(ActionView::Helpers::FormBuilder, object_name: "dossier[champs_public_attributes]"), champ:)
}
describe '#formatted_value_for_datetime_locale' do
# before { champ.validate(:prefill) }
subject { component.formatted_value_for_datetime_locale }
context 'when the value is nil' do
let(:champ) { create(:champ_datetime, dossier: create(:dossier), value: nil) }
let(:champ) { Champs::DatetimeChamp.new(value: nil, dossier:, stable_id: 99) }
it { is_expected.to be_nil }
end
context 'when the value is not a valid datetime' do
let(:champ) { create(:champ_datetime, dossier: create(:dossier), value: 'invalid') }
let(:champ) { Champs::DatetimeChamp.new(value: 'invalid', dossier:, stable_id: 99) }
it { is_expected.to be_nil }
end
context 'when the value is a valid datetime' do
let(:champ) { create(:champ_datetime, dossier: create(:dossier), value: '2020-01-01T00:00:00+01:00') }
let(:champ) { Champs::DatetimeChamp.new(value: '2020-01-01T00:00:00+01:00', dossier:, stable_id: 99) }
it { is_expected.to eq('2020-01-01T00:00') }
end

View file

@ -1,71 +1,60 @@
describe EditableChamp::EditableChampComponent, type: :component do
let(:component) { described_class.new(form: nil, champ: champ) }
let(:procedure) { create(:procedure, types_de_champ_public:, types_de_champ_private:) }
let(:types_de_champ_public) { [] }
let(:types_de_champ_private) { [] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:component) { described_class.new(form: nil, champ:) }
describe "editable_champ_controller" do
let(:dossier) { create(:dossier) }
let(:champ) { create(:champ, dossier: dossier) }
let(:controllers) { [] }
let(:data) { controllers.join(' ') }
subject { component.send(:stimulus_controller) }
context 'when an editable champ' do
context 'when an editable public champ' do
let(:controllers) { ['autosave'] }
let(:types_de_champ_public) { [{ type: :text }] }
it { expect(subject).to eq(data) }
end
context 'when a repetition champ' do
let(:champ) { create(:champ_repetition, dossier: dossier) }
let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :text }] }] }
it { expect(subject).to eq(nil) }
end
context 'when a carte champ' do
let(:champ) { create(:champ_carte, dossier: dossier) }
let(:types_de_champ_public) { [{ type: :carte }] }
it { expect(subject).to eq(nil) }
end
context 'when a private champ' do
let(:champ) { create(:champ, dossier: dossier, private: true) }
let(:types_de_champ_private) { [{ type: :text }] }
it { expect(subject).to eq('autosave') }
end
context 'when a dossier is en_construction' do
let(:controllers) { ['autosave'] }
let(:dossier) { create(:dossier, :en_construction) }
it { expect(subject).to eq(data) }
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
context 'when a public dropdown champ' do
let(:controllers) { ['autosave'] }
let(:champ) { create(:champ_drop_down_list, dossier: dossier) }
let(:types_de_champ_public) { [{ type: :drop_down_list }] }
it { expect(subject).to eq(data) }
end
context 'when a private dropdown champ' do
let(:controllers) { ['autosave'] }
let(:champ) { create(:champ_drop_down_list, dossier: dossier, private: true) }
let(:types_de_champ_private) { [{ type: :drop_down_list }] }
it { expect(subject).to eq(data) }
end
end
context 'when a public dropdown champ' do
let(:controllers) { ['autosave'] }
let(:champ) { create(:champ_drop_down_list, dossier: dossier) }
it { expect(subject).to eq(data) }
end
context 'when a private dropdown champ' do
let(:controllers) { ['autosave'] }
let(:champ) { create(:champ_drop_down_list, dossier: dossier, private: true) }
it { expect(subject).to eq(data) }
end
end
end

View file

@ -1,22 +1,24 @@
describe EditableChamp::ExplicationComponent, type: :component do
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:component) {
described_class.new(form: instance_double(ActionView::Helpers::FormBuilder, object_name: "dossier[champs_public_attributes]"), champ:)
}
let(:champ) { create(:champ_explication) }
describe 'no description' do
let(:types_de_champ_public) { [{ type: :explication }] }
subject { render_inline(component).to_html }
it { is_expected.not_to have_button("Lire plus") }
end
describe 'collapsed text is collapsed' do
subject { render_inline(component).to_html }
let(:types_de_champ_public) { [{ type: :explication, collapsible_explanation_enabled: "1", collapsible_explanation_text: "hide me" }] }
before do
champ.type_de_champ.update!(collapsible_explanation_enabled: "1", collapsible_explanation_text: "hide me")
end
subject { render_inline(component).to_html }
it { is_expected.to have_button("Lire plus") }
it { is_expected.to have_selector(".fr-collapse", text: "hide me") }

View file

@ -1,5 +1,9 @@
describe EditableChamp::PieceJustificativeComponent, type: :component do
let(:champ) { create(:champ_piece_justificative, dossier: create(:dossier)) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:component) {
described_class.new(form: instance_double(ActionView::Helpers::FormBuilder, object_name: "dossier[champs_public_attributes]"), champ:)
}
@ -9,7 +13,6 @@ describe EditableChamp::PieceJustificativeComponent, type: :component do
}
context 'when there is a template' do
let(:template) { champ.type_de_champ.piece_justificative_template }
let(:profil) { :user }
before do

View file

@ -2,6 +2,7 @@ RSpec.describe TypesDeChampEditor::HeaderSectionComponent, type: :component do
include ActionView::Context
include ActionView::Helpers::FormHelper
include ActionView::Helpers::FormOptionsHelper
let(:procedure) { create(:procedure, types_de_champ_public:) }
let(:component) do
cmp = nil
@ -14,8 +15,8 @@ RSpec.describe TypesDeChampEditor::HeaderSectionComponent, type: :component do
describe 'header_section_options_for_select' do
context 'without upper tdc' do
let(:tdc) { header.type_de_champ }
let(:header) { build(:champ_header_section) }
let(:types_de_champ_public) { [{ type: :header_section, level: 1 }] }
let(:tdc) { procedure.draft_revision.types_de_champ_public.first }
let(:upper_tdcs) { [] }
it 'allows up to level 1 header section' do
@ -24,9 +25,14 @@ RSpec.describe TypesDeChampEditor::HeaderSectionComponent, type: :component do
end
context 'with upper tdc of level 1' do
let(:tdc) { header.type_de_champ }
let(:header) { build(:champ_header_section_level_1) }
let(:upper_tdcs) { [build(:champ_header_section_level_1).type_de_champ] }
let(:types_de_champ_public) do
[
{ type: :header_section, level: 1 },
{ type: :header_section, level: 2 }
]
end
let(:tdc) { procedure.draft_revision.types_de_champ_public.last }
let(:upper_tdcs) { [procedure.draft_revision.types_de_champ_public.first] }
it 'allows up to level 2 header section' do
expect(subject).to have_selector("option", count: 2)
@ -34,34 +40,24 @@ RSpec.describe TypesDeChampEditor::HeaderSectionComponent, type: :component do
end
context 'with upper tdc of level 2' do
let(:tdc) { header.type_de_champ }
let(:header) { build(:champ_header_section_level_1) }
let(:upper_tdcs) { [build(:champ_header_section_level_1), build(:champ_header_section_level_2)].map(&:type_de_champ) }
let(:types_de_champ_public) do
[
{ type: :header_section, level: 1 },
{ type: :header_section, level: 2 },
{ type: :header_section, level: 3 }
]
end
let(:tdc) { procedure.draft_revision.types_de_champ_public.third }
let(:upper_tdcs) { [procedure.draft_revision.types_de_champ_public.first, procedure.draft_revision.types_de_champ_public.second] }
it 'allows up to level 3 header section' do
expect(subject).to have_selector("option", count: 3)
end
end
context 'with upper tdc of level 3' do
let(:tdc) { header.type_de_champ }
let(:header) { build(:champ_header_section_level_1) }
let(:upper_tdcs) do
[
build(:champ_header_section_level_1),
build(:champ_header_section_level_2),
build(:champ_header_section_level_3)
].map(&:type_de_champ)
end
it 'reaches limit of at most 3 section level' do
expect(subject).to have_selector("option", count: 3)
end
end
context 'with error' do
let(:tdc) { header.type_de_champ }
let(:header) { build(:champ_header_section_level_2) }
let(:types_de_champ_public) { [{ type: :header_section, level: 2 }] }
let(:tdc) { procedure.draft_revision.types_de_champ_public.first }
let(:upper_tdcs) { [] }
it 'includes disabled levels' do
@ -72,8 +68,8 @@ RSpec.describe TypesDeChampEditor::HeaderSectionComponent, type: :component do
end
describe 'errors' do
let(:tdc) { header.type_de_champ }
let(:header) { build(:champ_header_section_level_2) }
let(:types_de_champ_public) { [{ type: :header_section, level: 2 }] }
let(:tdc) { procedure.draft_revision.types_de_champ_public.first }
let(:upper_tdcs) { [] }
it 'returns errors' do

View file

@ -208,6 +208,6 @@ RSpec.describe API::Public::V1::DossiersController, type: :controller do
private
def find_champ_by_stable_id(dossier, stable_id)
dossier.champs.joins(:type_de_champ).find_by(types_de_champ: { stable_id: stable_id })
dossier.champs.find_by(stable_id:)
end
end

View file

@ -3,10 +3,11 @@ describe API::V2::GraphqlController do
let(:generated_token) { APIToken.generate(admin) }
let(:api_token) { generated_token.first }
let(:token) { generated_token.second }
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, administrateurs: [admin]) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure: procedure, en_construction_at: 1.day.ago) }
let(:dossier2) { create(:dossier, :en_construction, :with_individual, :archived, procedure: procedure, en_construction_at: 3.days.ago) }
let(:types_de_champ_public) { [] }
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, administrateurs: [admin], types_de_champ_public:) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, :with_populated_champs, procedure:) }
let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure:, en_construction_at: 1.day.ago) }
let(:dossier2) { create(:dossier, :en_construction, :with_individual, :archived, procedure:, en_construction_at: 3.days.ago) }
let(:dossiers) { [dossier] }
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
@ -806,7 +807,8 @@ describe API::V2::GraphqlController do
end
describe "champ piece_justificative" do
let(:champ) { create(:champ_piece_justificative, dossier: dossier) }
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:champ) { dossier.champs.first }
let(:byte_size) { 2712286911 }
context "with deprecated file field" do

View file

@ -1,8 +1,9 @@
describe AttachmentsController, type: :controller do
let(:user) { create(:user) }
let(:attachment) { champ.piece_justificative_file.attachments.first }
let(:dossier) { create(:dossier, user: user) }
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) }
let(:dossier) { create(:dossier, :with_populated_champs, user:, procedure:) }
let(:champ) { dossier.champs.first }
let(:signed_id) { attachment.blob.signed_id }
describe '#show' do
@ -46,8 +47,6 @@ describe AttachmentsController, type: :controller do
render_views
let(:attachment) { champ.piece_justificative_file.attachments.first }
let(:dossier) { create(:dossier, user: user) }
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id) }
let(:signed_id) { attachment.blob.signed_id }
subject do

View file

@ -8,7 +8,8 @@ describe Experts::AvisController, type: :controller do
let(:another_instructeur) { create(:instructeur) }
let(:claimant) { create(:expert) }
let(:expert) { create(:expert) }
let(:procedure) { create(:procedure, :published, instructeurs: [instructeur, another_instructeur, instructeur_with_instant_avis_notification]) }
let(:types_de_champ_public) { [] }
let(:procedure) { create(:procedure, :published, instructeurs: [instructeur, another_instructeur, instructeur_with_instant_avis_notification], types_de_champ_public:) }
let(:procedure_id) { procedure.id }
let(:another_procedure) { create(:procedure, :published, instructeurs: [instructeur]) }
let(:dossier) { create(:dossier, :en_construction, procedure:) }
@ -464,7 +465,8 @@ describe Experts::AvisController, type: :controller do
end
context 'with linked dossiers' do
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) }
let(:types_de_champ_public) { [{ type: :dossier_link }] }
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
context 'when the expert doesnt share linked dossiers' do
let(:invite_linked_dossiers) { false }

View file

@ -4,7 +4,8 @@ describe Instructeurs::DossiersController, type: :controller do
let(:instructeur) { create(:instructeur) }
let(:administration) { create(:administration) }
let(:instructeurs) { [instructeur] }
let(:procedure) { create(:procedure, :published, :for_individual, instructeurs: instructeurs) }
let(:types_de_champ_public) { [] }
let(:procedure) { create(:procedure, :published, :for_individual, instructeurs: instructeurs, types_de_champ_public:) }
let(:procedure_accuse_lecture) { create(:procedure, :published, :for_individual, :accuse_lecture, :new_administrateur, instructeurs: instructeurs) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:dossier_accuse_lecture) { create(:dossier, :en_construction, :with_individual, procedure: procedure_accuse_lecture) }
@ -854,7 +855,8 @@ describe Instructeurs::DossiersController, type: :controller do
context 'with linked dossiers' do
let(:asked_confidentiel) { false }
let(:previous_avis_confidentiel) { false }
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) }
let(:types_de_champ_public) { [{ type: :dossier_link }] }
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
before { subject }
context 'when the expert doesnt share linked dossiers' do
let(:invite_linked_dossiers) { false }
@ -873,7 +875,7 @@ describe Instructeurs::DossiersController, type: :controller do
context 'and the expert can access the linked dossiers' do
let(:saved_avis) { Avis.last(2).first }
let(:linked_avis) { Avis.last }
let(:linked_dossier) { Dossier.find_by(id: dossier.reload.champs_public.filter(&:dossier_link?).filter_map(&:value)) }
let(:linked_dossier) { Dossier.find_by(id: dossier.champs.first.value) }
let(:invite_linked_dossiers) do
instructeur.assign_to_procedure(linked_dossier.procedure)
true

View file

@ -159,7 +159,7 @@ describe Users::CommencerController, type: :controller do
expect(Dossier.count).to eq(1)
expect(session[:prefill_token]).to eq(Dossier.last.prefill_token)
expect(session[:prefill_params_digest]).to eq(PrefillChamps.digest({ "champ_#{type_de_champ_text.to_typed_id}" => "blabla" }))
expect(Dossier.last.champs.where(type_de_champ: type_de_champ_text).first.value).to eq("blabla")
expect(Dossier.last.champs.where(stable_id: type_de_champ_text.stable_id).first.value).to eq("blabla")
expect(Dossier.last.individual.nom).to eq("Dupont")
end
end
@ -190,8 +190,8 @@ describe Users::CommencerController, type: :controller do
subject { get :commencer, params: { path: path, prefill_token: "token", "champ_#{type_de_champ_text.to_typed_id}" => "blabla" } }
context "when the associated dossier exists" do
let!(:dossier) { create(:dossier, :prefilled, prefill_token: "token") }
let!(:champ_text) { create(:champ_text, dossier: dossier, type_de_champ: type_de_champ_text) }
let(:procedure) { create(:procedure, types_de_champ_public: [{}]) }
let!(:dossier) { create(:dossier, :prefilled, procedure:, prefill_token: "token") }
it "does not create a new dossier" do
subject

View file

@ -1,174 +1,110 @@
FactoryBot.define do
factory :champ do
factory :champ_do_not_use, class: 'Champ' do
stream { 'main' }
add_attribute(:private) { false }
dossier { association :dossier }
type_de_champ { association :type_de_champ, procedure: dossier.procedure }
after(:build) do |champ, _evaluator|
champ.stable_id = champ.type_de_champ.stable_id
end
trait :private do
add_attribute(:private) { true }
end
trait :with_piece_justificative_file do
after(:build) do |champ, _evaluator|
champ.piece_justificative_file.attach(
io: StringIO.new("toto"),
filename: "toto.txt",
content_type: "text/plain",
# we don't want to run virus scanner on this file
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
)
end
end
factory :champ_text, class: 'Champs::TextChamp' do
type_de_champ { association :type_de_champ_text, procedure: dossier.procedure }
factory :champ_do_not_use_text, class: 'Champs::TextChamp' do
value { 'text' }
end
factory :champ_textarea, class: 'Champs::TextareaChamp' do
type_de_champ { association :type_de_champ_textarea, procedure: dossier.procedure }
factory :champ_do_not_use_textarea, class: 'Champs::TextareaChamp' do
value { 'textarea' }
end
factory :champ_date, class: 'Champs::DateChamp' do
type_de_champ { association :type_de_champ_date, procedure: dossier.procedure }
factory :champ_do_not_use_date, class: 'Champs::DateChamp' do
value { '2019-07-10' }
end
factory :champ_datetime, class: 'Champs::DatetimeChamp' do
type_de_champ { association :type_de_champ_datetime, procedure: dossier.procedure }
factory :champ_do_not_use_datetime, class: 'Champs::DatetimeChamp' do
value { '15/09/1962 15:35' }
end
factory :champ_number, class: 'Champs::NumberChamp' do
type_de_champ { association :type_de_champ_number, procedure: dossier.procedure }
factory :champ_do_not_use_number, class: 'Champs::NumberChamp' do
value { '42' }
end
factory :champ_decimal_number, class: 'Champs::DecimalNumberChamp' do
type_de_champ { association :type_de_champ_decimal_number, procedure: dossier.procedure }
factory :champ_do_not_use_decimal_number, class: 'Champs::DecimalNumberChamp' do
value { '42.1' }
end
factory :champ_integer_number, class: 'Champs::IntegerNumberChamp' do
type_de_champ { association :type_de_champ_integer_number, procedure: dossier.procedure }
factory :champ_do_not_use_integer_number, class: 'Champs::IntegerNumberChamp' do
value { '42' }
end
factory :champ_checkbox, class: 'Champs::CheckboxChamp' do
type_de_champ { association :type_de_champ_checkbox, procedure: dossier.procedure }
factory :champ_do_not_use_checkbox, class: 'Champs::CheckboxChamp' do
value { 'true' }
end
factory :champ_civilite, class: 'Champs::CiviliteChamp' do
type_de_champ { association :type_de_champ_civilite, procedure: dossier.procedure }
factory :champ_do_not_use_civilite, class: 'Champs::CiviliteChamp' do
value { 'M.' }
end
factory :champ_email, class: 'Champs::EmailChamp' do
type_de_champ { association :type_de_champ_email, procedure: dossier.procedure }
factory :champ_do_not_use_email, class: 'Champs::EmailChamp' do
value { 'yoda@beta.gouv.fr' }
end
factory :champ_phone, class: 'Champs::PhoneChamp' do
type_de_champ { association :type_de_champ_phone, procedure: dossier.procedure }
factory :champ_do_not_use_phone, class: 'Champs::PhoneChamp' do
value { '0666666666' }
end
factory :champ_address, class: 'Champs::AddressChamp' do
type_de_champ { association :type_de_champ_address, procedure: dossier.procedure }
factory :champ_do_not_use_address, class: 'Champs::AddressChamp' do
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 }
factory :champ_do_not_use_yes_no, class: 'Champs::YesNoChamp' do
value { 'true' }
end
factory :champ_drop_down_list, class: 'Champs::DropDownListChamp' do
factory :champ_do_not_use_drop_down_list, class: 'Champs::DropDownListChamp' do
transient do
other { false }
end
type_de_champ { association :type_de_champ_drop_down_list, procedure: dossier.procedure, drop_down_other: other }
value { 'val1' }
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 }
factory :champ_do_not_use_multiple_drop_down_list, class: 'Champs::MultipleDropDownListChamp' do
value { '["val1", "val2"]' }
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 }
factory :champ_do_not_use_linked_drop_down_list, class: 'Champs::LinkedDropDownListChamp' do
value { '["categorie 1", "choix 1"]' }
end
factory :champ_pays, class: 'Champs::PaysChamp' do
type_de_champ { association :type_de_champ_pays, procedure: dossier.procedure }
factory :champ_do_not_use_pays, class: 'Champs::PaysChamp' do
value { 'France' }
end
factory :champ_regions, class: 'Champs::RegionChamp' do
type_de_champ { association :type_de_champ_regions, procedure: dossier.procedure }
factory :champ_do_not_use_regions, class: 'Champs::RegionChamp' do
value { 'Guadeloupe' }
end
factory :champ_departements, class: 'Champs::DepartementChamp' do
type_de_champ { association :type_de_champ_departements, procedure: dossier.procedure }
factory :champ_do_not_use_departements, class: 'Champs::DepartementChamp' do
value { '01' }
end
factory :champ_communes, class: 'Champs::CommuneChamp' do
type_de_champ { association :type_de_champ_communes, procedure: dossier.procedure }
factory :champ_do_not_use_communes, class: 'Champs::CommuneChamp' do
external_id { '60172' }
code_postal { '60580' }
end
factory :champ_epci, class: 'Champs::EpciChamp' do
type_de_champ { association :type_de_champ_epci, procedure: dossier.procedure }
factory :champ_do_not_use_epci, class: 'Champs::EpciChamp' do
value { 'CC Retz en Valois' }
external_id { '200071991' }
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_header_section_level_1, class: 'Champs::HeaderSectionChamp' do
type_de_champ { association :type_de_champ_header_section_level_1, procedure: dossier.procedure }
value { 'une section' }
end
factory :champ_header_section_level_2, class: 'Champs::HeaderSectionChamp' do
type_de_champ { association :type_de_champ_header_section_level_2, procedure: dossier.procedure }
value { 'une section' }
end
factory :champ_header_section_level_3, class: 'Champs::HeaderSectionChamp' do
type_de_champ { association :type_de_champ_header_section_level_3, procedure: dossier.procedure }
factory :champ_do_not_use_header_section, class: 'Champs::HeaderSectionChamp' do
value { 'une section' }
end
factory :champ_explication, class: 'Champs::ExplicationChamp' do
type_de_champ { association :type_de_champ_explication, procedure: dossier.procedure }
factory :champ_do_not_use_explication, class: 'Champs::ExplicationChamp' do
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 }
factory :champ_do_not_use_dossier_link, class: 'Champs::DossierLinkChamp' do
value { create(:dossier, :en_construction).id }
end
factory :champ_without_piece_justificative, class: 'Champs::PieceJustificativeChamp' do
type_de_champ { association :type_de_champ_piece_justificative, procedure: dossier.procedure }
end
factory :champ_piece_justificative, class: 'Champs::PieceJustificativeChamp' do
type_de_champ { association :type_de_champ_piece_justificative, procedure: dossier.procedure }
factory :champ_do_not_use_piece_justificative, class: 'Champs::PieceJustificativeChamp' do
transient do
size { 4 }
end
@ -184,8 +120,7 @@ FactoryBot.define do
end
end
factory :champ_titre_identite, class: 'Champs::TitreIdentiteChamp' do
type_de_champ { association :type_de_champ_titre_identite, procedure: dossier.procedure }
factory :champ_do_not_use_titre_identite, class: 'Champs::TitreIdentiteChamp' do
transient do
skip_default_attachment { false }
end
@ -203,109 +138,67 @@ FactoryBot.define do
end
end
factory :champ_carte, class: 'Champs::CarteChamp' do
type_de_champ { association :type_de_champ_carte, procedure: dossier.procedure }
factory :champ_do_not_use_carte, class: 'Champs::CarteChamp' do
geo_areas { build_list(:geo_area, 2) }
end
factory :champ_iban, class: 'Champs::IbanChamp' do
type_de_champ { association :type_de_champ_iban, procedure: dossier.procedure }
factory :champ_do_not_use_iban, class: 'Champs::IbanChamp' do
end
factory :champ_annuaire_education, class: 'Champs::AnnuaireEducationChamp' do
type_de_champ { association :type_de_champ_annuaire_education, procedure: dossier.procedure }
factory :champ_do_not_use_annuaire_education, class: 'Champs::AnnuaireEducationChamp' do
end
factory :champ_cnaf, class: 'Champs::CnafChamp' do
type_de_champ { association :type_de_champ_cnaf, procedure: dossier.procedure }
factory :champ_do_not_use_cnaf, class: 'Champs::CnafChamp' do
end
factory :champ_dgfip, class: 'Champs::DgfipChamp' do
type_de_champ { association :type_de_champ_dgfip, procedure: dossier.procedure }
factory :champ_do_not_use_dgfip, class: 'Champs::DgfipChamp' do
end
factory :champ_pole_emploi, class: 'Champs::PoleEmploiChamp' do
type_de_champ { association :type_de_champ_pole_emploi, procedure: dossier.procedure }
factory :champ_do_not_use_pole_emploi, class: 'Champs::PoleEmploiChamp' do
end
factory :champ_mesri, class: 'Champs::MesriChamp' do
type_de_champ { association :type_de_champ_mesri, procedure: dossier.procedure }
factory :champ_do_not_use_mesri, class: 'Champs::MesriChamp' do
end
factory :champ_siret, class: 'Champs::SiretChamp' do
type_de_champ { association :type_de_champ_siret, procedure: dossier.procedure }
factory :champ_do_not_use_siret, class: 'Champs::SiretChamp' do
association :etablissement, factory: [:etablissement]
value { '44011762001530' }
end
factory :champ_rna, class: 'Champs::RNAChamp' do
type_de_champ { association :type_de_champ_rna, procedure: dossier.procedure }
association :etablissement, factory: [:etablissement]
factory :champ_do_not_use_rna, class: 'Champs::RNAChamp' do
value { 'W173847273' }
end
factory :champ_engagement_juridique, class: 'Champs::EngagementJuridiqueChamp' do
type_de_champ { association :type_de_champ_engagement_juridique, procedure: dossier.procedure }
factory :champ_do_not_use_engagement_juridique, class: 'Champs::EngagementJuridiqueChamp' do
end
factory :champ_cojo, class: 'Champs::COJOChamp' do
type_de_champ { association :type_de_champ_cojo, procedure: dossier.procedure }
factory :champ_do_not_use_cojo, class: 'Champs::COJOChamp' do
end
factory :champ_rnf, class: 'Champs::RNFChamp' do
type_de_champ { association :type_de_champ_rnf, procedure: dossier.procedure }
factory :champ_do_not_use_rnf, class: 'Champs::RNFChamp' do
end
factory :champ_expression_reguliere, class: 'Champs::ExpressionReguliereChamp' do
type_de_champ { association :type_de_champ_expression_reguliere, procedure: dossier.procedure }
factory :champ_do_not_use_expression_reguliere, class: 'Champs::ExpressionReguliereChamp' do
end
factory :champ_repetition, class: 'Champs::RepetitionChamp' do
type_de_champ { association :type_de_champ_repetition, procedure: dossier.procedure }
factory :champ_do_not_use_repetition, class: 'Champs::RepetitionChamp' do
transient do
rows { 2 }
end
after(:build) do |champ_repetition, evaluator|
revision = champ_repetition.type_de_champ.procedure&.active_revision || build(:procedure_revision)
parent = revision.revision_types_de_champ.find { |rtdc| rtdc.type_de_champ == champ_repetition.type_de_champ }
types_de_champ = revision.revision_types_de_champ.filter { |rtdc| rtdc.parent == parent }.map(&:type_de_champ)
revision = champ_repetition.type_de_champ.procedure.active_revision
parent = revision.revision_types_de_champ.find { _1.type_de_champ == champ_repetition.type_de_champ }
types_de_champ = revision.revision_types_de_champ.filter { _1.parent == parent }.map(&:type_de_champ)
evaluator.rows.times do
row_id = ULID.generate
champ_repetition.champs << types_de_champ.map do |type_de_champ|
build(:"champ_#{type_de_champ.type_champ}", dossier: champ_repetition.dossier, row_id:, type_de_champ: type_de_champ, parent: champ_repetition, private: champ_repetition.private?)
attrs = { dossier: champ_repetition.dossier, parent: champ_repetition, private: champ_repetition.private?, stable_id: type_de_champ.stable_id, row_id: }
build(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
end
end
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,
position: 0,
parent: champ_repetition.type_de_champ,
libelle: 'Justificatif de domicile')
type_de_champ_pj1 = build(:type_de_champ_piece_justificative,
position: 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

View file

@ -119,37 +119,6 @@ FactoryBot.define do
hidden_by_reason { DeletedDossier.reasons.fetch(:instructeur_request) }
end
trait :with_dossier_link do
after(:create) do |dossier, _evaluator|
# create linked dossier
linked_dossier = create(:dossier, :en_construction)
# find first type de champ dossier_link
type_de_champ = dossier.types_de_champ.find do |t|
t.type_champ == TypeDeChamp.type_champs.fetch(:dossier_link)
end
# if type de champ does not exist create it
if !type_de_champ
type_de_champ = create(:type_de_champ_dossier_link, procedure: dossier.procedure)
end
# find champ with the type de champ
champ = dossier.reload.champs_public.find do |c|
c.type_de_champ == type_de_champ
end
# if champ does not exist create it
if !champ
champ = create(:champ_dossier_link, dossier: dossier, type_de_champ: type_de_champ)
end
# set champ value with linked dossier
champ.value = linked_dossier.id
champ.save!
end
end
trait :with_commentaires do
commentaires { [build(:commentaire), build(:commentaire)] }
end
@ -285,13 +254,13 @@ FactoryBot.define do
after(:create) do |dossier, _evaluator|
dossier.champs_to_destroy.where(private: false).destroy_all
dossier.types_de_champ.each do |type_de_champ|
if type_de_champ.simple_drop_down_list?
create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first)
value = if type_de_champ.simple_drop_down_list?
type_de_champ.drop_down_list_enabled_non_empty_options.first
elsif type_de_champ.multiple_drop_down_list?
create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first(2).to_json)
else
create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:)
type_de_champ.drop_down_list_enabled_non_empty_options.first(2).to_json
end
attrs = { stable_id: type_de_champ.stable_id, dossier:, value: }.compact
create(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
end
dossier.reload
end
@ -301,13 +270,13 @@ FactoryBot.define do
after(:create) do |dossier, _evaluator|
dossier.champs_to_destroy.where(private: true).destroy_all
dossier.types_de_champ_private.each do |type_de_champ|
if type_de_champ.simple_drop_down_list?
create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first)
value = if type_de_champ.simple_drop_down_list?
type_de_champ.drop_down_list_enabled_non_empty_options.first
elsif type_de_champ.multiple_drop_down_list?
create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first(2).to_json)
else
create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:)
type_de_champ.drop_down_list_enabled_non_empty_options.first(2).to_json
end
attrs = { stable_id: type_de_champ.stable_id, dossier:, private: true, value: }.compact
create(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
end
dossier.reload
end

View file

@ -1,6 +1,5 @@
FactoryBot.define do
factory :geo_area do
association :champ
properties { {} }
geometry { {} }

View file

@ -1,7 +1,9 @@
RSpec.describe GalleryHelper, type: :helper do
let(:procedure) { create(:procedure_with_dossiers) }
let(:type_de_champ_pj) { create(:type_de_champ_piece_justificative, stable_id: 3, libelle: 'Justificatif de domicile', procedure:) }
let(:champ_pj) { create(:champ_piece_justificative, type_de_champ: type_de_champ_pj) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :piece_justificative, stable_id: 3, libelle: 'Justificatif de domicile' }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ_pj) { dossier.champs.first }
let(:blob_info) do
{
filename: file.original_filename,

View file

@ -3,7 +3,11 @@
require 'rails_helper'
RSpec.describe ChampFetchExternalDataJob, type: :job do
let(:champ) { build(:champ, external_id: champ_external_id, data:) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :communes }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
let(:external_id) { "an ID" }
let(:champ_external_id) { "an ID" }
let(:data) { nil }
@ -15,6 +19,7 @@ RSpec.describe ChampFetchExternalDataJob, type: :job do
include Dry::Monads[:result]
before do
champ.update_columns(external_id: champ_external_id, data:)
allow(champ).to receive(:fetch_external_data).and_return(fetched_data)
allow(champ).to receive(:update_with_external_data!)
allow(champ).to receive(:log_fetch_external_data_exception)

View file

@ -15,9 +15,14 @@ RSpec.describe Cron::BackfillSiretDegradedModeJob, type: :job do
end
context 'fix etablisEtablissementAdapter.newsement with champs with adresse nil' do
let(:champ_siret) { create(:champ_siret, etablissement: etablissement) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :siret }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ_siret) { dossier.champs.first }
before do
champ_siret
champ_siret.update_column(:etablissement_id, etablissement.id)
end
it 'works' do
allow_any_instance_of(APIEntreprise::EtablissementAdapter).to receive(:to_params).and_return({ adresse: new_adresse })

View file

@ -1,11 +1,15 @@
RSpec.describe DossierIndexSearchTermsJob, type: :job do
let(:dossier) { create(:dossier) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:, types_de_champ_private:) }
let(:types_de_champ_public) { [{ type: :text }] }
let(:types_de_champ_private) { [{ type: :text }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ_siret) { dossier.champs.first }
subject(:perform_job) { described_class.perform_now(dossier.reload) }
before do
create(:champ_text, dossier:, value: "un nouveau champ")
create(:champ_text, dossier:, value: "private champ", private: true)
dossier.champs_public.first.update_column(:value, "un nouveau champ")
dossier.champs_private.first.update_column(:value, "private champ")
end
it "update search terms columns" do

View file

@ -1,11 +1,17 @@
describe Migrations::BatchUpdateDatetimeValuesJob, type: :job do
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :datetime, mandatory: }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:datetime_champ) { dossier.champs.first }
let(:mandatory) { true }
before do
datetime_champ.update_column(:value, value)
datetime_champ.save(validate: false)
end
context "when the value is a valid ISO8601 date" do
let!(:value) { Time.zone.parse('10/01/2023 13:30').iso8601 }
let!(:datetime_champ) { build(:champ_datetime, value: value) }
subject { described_class.perform_now([datetime_champ.id]) }
@ -17,7 +23,6 @@ describe Migrations::BatchUpdateDatetimeValuesJob, type: :job do
context "when the value is a date convertible to IS8061" do
let!(:value) { "2023-01-10" }
let!(:datetime_champ) { build(:champ_datetime, value: value) }
subject { described_class.perform_now([datetime_champ.id]) }
@ -28,11 +33,8 @@ describe Migrations::BatchUpdateDatetimeValuesJob, type: :job do
end
context "when the value is a date not convertible to IS8061" do
before do
datetime_champ.type_de_champ.update!(mandatory: false)
end
let!(:datetime_champ) { build(:champ_datetime, value: "blabla") }
let!(:value) { "blabla" }
let!(:mandatory) { false }
subject { described_class.perform_now([datetime_champ.id]) }
@ -43,7 +45,7 @@ describe Migrations::BatchUpdateDatetimeValuesJob, type: :job do
end
context "when the value is a date not convertible to IS8061 and the champ is required" do
let!(:datetime_champ) { build(:champ_datetime, value: "blabla") }
let!(:value) { "blabla" }
subject { described_class.perform_now([datetime_champ.id]) }
@ -57,7 +59,7 @@ describe Migrations::BatchUpdateDatetimeValuesJob, type: :job do
end
context "when the value is nil" do
let!(:datetime_champ) { build(:champ_datetime, value: nil) }
let!(:value) { nil }
subject { described_class.perform_now([datetime_champ.id]) }

View file

@ -1,8 +1,14 @@
describe Migrations::BatchUpdatePaysValuesJob, type: :job do
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :pays, mandatory: }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:pays_champ) { dossier.champs.first }
let(:mandatory) { true }
before { pays_champ.update_columns(attributes) }
subject { described_class.perform_now([pays_champ.id]) }
context "the value is correct" do
let(:pays_champ) { create(:champ_pays).tap { _1.update_columns(value: 'France', external_id: 'FR') } }
let(:attributes) { { value: 'France', external_id: 'FR' } }
it 'does not change it' do
subject
@ -12,11 +18,8 @@ describe Migrations::BatchUpdatePaysValuesJob, type: :job do
end
context "the value is incorrect" do
before do
pays_champ.type_de_champ.update!(mandatory: false)
end
let(:pays_champ) { create(:champ_pays).tap { _1.update_columns(value: 'Incorrect') } }
let(:attributes) { { value: 'Incorrect' } }
let(:mandatory) { false }
it 'updates value to nil' do
subject
@ -26,7 +29,7 @@ describe Migrations::BatchUpdatePaysValuesJob, type: :job do
end
context "the value is easily cleanable" do
let(:pays_champ) { create(:champ_pays).tap { _1.update_columns(value: 'Vietnam') } }
let(:attributes) { { value: 'Vietnam' } }
it 'cleans the value' do
subject
@ -36,7 +39,7 @@ describe Migrations::BatchUpdatePaysValuesJob, type: :job do
end
context "the value is hard to clean" do
let(:pays_champ) { create(:champ_pays).tap { _1.update_columns(value: 'CHRISTMAS (ILE)') } }
let(:attributes) { { value: 'CHRISTMAS (ILE)' } }
it 'cleans the value' do
subject

View file

@ -1,6 +1,10 @@
describe Migrations::NormalizeCommunesJob, type: :job do
context 'when value is "", external_id is "", and code_departement is "undefined"' do
let(:champ) { create(:champ_communes) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :communes }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
before { champ.update_columns(external_id: "", value: "", value_json: { code_departement: 'undefined', departement: 'undefined' }) }
subject { described_class.perform_now([champ.id]) }
it 'empty the champs' do

View file

@ -1,68 +0,0 @@
describe Recovery::AlignChampWithDossierRevision do
let(:procedure) { create(:procedure, types_de_champ_public: [{ stable_id: bad_stable_id }, {}]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:bad_dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:bad_stable_id) { 999 }
let(:bad_champ) { bad_dossier.champs.find { bad_stable_id == _1.stable_id } }
context 'when type_de_champ exists in dossier revision' do
before do
procedure.publish!
procedure.draft_revision
.find_and_ensure_exclusive_use(bad_stable_id)
.update(libelle: "New libelle")
previous_revision = procedure.published_revision
previous_type_de_champ = previous_revision.types_de_champ.find { bad_stable_id == _1.stable_id }
procedure.publish_revision!
procedure.reload
bad_dossier
bad_champ.update(type_de_champ: previous_type_de_champ)
end
it 'bad dossier shoud be bad' do
expect(procedure.revisions.size).to eq(3)
expect(bad_dossier.revision).to eq(procedure.published_revision)
expect(bad_dossier.champs.size).to eq(2)
expect(bad_dossier.champs_public.size).to eq(2)
expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error
fixer = Recovery::AlignChampWithDossierRevision.new(Dossier)
fixer.run
expect(fixer.logs.size).to eq(1)
expect(fixer.logs.first.fetch(:status)).to eq(:updated)
expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error
expect(bad_dossier.champs.size).to eq(2)
expect(bad_dossier.champs_public.size).to eq(2)
end
end
context 'when type_de_champ does not exist in dossier revision' do
before do
procedure.publish!
bad_dossier
procedure.draft_revision.remove_type_de_champ(bad_stable_id)
procedure.publish_revision!
bad_dossier.update(revision: procedure.published_revision)
end
it 'bad dossier shoud be bad' do
expect(procedure.revisions.size).to eq(3)
expect(bad_dossier.revision).to eq(procedure.published_revision)
expect(bad_dossier.champs.size).to eq(2)
expect(bad_dossier.champs_public.size).to eq(2)
expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error
fixer = Recovery::AlignChampWithDossierRevision.new(Dossier)
fixer.run(destroy_extra_champs: true)
expect(fixer.logs.size).to eq(1)
expect(fixer.logs.first.fetch(:status)).to eq(:not_found)
expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error
expect(bad_dossier.champs.size).to eq(1)
expect(bad_dossier.champs_public.size).to eq(1)
end
end
end

View file

@ -1,7 +1,7 @@
describe Champ do
describe '#private?' do
let(:type_de_champ) { build(:type_de_champ, :private) }
let(:champ) { type_de_champ.champ.build }
let(:champ) { type_de_champ.build_champ }
it { expect(champ.private?).to be_truthy }
it { expect(champ.public?).to be_falsey }

View file

@ -1,56 +0,0 @@
shared_examples 'champ_spec' do
describe 'mandatory_blank?' do
let(:type_de_champ) { build(:type_de_champ, mandatory: mandatory) }
let(:champ) { build(:champ, type_de_champ: type_de_champ, value: value) }
let(:value) { '' }
let(:mandatory) { true }
context 'when mandatory and blank' do
it { expect(champ.mandatory_blank?).to be(true) }
end
context 'when carte mandatory and blank' do
let(:type_de_champ) { build(:type_de_champ_carte, mandatory: mandatory) }
let(:champ) { build(:champ_carte, type_de_champ: type_de_champ, value: value) }
let(:value) { nil }
it { expect(champ.mandatory_blank?).to be(true) }
end
context 'when multiple_drop_down_list mandatory and blank' do
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, mandatory: mandatory) }
let(:champ) { build(:champ_multiple_drop_down_list, type_de_champ: type_de_champ, value: value) }
let(:value) { '[]' }
it { expect(champ.mandatory_blank?).to be(true) }
end
context 'when repetition blank' do
let(:type_de_champ) { build(:type_de_champ_repetition) }
let(:champ) { build(:champ_repetition, type_de_champ: type_de_champ, rows: 0) }
it { expect(champ.blank?).to be(true) }
end
context 'when repetition not blank' do
let(:type_de_champ) { build(:type_de_champ_repetition, :with_types_de_champ, procedure: build(:procedure)) }
let(:champ) { build(:champ_repetition, type_de_champ: type_de_champ) }
it { expect(champ.blank?).to be(false) }
end
context 'when not blank' do
let(:value) { 'yop' }
it { expect(champ.mandatory_blank?).to be(false) }
end
context 'when not mandatory' do
let(:mandatory) { false }
it { expect(champ.mandatory_blank?).to be(false) }
end
context 'when not mandatory or blank' do
let(:value) { 'u' }
let(:mandatory) { false }
it { expect(champ.mandatory_blank?).to be(false) }
end
end
end

View file

@ -1,9 +1,62 @@
describe Champ do
include ActiveJob::TestHelper
require 'models/champ_shared_example.rb'
describe 'mandatory_blank?' do
let(:type_de_champ) { build(:type_de_champ, mandatory: mandatory) }
let(:champ) { Champ.new(value: value) }
let(:value) { '' }
let(:mandatory) { true }
before { allow(champ).to receive(:type_de_champ).and_return(type_de_champ) }
it_should_behave_like "champ_spec"
context 'when mandatory and blank' do
it { expect(champ.mandatory_blank?).to be(true) }
end
context 'when carte mandatory and blank' do
let(:type_de_champ) { build(:type_de_champ_carte, mandatory: mandatory) }
let(:champ) { Champs::CarteChamp.new(value: value) }
let(:value) { nil }
it { expect(champ.mandatory_blank?).to be(true) }
end
context 'when multiple_drop_down_list mandatory and blank' do
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, mandatory: mandatory) }
let(:champ) { Champs::MultipleDropDownListChamp.new(value: value) }
let(:value) { '[]' }
it { expect(champ.mandatory_blank?).to be(true) }
end
context 'when repetition blank' do
let(:type_de_champ) { build(:type_de_champ_repetition) }
let(:champ) { Champs::RepetitionChamp.new }
it { expect(champ.blank?).to be(true) }
end
context 'when repetition not blank' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text }] }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.find(&:repetition?) }
it { expect(champ.blank?).to be(false) }
end
context 'when not blank' do
let(:value) { 'yop' }
it { expect(champ.mandatory_blank?).to be(false) }
end
context 'when not mandatory' do
let(:mandatory) { false }
it { expect(champ.mandatory_blank?).to be(false) }
end
context 'when not mandatory or blank' do
let(:value) { 'u' }
let(:mandatory) { false }
it { expect(champ.mandatory_blank?).to be(false) }
end
end
describe "associations" do
it { is_expected.to belong_to(:dossier) }
@ -18,14 +71,14 @@ describe Champ do
describe "normalization" do
it "should remove null bytes before save" do
champ = create(:champ, value: "foo\u0000bar")
champ = Champ.new(value: "foo\u0000bar")
champ.normalize
expect(champ.value).to eq "foobar"
end
end
describe '#public?' do
let(:type_de_champ) { build(:type_de_champ) }
let(:champ) { type_de_champ.champ.build }
let(:champ) { Champ.new }
it { expect(champ.public?).to be_truthy }
it { expect(champ.private?).to be_falsey }
@ -87,10 +140,8 @@ describe Champ do
end
describe '#format_datetime' do
let(:champ) { build(:champ_datetime, value: value) }
before { champ.save! }
let(:champ) { Champs::DatetimeChamp.new(value: value) }
before { champ.run_callbacks(:validation) }
context 'when the value is sent by a modern browser' do
let(:value) { '2017-12-31 10:23' }
@ -105,9 +156,8 @@ describe Champ do
end
describe '#multiple_select_to_string' do
let(:champ) { build(:champ_multiple_drop_down_list, value: value) }
before { champ.save! }
let(:champ) { Champs::MultipleDropDownListChamp.new(value: value) }
# before { champ.save! }
# when using the old form, and the ChampsService Class
# TODO: to remove
@ -142,7 +192,8 @@ describe Champ do
end
describe 'for_export' do
let(:champ) { create(:champ_text, value: value) }
let(:champ) { Champs::TextChamp.new(value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_text)) }
context 'when type_de_champ is text' do
let(:value) { '123' }
@ -151,14 +202,17 @@ describe Champ do
end
context 'when type_de_champ is textarea' do
let(:champ) { create(:champ_textarea, value: value) }
let(:champ) { Champs::TextareaChamp.new(value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_textarea)) }
let(:value) { '<b>gras<b>' }
it { expect(champ.for_export).to eq('gras') }
end
context 'when type_de_champ is yes_no' do
let(:champ) { create(:champ_yes_no, value: value) }
let(:champ) { Champs::YesNoChamp.new(value: value) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_yes_no)) }
context 'if yes' do
let(:value) { 'true' }
@ -180,18 +234,21 @@ describe Champ do
end
context 'when type_de_champ is multiple_drop_down_list' do
let(:champ) { create(:champ_multiple_drop_down_list, value:) }
let(:champ) { Champs::MultipleDropDownListChamp.new(value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_multiple_drop_down_list)) }
let(:value) { '["Crétinier", "Mousserie"]' }
it { expect(champ.for_export).to eq('Crétinier, Mousserie') }
end
context 'when type_de_champ and champ.type mismatch' do
let(:champ_yes_no) { create(:champ_yes_no, value: 'true') }
let(:champ_text) { create(:champ_text, value: 'Hello') }
let(:value) { :noop }
let(:champ_yes_no) { Champs::YesNoChamp.new(value: 'true') }
let(:champ_text) { Champs::TextChamp.new(value: 'hello') }
it { expect(TypeDeChamp.champ_value_for_export(champ_text.type_champ, champ_yes_no)).to eq(nil) }
it { expect(TypeDeChamp.champ_value_for_export(champ_yes_no.type_champ, champ_text)).to eq('Non') }
it { expect(TypeDeChamp.champ_value_for_export('text', champ_yes_no)).to eq(nil) }
it { expect(TypeDeChamp.champ_value_for_export('yes_no', champ_text)).to eq('Non') }
end
end
@ -199,7 +256,7 @@ describe Champ do
subject { champ.search_terms }
context 'for adresse champ' do
let(:champ) { create(:champ_address, value:) }
let(:champ) { Champs::AddressChamp.new(value:) }
let(:value) { "10 rue du Pinson qui Piaille" }
it { is_expected.to eq([value]) }
@ -207,8 +264,8 @@ describe Champ do
context 'for checkbox champ' do
let(:libelle) { champ.libelle }
let(:champ) { create(:champ_checkbox, value:) }
let(:champ) { Champs::CheckboxChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_checkbox)) }
context 'when the box is checked' do
let(:value) { 'true' }
@ -223,74 +280,79 @@ describe Champ do
end
context 'for civilite champ' do
let(:champ) { create(:champ_civilite, value:) }
let(:champ) { Champs::CiviliteChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_civilite)) }
let(:value) { "M." }
it { is_expected.to eq([value]) }
end
context 'for date champ' do
let(:champ) { create(:champ_date, value:) }
let(:champ) { Champs::DateChamp.new(value:) }
let(:value) { "2018-07-30" }
it { is_expected.to be_nil }
end
context 'for date time champ' do
let(:champ) { create(:champ_datetime, value:) }
let(:champ) { Champs::DatetimeChamp.new(value:) }
let(:value) { "2018-04-29 09:00" }
it { is_expected.to be_nil }
end
context 'for département champ' do
let(:champ) { create(:champ_departements, value:) }
let(:champ) { Champs::DepartementChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_departements)) }
let(:value) { "69" }
it { is_expected.to eq(['69 Rhône']) }
end
context 'for dossier link champ' do
let(:champ) { create(:champ_dossier_link, value:) }
let(:champ) { Champs::DossierLinkChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_dossier_link)) }
let(:value) { "9103132886" }
it { is_expected.to eq([value]) }
end
context 'for drop down list champ' do
let(:champ) { create(:champ_dossier_link, value:) }
let(:champ) { Champs::DropDownListChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_drop_down_list)) }
let(:value) { "HLM" }
it { is_expected.to eq([value]) }
end
context 'for email champ' do
let(:champ) { build(:champ_email, value:) }
let(:champ) { Champs::EmailChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_email)) }
let(:value) { "machin@example.com" }
it { is_expected.to eq([value]) }
end
context 'for explication champ' do
let(:champ) { build(:champ_explication) }
let(:champ) { Champs::ExplicationChamp.new }
it { is_expected.to be_nil }
end
context 'for header section champ' do
let(:champ) { build(:champ_header_section) }
let(:champ) { Champs::HeaderSectionChamp.new }
it { is_expected.to be_nil }
end
context 'for linked drop down list champ' do
let(:champ) { create(:champ_linked_drop_down_list, primary_value: "hello", secondary_value: "world") }
let(:champ) { Champs::LinkedDropDownListChamp.new(primary_value: "hello", secondary_value: "world") }
it { is_expected.to eq(["hello", "world"]) }
end
context 'for multiple drop down list champ' do
let(:champ) { build(:champ_multiple_drop_down_list, value:) }
let(:champ) { Champs::MultipleDropDownListChamp.new(value:) }
context 'when there are multiple values selected' do
let(:value) { JSON.generate(['goodbye', 'cruel', 'world']) }
@ -306,35 +368,41 @@ describe Champ do
end
context 'for number champ' do
let(:champ) { build(:champ_number, value:) }
let(:champ) { Champs::NumberChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_number)) }
let(:value) { "1234" }
it { is_expected.to eq([value]) }
end
context 'for pays champ' do
let(:champ) { build(:champ_pays, value:) }
let(:champ) { Champs::PaysChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_pays)) }
let(:value) { "FR" }
it { is_expected.to eq(['France']) }
end
context 'for phone champ' do
let(:champ) { build(:champ_phone, value:) }
let(:champ) { Champs::PhoneChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_phone)) }
let(:value) { "06 06 06 06 06" }
it { is_expected.to eq([value]) }
end
context 'for pièce justificative champ' do
let(:champ) { build(:champ_piece_justificative, value:) }
let(:champ) { Champs::PieceJustificativeChamp.new(value:) }
let(:value) { nil }
it { is_expected.to be_nil }
end
context 'for region champ' do
let(:champ) { build(:champ_regions, value:) }
let(:champ) { Champs::RegionChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_regions)) }
let(:value) { "11" }
it { is_expected.to eq(['Île-de-France']) }
@ -375,13 +443,13 @@ describe Champ do
association_date_publication: "1962-05-31"
)
end
let(:champ) { create(:champ_siret, value: etablissement.siret, etablissement:) }
let(:champ) { Champs::SiretChamp.new(value: etablissement.siret, etablissement:) }
it { is_expected.to eq([etablissement.entreprise_siren, etablissement.entreprise_numero_tva_intracommunautaire, etablissement.entreprise_forme_juridique, etablissement.entreprise_forme_juridique_code, etablissement.entreprise_nom_commercial, etablissement.entreprise_raison_sociale, etablissement.entreprise_siret_siege_social, etablissement.entreprise_nom, etablissement.entreprise_prenom, etablissement.association_rna, etablissement.association_titre, etablissement.association_objet, etablissement.siret, etablissement.enseigne, etablissement.naf, etablissement.libelle_naf, etablissement.adresse, etablissement.code_postal, etablissement.localite, etablissement.code_insee_localite]) }
end
context 'when there is no etablissement' do
let(:champ) { create(:champ_siret, value:, etablissement: nil) }
let(:champ) { Champs::SiretChamp.new(value:, etablissement: nil) }
let(:value) { "35130347400024" }
it { is_expected.to eq([value]) }
@ -389,21 +457,25 @@ describe Champ do
end
context 'for text champ' do
let(:champ) { build(:champ_text, value:) }
let(:champ) { Champs::TextChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_text)) }
let(:value) { "Blah" }
it { is_expected.to eq([value]) }
end
context 'for text area champ' do
let(:champ) { build(:champ_textarea, value:) }
let(:champ) { Champs::TextareaChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_textarea)) }
let(:value) { "Bla\nBlah de bla." }
it { is_expected.to eq([value]) }
end
context 'for yes/no champ' do
let(:champ) { build(:champ_yes_no, value:) }
let(:champ) { Champs::YesNoChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_yes_no)) }
let(:libelle) { champ.libelle }
context 'when the box is checked' do
@ -422,7 +494,9 @@ describe Champ do
describe '#enqueue_virus_scan' do
context 'when type_champ is type_de_champ_piece_justificative' do
let(:champ) { build(:champ_piece_justificative) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { dossier.champs.first }
context 'and there is a blob' do
before do
@ -450,8 +524,9 @@ describe Champ do
describe '#enqueue_watermark_job' do
context 'when type_champ is type_de_champ_titre_identite' do
let(:type_de_champ) { create(:type_de_champ_titre_identite) }
let(:champ) { build(:champ_titre_identite, type_de_champ: type_de_champ, skip_default_attachment: true) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :titre_identite }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { dossier.champs.first }
before do
allow(ClamavService).to receive(:safe_file?).and_return(true)
@ -476,40 +551,21 @@ describe Champ do
end
end
describe 'repetition' do
let(:procedure) { create(:procedure, :published, types_de_champ_private: [{}], types_de_champ_public: [{}, { type: :repetition, mandatory: true, children: [{}, { type: :integer_number }] }]) }
let(:tdc_repetition) { procedure.active_revision.types_de_champ_public.find(&:repetition?) }
let(:tdc_text) { procedure.active_revision.children_of(tdc_repetition).first }
let(:dossier) { create(:dossier, procedure: procedure) }
let(:champ) { dossier.champs_public.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_id: ULID.generate) }
context 'when creating the model directly' do
let(:champ_text_row_1) { create(:champ_text, type_de_champ: tdc_text, row_id: ULID.generate, parent: champ, dossier: nil) }
it 'associates nested champs to the parent dossier' do
expect(champ_text_row_1.dossier_id).to eq(champ.dossier_id)
end
end
end
describe '#log_fetch_external_data_exception' do
let(:champ) { create(:champ_siret) }
let(:champ) { Champs::SiretChamp.new }
context "add execption to the log" do
before do
champ.log_fetch_external_data_exception(StandardError.new('My special exception!'))
it do
expect(champ).to receive(:update_column).with(:fetch_external_data_exceptions, ['PAN'])
champ.log_fetch_external_data_exception(double(inspect: 'PAN'))
end
it { expect(champ.fetch_external_data_exceptions).to eq(['#<StandardError: My special exception!>']) }
end
end
describe "fetch_external_data" do
let(:champ) { create(:champ_rnf, data: 'some data') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :rnf }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { dossier.champs.first.tap { _1.update_column(:data, 'some data') } }
context "cleanup_if_empty" do
it "remove data if external_id changes" do
@ -534,47 +590,50 @@ describe Champ do
end
describe "#input_name" do
let(:champ) { create(:champ_text) }
let(:champ) { Champs::TextChamp.new }
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.public_id}]" }
context "when private" do
let(:champ) { create(:champ_text, private: true) }
let(:champ) { Champs::TextChamp.new(private: true) }
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.public_id}]" }
end
context "when has parent" do
let(:champ) { create(:champ_text, parent: create(:champ_text)) }
let(:champ) { Champs::TextChamp.new(parent: Champs::TextChamp.new) }
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.public_id}]" }
end
context "when has private parent" do
let(:champ) { create(:champ_text, private: true, parent: create(:champ_text, private: true)) }
let(:champ) { Champs::TextChamp.new(private: true, parent: Champs::TextChamp.new(private: true)) }
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.public_id}]" }
end
end
describe '#update_with_external_data!' do
let(:champ) { create(:champ_siret) }
let(:data) { "data" }
subject { champ.update_with_external_data!(data: data) }
it { expect { subject }.to change { champ.reload.data }.to(data) }
end
describe 'dom_id' do
let(:champ) { build(:champ_text, row_id: '1234') }
let(:champ) { Champs::TextChamp.new(row_id: '1234') }
before do
allow(champ).to receive(:type_de_champ).and_return(create(:type_de_champ_text))
end
it { expect(champ.public_id).to eq("#{champ.stable_id}-#{champ.row_id}") }
it { expect(ActionView::RecordIdentifier.dom_id(champ)).to eq("champ_#{champ.public_id}") }
it { expect(ActionView::RecordIdentifier.dom_id(champ.type_de_champ)).to eq("type_de_champ_#{champ.type_de_champ.id}") }
it { expect(ActionView::RecordIdentifier.dom_class(champ)).to eq("champ") }
it do
expect(champ.public_id).to eq("#{champ.stable_id}-#{champ.row_id}")
expect(ActionView::RecordIdentifier.dom_id(champ)).to eq("champ_#{champ.public_id}")
expect(ActionView::RecordIdentifier.dom_id(champ.type_de_champ)).to eq("type_de_champ_#{champ.type_de_champ.id}")
expect(ActionView::RecordIdentifier.dom_class(champ)).to eq("champ")
end
end
describe 'clone' do
let(:procedure) { create(:procedure, types_de_champ_private:, types_de_champ_public:) }
let(:types_de_champ_private) { [] }
let(:types_de_champ_public) { [] }
let(:champ) { dossier.champs.first }
subject { champ.clone(fork) }
context 'when champ public' do
let(:champ) { create(:champ_piece_justificative, private: false) }
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
context 'when fork' do
let(:fork) { true }
@ -592,7 +651,8 @@ describe Champ do
end
context 'champ private' do
let(:champ) { create(:champ_piece_justificative, private: true) }
let(:dossier) { create(:dossier, :with_populated_annotations, procedure:) }
let(:types_de_champ_private) { [{ type: :piece_justificative }] }
context 'when fork' do
let(:fork) { true }

View file

@ -1,5 +1,11 @@
describe Champs::AddressChamp do
let(:champ) { build(:champ_address, value:, data:) }
let(:champ) do
described_class.new.tap do |champ|
champ.value = value
champ.data = data
champ.validate
end
end
let(:value) { '' }
let(:data) { nil }

View file

@ -4,7 +4,9 @@ require 'rails_helper'
RSpec.describe Champs::AnnuaireEducationChamp do
describe '#update_with_external_data!' do
let(:champ) { create(:champ_annuaire_education, data: "any data") }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :annuaire_education }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first.tap { _1.update_column(:data, 'any data') } }
subject { champ.update_with_external_data!(data: data) }
shared_examples "a data updater (without updating the value)" do |data|

View file

@ -1,5 +1,6 @@
describe Champs::CarteChamp do
let(:champ) { build(:champ_carte, geo_areas:) }
let(:champ) { Champs::CarteChamp.new(geo_areas:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_carte)) }
let(:value) { '' }
let(:coordinates) { [[[2.3859214782714844, 48.87442541960633], [2.3850631713867183, 48.87273183590832], [2.3809432983398438, 48.87081237174292], [2.3859214782714844, 48.87442541960633]]] }
let(:geo_json) do

View file

@ -1,12 +1,11 @@
describe Champs::CheckboxChamp do
it_behaves_like "a boolean champ" do
let(:boolean_champ) { build(:champ_checkbox, value: value) }
end
let(:boolean_champ) { described_class.new(value: value) }
before { allow(boolean_champ).to receive(:type_de_champ).and_return(build(:type_de_champ_checkbox)) }
it_behaves_like "a boolean champ"
# TODO remove when normalize_checkbox_values is over
describe '#true?' do
let(:checkbox_champ) { build(:champ_checkbox, value: value) }
subject { checkbox_champ.true? }
subject { boolean_champ.true? }
context "when the checkbox value is 'on'" do
let(:value) { 'on' }

View file

@ -1,6 +1,6 @@
describe Champs::CnafChamp, type: :model do
let(:champ) { build(:champ_cnaf) }
let(:champ) { described_class.new(dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_cnaf)) }
describe 'numero_allocataire and code_postal' do
before do
champ.numero_allocataire = '1234567'
@ -37,7 +37,7 @@ describe Champs::CnafChamp, type: :model do
describe '#validate' do
let(:numero_allocataire) { '1234567' }
let(:code_postal) { '12345' }
let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_cnaf)) }
let(:champ) { described_class.new(dossier: build(:dossier)) }
let(:validation_context) { :champs_public_value }
subject { champ.valid?(validation_context) }

View file

@ -1,7 +1,5 @@
describe Champs::COJOChamp, type: :model do
let(:champ) { build(:champ_cojo, accreditation_number:, accreditation_birthdate:) }
let(:external_id) { nil }
let(:stub) { stub_request(:post, url).with(body: { accreditationNumber: accreditation_number.to_i, birthdate: accreditation_birthdate }).to_return(body:, status:) }
let(:url) { COJOService.new.send(:url) }
let(:body) { Rails.root.join('spec', 'fixtures', 'files', 'api_cojo', "accreditation_#{response_type}.json").read }
let(:status) { 200 }
@ -9,8 +7,17 @@ describe Champs::COJOChamp, type: :model do
let(:accreditation_number) { '123456' }
let(:accreditation_birthdate) { '21/12/1959' }
before { stub_request(:post, url).with(body: { accreditationNumber: accreditation_number.to_i, birthdate: accreditation_birthdate }).to_return(body:, status:) }
describe 'fetch_external_data' do
subject { stub; champ.fetch_external_data }
let(:champ) do
described_class.new do |champ|
champ.accreditation_number = accreditation_number
champ.accreditation_birthdate = accreditation_birthdate
end
end
subject { champ.fetch_external_data }
context 'success (yes)' do
it { expect(subject.value!).to eq({ accreditation_success: true, accreditation_first_name: 'Florence', accreditation_last_name: 'Griffith-Joyner' }) }
@ -49,9 +56,14 @@ describe Champs::COJOChamp, type: :model do
end
describe 'fill champ' do
let(:champ) { create(:champ_cojo, accreditation_number:, accreditation_birthdate:) }
subject { stub; champ.touch; perform_enqueued_jobs; champ.reload }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :cojo }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
before do
champ.update(accreditation_number:, accreditation_birthdate:, data: nil)
perform_enqueued_jobs;
end
subject { champ.reload }
it 'success (yes)' do
expect(subject.blank?).to be_falsey

View file

@ -1,8 +1,17 @@
describe Champs::CommuneChamp do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :communes, stable_id: 99 }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:code_insee) { '63102' }
let(:code_postal) { '63290' }
let(:code_departement) { '63' }
let(:champ) { create(:champ_communes, code_postal:, external_id: code_insee) }
let(:champ) do
described_class.new(stable_id: 99, dossier:).tap do |champ|
champ.code_postal = code_postal
champ.external_id = code_insee
champ.run_callbacks(:save)
end
end
describe 'value' do
it 'find commune' do
@ -18,7 +27,12 @@ describe Champs::CommuneChamp do
end
context 'with code' do
let(:champ) { create(:champ_communes, code: '63102-63290') }
let(:champ) do
described_class.new(stable_id: 99, dossier:).tap do |champ|
champ.code = '63102-63290'
champ.run_callbacks(:save)
end
end
it 'find commune' do
expect(champ.to_s).to eq('Châteldon (63290)')

View file

@ -1,47 +1,49 @@
describe Champs::DateChamp do
let(:date_champ) { create(:champ_date) }
let(:type_de_champ) { create(:type_de_champ_date) }
let(:date_champ) { described_class.new }
before { allow(date_champ).to receive(:type_de_champ).and_return(type_de_champ) }
describe '#convert_to_iso8601' do
it 'preserves nil' do
champ = champ_with_value(nil)
champ.save
expect(champ.reload.value).to be_nil
champ.validate
expect(champ.value).to be_nil
end
it 'converts to nil if empty string' do
champ = champ_with_value("")
champ.save
expect(champ.reload.value).to be_nil
champ.validate
expect(champ.value).to be_nil
end
it 'converts to nil if not ISO8601' do
champ = champ_with_value("12-21-2023")
champ.save
expect(champ.reload.value).to be_nil
champ.validate
expect(champ.value).to be_nil
end
it 'converts to nil if not date' do
champ = champ_with_value("value")
champ.save
expect(champ.reload.value).to be_nil
champ.validate
expect(champ.value).to be_nil
end
it "converts %d/%m/%Y format to ISO" do
champ = champ_with_value("31/12/2017")
champ.save
expect(champ.reload.value).to eq("2017-12-31")
champ.validate
expect(champ.value).to eq("2017-12-31")
end
it 'preserves if ISO8601' do
champ = champ_with_value("2023-12-21")
champ.save
expect(champ.reload.value).to eq("2023-12-21")
champ.validate
expect(champ.value).to eq("2023-12-21")
end
it 'converts to nil if false iso' do
champ = champ_with_value("2023-27-02")
champ.save
expect(champ.reload.value).to eq(nil)
champ.validate
expect(champ.value).to eq(nil)
end
end

View file

@ -1,47 +1,47 @@
describe Champs::DatetimeChamp do
let(:datetime_champ) { build(:champ_datetime) }
let(:datetime_champ) { described_class.new }
describe '#convert_to_iso8601' do
it 'preserves nil' do
champ = champ_with_value(nil)
champ.save
expect(champ.reload.value).to be_nil
champ.run_callbacks(:validation)
expect(champ.value).to be_nil
end
it 'converts to nil if empty string' do
champ = champ_with_value("")
champ.save
expect(champ.reload.value).to be_nil
champ.run_callbacks(:validation)
expect(champ.value).to be_nil
end
it 'converts to nil if not ISO8601' do
champ = champ_with_value("12-21-2023 03:20")
champ.save
expect(champ.reload.value).to be_nil
champ.run_callbacks(:validation)
expect(champ.value).to be_nil
end
it 'converts to nil if not datetime' do
champ = champ_with_value("value")
champ.save
expect(champ.reload.value).to be_nil
champ.run_callbacks(:validation)
expect(champ.value).to be_nil
end
it 'preserves if ISO8601' do
champ = champ_with_value("2023-12-21T03:20")
champ.save
expect(champ.reload.value).to eq(Time.zone.parse("2023-12-21T03:20:00").iso8601)
champ.run_callbacks(:validation)
expect(champ.value).to eq(Time.zone.parse("2023-12-21T03:20:00").iso8601)
end
it 'converts to ISO8601 if form format' do
champ = champ_with_value("{3=>21, 2=>12, 1=>2023, 4=>3, 5=>20}")
champ.save
expect(champ.reload.value).to eq(Time.zone.parse("2023-12-21T03:20:00").iso8601)
champ.run_callbacks(:validation)
expect(champ.value).to eq(Time.zone.parse("2023-12-21T03:20:00").iso8601)
end
it 'converts to ISO8601 if old browser form format' do
champ = champ_with_value("21/12/2023 03:20")
champ.save
expect(champ.reload.value).to eq(Time.zone.parse("2023-12-21T03:20:00").iso8601)
champ.run_callbacks(:validation)
expect(champ.value).to eq(Time.zone.parse("2023-12-21T03:20:00").iso8601)
end
end

View file

@ -1,8 +1,13 @@
describe Champs::DecimalNumberChamp do
let(:champ) { build(:champ_decimal_number, value:) }
subject { champ.validate(:champs_public_value) }
describe 'validation' do
let(:champ) { Champs::DecimalNumberChamp.new(value:, dossier: build(:dossier)) }
before do
allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_decimal_number))
allow(champ).to receive(:visible?).and_return(true)
champ.run_callbacks(:validation)
end
subject { champ.validate(:champs_public_value) }
context 'when the value is integer number' do
let(:value) { 2 }
@ -25,7 +30,7 @@ describe Champs::DecimalNumberChamp do
end
context 'when value contain space' do
let(:champ) { create(:champ_decimal_number, :private, value:) }
before { champ.run_callbacks(:validation) }
let(:value) { ' 2.6666 ' }
it { expect(champ.value).to eq('2.6666') }
end
@ -52,14 +57,15 @@ describe Champs::DecimalNumberChamp do
end
context 'when the champ is private, value is invalid, but validation is public' do
let(:champ) { build(:champ_decimal_number, :private, value:) }
let(:champ) { Champs::DecimalNumberChamp.new(value:, private: true, dossier: build(:dossier)) }
let(:value) { '2.6666' }
it { is_expected.to be_truthy }
end
end
describe 'for_export' do
let(:champ) { create(:champ_decimal_number, value:) }
let(:champ) { Champs::DecimalNumberChamp.new(value:) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_decimal_number)) }
subject { champ.for_export }
context 'with nil' do
let(:value) { 0 }

View file

@ -1,7 +1,8 @@
describe Champs::DepartementChamp, type: :model do
describe 'validations' do
describe 'external link' do
let(:champ) { build(:champ_departements, external_id: external_id) }
let(:champ) { described_class.new(external_id: external_id, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_departements)) }
subject { champ.validate(:champs_public_value) }
context 'when nil' do
@ -30,7 +31,9 @@ describe Champs::DepartementChamp, type: :model do
end
describe 'value' do
let(:champ) { create(:champ_departements) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :departements }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { dossier.champs.first }
subject { champ.validate(:champs_public_value) }
before { champ.update_columns(value: value) }
@ -61,7 +64,8 @@ describe Champs::DepartementChamp, type: :model do
end
describe 'value' do
let(:champ) { build(:champ_departements, value: nil) }
let(:champ) { described_class.new(value: nil, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_departements)) }
it 'with code having 2 chars' do
champ.value = '01'

View file

@ -1,5 +1,5 @@
describe Champs::DgfipChamp, type: :model do
let(:champ) { build(:champ_dgfip) }
let(:champ) { described_class.new }
describe 'numero_fiscal and reference_avis' do
before do
@ -37,7 +37,8 @@ describe Champs::DgfipChamp, type: :model do
describe '#validate' do
let(:numero_fiscal) { '1122299999092' }
let(:reference_avis) { 'FC22299999092' }
let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_dgfip)) }
let(:champ) { described_class.new(dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_dgfip)) }
let(:validation_context) { :champs_public_value }
subject { champ.valid?(validation_context) }

View file

@ -1,7 +1,7 @@
describe Champs::DossierLinkChamp, type: :model do
describe 'prefilling validations' do
describe 'value' do
subject { build(:champ_dossier_link, value: value).valid?(:prefill) }
subject { described_class.new(value:, dossier: build(:dossier)).valid?(:prefill) }
context 'when nil' do
let(:value) { nil }

View file

@ -1,7 +1,8 @@
describe Champs::DropDownListChamp do
describe 'validations' do
describe 'inclusion' do
let(:champ) { build(:champ_drop_down_list, other: other, value: value) }
let(:champ) { described_class.new(other:, value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_drop_down_list, drop_down_other: other)) }
subject { champ.validate(:champs_public_value) }
context 'when the other value is accepted' do
@ -51,7 +52,8 @@ describe Champs::DropDownListChamp do
end
describe '#drop_down_other?' do
let(:drop_down) { create(:champ_drop_down_list) }
let(:drop_down) { described_class.new(dossier: build(:dossier)) }
before { allow(drop_down).to receive(:type_de_champ).and_return(build(:type_de_champ_drop_down_list)) }
context 'when drop_down_other is nil' do
it do

View file

@ -1,7 +1,7 @@
describe Champs::EmailChamp do
describe 'validation' do
let(:champ) { build(:champ_email, value: value) }
let(:champ) { described_class.new(value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_email)) }
subject { champ.validate(:champs_public_value) }
context 'when nil' do

View file

@ -3,8 +3,8 @@ describe Champs::EpciChamp, type: :model do
subject { champ.validate(:champs_public_value) }
describe 'code_departement' do
let(:champ) { build(:champ_epci, code_departement: code_departement) }
let(:champ) { Champs::EpciChamp.new(code_departement: code_departement, dossier: build(:dossier)) }
before { allow(champ).to receive(:visible?).and_return(true) }
context 'when nil' do
let(:code_departement) { nil }
@ -31,9 +31,13 @@ describe Champs::EpciChamp, type: :model do
end
describe 'external_id' do
let(:champ) { build(:champ_epci, code_departement: code_departement, external_id: nil) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :epci }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
before do
champ.code_departement = code_departement
champ.external_id = nil
champ.save!(validate: false)
champ.update_columns(external_id: external_id)
end
@ -75,13 +79,16 @@ describe Champs::EpciChamp, type: :model do
end
describe 'value' do
subject { champ.validate(:champs_public_value) }
let(:champ) { build(:champ_epci, code_departement: code_departement, external_id: nil, value: nil) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :epci }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
before do
champ.value = nil
champ.code_departement = code_departement
champ.external_id = nil
champ.save!(validate: false)
champ.update_columns(external_id: external_id, value: value)
champ.update_columns(external_id:, value:)
end
context 'when code_departement is nil' do
@ -144,10 +151,11 @@ describe Champs::EpciChamp, type: :model do
end
describe 'value' do
let(:champ) { build(:champ_epci, external_id: nil, value: nil) }
let(:champ) { described_class.new }
let(:epci) { APIGeoService.epcis('01').first }
it 'with departement and code' do
allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_epci))
champ.code_departement = '01'
champ.value = epci[:code]
expect(champ.blank?).to be_falsey

View file

@ -1,16 +1,21 @@
describe Champs::IbanChamp do
describe '#valid?' do
let(:champ) { Champs::IbanChamp.new(dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_iban)) }
def with_value(value)
champ.tap { _1.value = value }
end
it do
expect(build(:champ_iban, value: nil).valid?(:champs_public_value)).to be_truthy
expect(build(:champ_iban, value: "FR35 KDSQFDJQSMFDQMFDQ").valid?(:champs_public_value)).to be_falsey
expect(build(:champ_iban, value: "FR7630006000011234567890189").valid?(:champs_public_value)).to be_truthy
expect(build(:champ_iban, value: "FR76 3000 6000 0112 3456 7890 189").valid?(:champs_public_value)).to be_truthy
expect(build(:champ_iban, value: "FR76 3000 6000 0112 3456 7890 189DSF").valid?(:champs_public_value)).to be_falsey
expect(build(:champ_iban, value: "FR76 3000 6000 0112 3456 7890 189").valid?(:champs_public_value)).to be_truthy
expect(with_value(nil).valid?(:champs_public_value)).to be_truthy
expect(with_value("FR35 KDSQFDJQSMFDQMFDQ").valid?(:champs_public_value)).to be_falsey
expect(with_value("FR7630006000011234567890189").valid?(:champs_public_value)).to be_truthy
expect(with_value("FR76 3000 6000 0112 3456 7890 189").valid?(:champs_public_value)).to be_truthy
expect(with_value("FR76 3000 6000 0112 3456 7890 189DSF").valid?(:champs_public_value)).to be_falsey
expect(with_value("FR76 3000 6000 0112 3456 7890 189").valid?(:champs_public_value)).to be_truthy
end
it 'format value after validation' do
champ = build(:champ_iban, value: "FR76 3000 6000 0112 3456 7890 189")
with_value("FR76 3000 6000 0112 3456 7890 189")
champ.valid?(:champs_public_value)
expect(champ.value).to eq("FR76 3000 6000 0112 3456 7890 189")
end

View file

@ -1,5 +1,6 @@
describe Champs::IntegerNumberChamp do
let(:champ) { build(:champ_integer_number, value:) }
let(:champ) { Champs::IntegerNumberChamp.new(value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:visible?).and_return(true) }
subject { champ.validate(:champs_public_value) }
describe '#valid?' do

View file

@ -1,21 +1,19 @@
describe Champs::LinkedDropDownListChamp do
describe '#unpack_value' do
let(:champ) { build(:champ_linked_drop_down_list, value: '["tata", "tutu"]') }
let(:champ) { Champs::LinkedDropDownListChamp.new(value: '["tata", "tutu"]') }
it { expect(champ.primary_value).to eq('tata') }
it { expect(champ.secondary_value).to eq('tutu') }
end
describe '#pack_value' do
let(:champ) { build(:champ_linked_drop_down_list, primary_value: 'tata', secondary_value: 'tutu') }
before { champ.save }
let(:champ) { Champs::LinkedDropDownListChamp.new(primary_value: 'tata', secondary_value: 'tutu') }
it { expect(champ.value).to eq('["tata","tutu"]') }
end
describe '#primary_value=' do
let!(:champ) { build(:champ_linked_drop_down_list, primary_value: 'tata', secondary_value: 'tutu') }
let(:champ) { Champs::LinkedDropDownListChamp.new(primary_value: 'tata', secondary_value: 'tutu') }
before { champ.primary_value = '' }
@ -23,7 +21,8 @@ describe Champs::LinkedDropDownListChamp do
end
describe '#to_s' do
let(:champ) { build(:champ_linked_drop_down_list, value: [primary_value, secondary_value].to_json) }
let(:champ) { Champs::LinkedDropDownListChamp.new(value: [primary_value, secondary_value].to_json) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_linked_drop_down_list)) }
let(:primary_value) { nil }
let(:secondary_value) { nil }
@ -48,11 +47,12 @@ describe Champs::LinkedDropDownListChamp do
end
describe 'for_export' do
let(:champ) { build(:champ_linked_drop_down_list, value:) }
let(:champ) { Champs::LinkedDropDownListChamp.new(value:) }
let(:value) { [primary_value, secondary_value].to_json }
let(:primary_value) { nil }
let(:secondary_value) { nil }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_linked_drop_down_list)) }
subject { champ.for_export }
context 'with no value' do
@ -78,7 +78,8 @@ describe Champs::LinkedDropDownListChamp do
describe '#mandatory_and_blank' do
let(:value) { "--Primary--\nSecondary" }
subject { described_class.new(type_de_champ: type_de_champ) }
subject { described_class.new }
before { allow(subject).to receive(:type_de_champ).and_return(type_de_champ) }
context 'when the champ is not mandatory' do
let(:type_de_champ) { build(:type_de_champ_linked_drop_down_list, mandatory: false, drop_down_list_value: value) }

View file

@ -32,7 +32,9 @@ describe Champs::MesriChamp, type: :model do
end
describe '#validate' do
let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_mesri)) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :mesri, stable_id: 99 }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { described_class.new(dossier:, stable_id: 99) }
let(:validation_context) { :create }
subject { champ.valid?(validation_context) }

View file

@ -1,7 +1,8 @@
describe Champs::MultipleDropDownListChamp do
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, drop_down_list_value: "val1\r\nval2\r\nval3\r\n[brackets] val4") }
let(:value) { nil }
let(:champ) { build(:champ_multiple_drop_down_list, type_de_champ:, value:) }
let(:champ) { Champs::MultipleDropDownListChamp.new(value:, dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(type_de_champ) }
describe 'validations' do
subject { champ.validate(:champs_public_value) }

View file

@ -1,5 +1,6 @@
describe Champs::PaysChamp, type: :model do
let(:champ) { build(:champ_pays, value: nil) }
let(:champ) { described_class.new(value: nil) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_pays)) }
describe 'value' do
it 'with code' do

View file

@ -1,6 +1,6 @@
describe Champs::PhoneChamp do
let(:champ) { build(:champ_phone) }
let(:champ) { Champs::PhoneChamp.new(dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_phone)) }
describe '#validate' do
it do
expect(champ_with_value(nil).validate(:champs_public_value)).to be_truthy

View file

@ -3,60 +3,52 @@ require 'active_storage_validations/matchers'
describe Champs::PieceJustificativeChamp do
include ActiveStorageValidations::Matchers
describe "skip_validation is not set anymore" do
subject { champ_pj.type_de_champ.skip_pj_validation }
context 'before_save' do
let(:champ_pj) { build (:champ_piece_justificative) }
it { is_expected.to be_falsy }
end
context 'after_save' do
let(:champ_pj) { create (:champ_piece_justificative) }
it { is_expected.to be_falsy }
end
end
describe "validations" do
let(:champ_pj) { create(:champ_piece_justificative) }
subject { champ_pj }
let(:champ) { Champs::PieceJustificativeChamp.new }
subject { champ }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_piece_justificative)) }
context "by default" do
it { is_expected.to validate_size_of(:piece_justificative_file).less_than(Champs::PieceJustificativeChamp::FILE_MAX_SIZE) }
it { is_expected.to validate_content_type_of(:piece_justificative_file).rejecting('application/x-ms-dos-executable') }
it { expect(champ_pj.type_de_champ.skip_pj_validation).to be_falsy }
it { expect(champ.type_de_champ.skip_pj_validation).to be_falsy }
end
context "when validation is disabled" do
before { champ_pj.type_de_champ.update(skip_pj_validation: true) }
before { champ.type_de_champ.update(skip_pj_validation: true) }
it { is_expected.not_to validate_size_of(:piece_justificative_file).less_than(Champs::PieceJustificativeChamp::FILE_MAX_SIZE) }
end
context "when content-type validation is disabled" do
before { champ_pj.type_de_champ.update(skip_content_type_pj_validation: true) }
before { champ.type_de_champ.update(skip_content_type_pj_validation: true) }
it { is_expected.not_to validate_content_type_of(:piece_justificative_file).rejecting('application/x-ms-dos-executable') }
end
end
describe "#for_export" do
let(:champ_pj) { create(:champ_piece_justificative) }
subject { champ_pj.for_export }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
subject { champ.for_export }
it { is_expected.to eq('toto.txt') }
context 'without attached file' do
before { champ_pj.piece_justificative_file.purge }
before { champ.piece_justificative_file.purge }
it { is_expected.to eq(nil) }
end
end
describe '#for_api' do
let(:champ_pj) { create(:champ_piece_justificative) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ) { dossier.champs.first }
before { champ_pj.piece_justificative_file.first.blob.update(virus_scan_result:) }
before { champ.piece_justificative_file.first.blob.update(virus_scan_result:) }
subject { champ_pj.for_api }
subject { champ.for_api }
context 'when file is safe' do
let(:virus_scan_result) { ActiveStorage::VirusScanner::SAFE }

View file

@ -1,5 +1,6 @@
describe Champs::PoleEmploiChamp, type: :model do
let(:champ) { build(:champ_pole_emploi) }
let(:champ) { described_class.new(dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(:type_de_champ_pole_emploi) }
describe 'identifiant' do
before do
@ -32,7 +33,6 @@ describe Champs::PoleEmploiChamp, type: :model do
end
describe '#validate' do
let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_pole_emploi)) }
let(:validation_context) { :create }
subject { champ.valid?(validation_context) }

View file

@ -1,7 +1,17 @@
describe Champs::RegionChamp, type: :model do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :regions, stable_id: 99 }]) }
let(:dossier) { create(:dossier, procedure:) }
describe 'validations' do
describe 'external link' do
let(:champ) { build(:champ_regions, value: nil, external_id: external_id) }
let(:champ) do
described_class
.new(stable_id: 99, dossier:)
.tap do |champ|
champ.value = nil
champ.external_id = external_id
end
end
subject { champ.validate(:champs_public_value) }
context 'when nil' do
let(:external_id) { nil }
@ -29,21 +39,26 @@ describe Champs::RegionChamp, type: :model do
end
describe 'value' do
let(:champ) { create(:champ_regions, value: nil) }
let(:champ) do
described_class
.new(stable_id: 99, dossier:)
.tap do |champ|
champ.value = value
end
end
subject { champ.validate(:champs_public_value) }
before { champ.update_columns(value: value) }
context 'when nil' do
let(:value) { nil }
it { is_expected.to be_truthy }
end
# not real use case, the value= method override value when blank? aka "" to nil
context 'when blank' do
let(:value) { '' }
it { is_expected.to be_falsey }
xit { is_expected.to be_falsey }
end
context 'when included in the region names' do
@ -61,7 +76,9 @@ describe Champs::RegionChamp, type: :model do
end
describe 'value' do
let(:champ) { build(:champ_regions, value: nil) }
let(:champ) do
described_class.new(stable_id: 99, dossier:)
end
it 'with code' do
champ.value = '01'

View file

@ -1,12 +1,15 @@
describe Champs::RNAChamp do
let(:champ) { create(:champ_rna, value: "W182736273") }
let(:champ) { Champs::RNAChamp.new(value: "W182736273", dossier: build(:dossier)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_rna)) }
def with_value(value)
champ.tap { _1.value = value }
end
describe '#valid?' do
it { expect(build(:champ_rna, value: nil).valid?(:champs_public_value)).to be_truthy }
it { expect(build(:champ_rna, value: "2736251627").valid?(:champs_public_value)).to be_falsey }
it { expect(build(:champ_rna, value: "A172736283").valid?(:champs_public_value)).to be_falsey }
it { expect(build(:champ_rna, value: "W1827362718").valid?(:champs_public_value)).to be_falsey }
it { expect(build(:champ_rna, value: "W182736273").valid?(:champs_public_value)).to be_truthy }
it { expect(with_value(nil).validate(:champs_public_value)).to be_truthy }
it { expect(with_value("2736251627").validate(:champs_public_value)).to be_falsey }
it { expect(with_value("A172736283").validate(:champs_public_value)).to be_falsey }
it { expect(with_value("W1827362718").validate(:champs_public_value)).to be_falsey }
it { expect(with_value("W182736273").validate(:champs_public_value)).to be_truthy }
end
describe "#export" do

View file

@ -1,14 +1,15 @@
describe Champs::RNFChamp, type: :model do
let(:champ) { build(:champ_rnf, external_id:) }
let(:stub) { stub_request(:get, "#{url}/#{external_id}").to_return(body:, status:) }
let(:url) { RNFService.new.send(:url) }
let(:body) { Rails.root.join('spec', 'fixtures', 'files', 'api_rnf', "#{response_type}.json").read }
let(:champ) { described_class.new(external_id:) }
let(:external_id) { '075-FDD-00003-01' }
let(:status) { 200 }
let(:body) { Rails.root.join('spec', 'fixtures', 'files', 'api_rnf', "#{response_type}.json").read }
let(:response_type) { 'valid' }
describe 'fetch_external_data' do
subject { stub; champ.fetch_external_data }
let(:url) { RNFService.new.send(:url) }
let(:status) { 200 }
before { stub_request(:get, "#{url}/#{external_id}").to_return(body:, status:) }
subject { champ.fetch_external_data }
context 'success' do
it do
@ -84,7 +85,8 @@ describe Champs::RNFChamp, type: :model do
end
describe 'for_export' do
let(:champ) { build(:champ_rnf, external_id:, data: JSON.parse(body)) }
let(:champ) { described_class.new(external_id:, data: JSON.parse(body)) }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_rnf)) }
it do
expect(champ.for_export(:value)).to eq '075-FDD-00003-01'
expect(champ.for_export(:nom)).to eq 'Fondation SFR'

View file

@ -1,13 +1,16 @@
describe Champs::TitreIdentiteChamp do
describe "#for_export" do
let(:champ_titre_identite) { create(:champ_titre_identite) }
subject { champ_titre_identite.for_export }
it { is_expected.to eq('présent') }
let(:champ) { described_class.new }
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_titre_identite)) }
subject { champ.for_export }
context 'without attached file' do
let(:piece_justificative_file) { double(attached?: true) }
before { allow(champ).to receive(:piece_justificative_file).and_return(piece_justificative_file) }
it { is_expected.to eq('présent') }
end
context 'without attached file' do
before { champ_titre_identite.piece_justificative_file.purge }
it { is_expected.to eq('absent') }
end
end

View file

@ -1,5 +1,6 @@
describe Champs::YesNoChamp do
it_behaves_like "a boolean champ" do
let(:boolean_champ) { build(:champ_yes_no, value: value) }
let(:boolean_champ) { described_class.new(value: value) }
before { allow(boolean_champ).to receive(:type_de_champ).and_return(build(:type_de_champ_yes_no)) }
end
end

View file

@ -1,11 +1,10 @@
describe ChampConditionalConcern do
include Logic
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :decimal_number, stable_id: 99 }, { type: :decimal_number, condition: }]) }
let(:dossier) { create(:dossier, revision: procedure.active_revision) }
let(:types_de_champ) { procedure.active_revision.types_de_champ_public }
let(:champ) { create(:champ_decimal_number, dossier:, type_de_champ: types_de_champ.first, value: '1.1234') }
let(:last_champ) { create(:champ_decimal_number, dossier:, type_de_champ: types_de_champ.last, value: '1.1234') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :decimal_number, stable_id: 99 }, { type: :decimal_number, stable_id: 999, condition: }]) }
let(:dossier) { create(:dossier, :with_populated_champs, revision: procedure.active_revision) }
let(:champ) { dossier.champs.find { _1.stable_id == 99 }.tap { _1.update_column(:value, '1.1234') } }
let(:last_champ) { dossier.champs.find { _1.stable_id == 999 }.tap { _1.update_column(:value, '1.1234') } }
let(:condition) { nil }
describe '#dependent_conditions?' do

View file

@ -17,7 +17,7 @@ RSpec.describe DossierCloneConcern do
describe '#clone' do
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
let(:types_de_champ_public) { [{}] }
let(:types_de_champ_private) { [{}] }
let(:types_de_champ_private) { [] }
let(:fork) { false }
subject(:new_dossier) { dossier.clone(fork:) }
@ -131,67 +131,65 @@ RSpec.describe DossierCloneConcern do
end
context 'for Champs::Repetition with rows, original_champ.repetition and rows are duped' do
let(:dossier) { create(:dossier) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: dossier.procedure) }
let(:champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) }
before { dossier.champs_public << champ_repetition }
let(:types_de_champ_public) { [{ type: :repetition, children: [{}, {}] }] }
let(:champ_repetition) { dossier.champs.first }
let(:cloned_champ_repetition) { new_dossier.champs.first }
it do
expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.count).to eq(4)
expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.ids).not_to eq(champ_repetition.champs.ids)
expect(cloned_champ_repetition.champs.count).to eq(4)
expect(cloned_champ_repetition.champs.ids).not_to eq(champ_repetition.champs.ids)
end
end
context 'for Champs::CarteChamp with geo areas, original_champ.geo_areas are duped' do
let(:dossier) { create(:dossier) }
let(:type_de_champ_carte) { create(:type_de_champ_carte, procedure: dossier.procedure) }
let(:geo_area) { create(:geo_area, :selection_utilisateur, :polygon) }
let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) }
before { dossier.champs_public << champ_carte }
let(:types_de_champ_public) { [{ type: :carte }] }
let(:champ_carte) { dossier.champs.first }
let(:cloned_champ_carte) { new_dossier.champs.first }
it do
expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.count).to eq(1)
expect(Champs::CarteChamp.where(dossier: new_dossier).first.geo_areas.ids).not_to eq(champ_carte.geo_areas.ids)
expect(cloned_champ_carte.geo_areas.count).to eq(2)
expect(cloned_champ_carte.geo_areas.ids).not_to eq(champ_carte.geo_areas.ids)
end
end
context 'for Champs::SiretChamp, original_champ.etablissement is duped' do
let(:dossier) { create(:dossier) }
let(:type_de_champs_siret) { create(:type_de_champ_siret, procedure: dossier.procedure) }
let(:etablissement) { create(:etablissement) }
let(:champ_siret) { create(:champ_siret, type_de_champ: type_de_champs_siret, etablissement: create(:etablissement)) }
before { dossier.champs_public << champ_siret }
let(:types_de_champ_public) { [{ type: :siret }] }
let(:champ_siret) { dossier.champs.first }
let(:cloned_champ_siret) { new_dossier.champs.first }
it do
expect(Champs::SiretChamp.where(dossier: dossier).first.etablissement).not_to be_nil
expect(Champs::SiretChamp.where(dossier: new_dossier).first.etablissement.id).not_to eq(champ_siret.etablissement.id)
it do
expect(champ_siret.etablissement).not_to be_nil
expect(cloned_champ_siret.etablissement.id).not_to eq(champ_siret.etablissement.id)
end
end
end
context 'for Champs::PieceJustificative, original_champ.piece_justificative_file is duped' do
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:champ_piece_justificative) { dossier.champs_public.first }
let(:champ_piece_justificative) { dossier.champs.first }
let(:cloned_champ_piece_justificative) { new_dossier.champs.first }
it { expect(Champs::PieceJustificativeChamp.where(dossier: new_dossier).first.piece_justificative_file.first.blob).to eq(champ_piece_justificative.piece_justificative_file.first.blob) }
it { expect(cloned_champ_piece_justificative.piece_justificative_file.first.blob).to eq(champ_piece_justificative.piece_justificative_file.first.blob) }
end
context 'for Champs::AddressChamp, original_champ.data is duped' do
let(:dossier) { create(:dossier) }
let(:type_de_champs_adress) { create(:type_de_champ_address, procedure: dossier.procedure) }
let(:etablissement) { create(:etablissement) }
let(:champ_address) { create(:champ_address, type_de_champ: type_de_champs_adress, external_id: 'Address', data: { city_code: '75019' }) }
before { dossier.champs_public << champ_address }
let(:types_de_champ_public) { [{ type: :address }] }
let(:champ_address) { dossier.champs.first }
let(:cloned_champ_address) { new_dossier.champs.first }
before { champ_address.update(external_id: 'Address', data: { city_code: '75019' }) }
it do
expect(Champs::AddressChamp.where(dossier: dossier).first.data).not_to be_nil
expect(Champs::AddressChamp.where(dossier: dossier).first.external_id).not_to be_nil
expect(Champs::AddressChamp.where(dossier: new_dossier).first.external_id).to eq(champ_address.external_id)
expect(Champs::AddressChamp.where(dossier: new_dossier).first.data).to eq(champ_address.data)
expect(champ_address.data).not_to be_nil
expect(champ_address.external_id).not_to be_nil
expect(cloned_champ_address.external_id).to eq(champ_address.external_id)
expect(cloned_champ_address.data).to eq(champ_address.data)
end
end
end
context 'private are renewd' do
let(:types_de_champ_private) { [{}] }
it 'reset champs private values' do
expect(new_dossier.champs_private.count).to eq(dossier.champs_private.count)
expect(new_dossier.champs_private.ids).not_to eq(dossier.champs_private.ids)
@ -217,13 +215,13 @@ RSpec.describe DossierCloneConcern do
context "piece justificative champ" do
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:champ_pj) { dossier.champs_public.first }
let(:champ_pj) { dossier.champs.first }
let(:cloned_champ_pj) { new_dossier.champs.first }
it {
champ_pj_fork = Champs::PieceJustificativeChamp.where(dossier: new_dossier).first
expect(champ_pj_fork.piece_justificative_file.first.blob).to eq(champ_pj.piece_justificative_file.first.blob)
expect(champ_pj_fork.created_at).to eq(champ_pj.created_at)
expect(champ_pj_fork.updated_at).to eq(champ_pj.updated_at)
expect(cloned_champ_pj.piece_justificative_file.first.blob).to eq(champ_pj.piece_justificative_file.first.blob)
expect(cloned_champ_pj.created_at).to eq(champ_pj.created_at)
expect(cloned_champ_pj.updated_at).to eq(champ_pj.updated_at)
}
end
@ -258,7 +256,8 @@ RSpec.describe DossierCloneConcern do
before do
champ = dossier.champs.find { _1.stable_id == 992 }
geo_area = build(:geo_area, champ:, geometry: { "i'm" => "invalid" })
geo_area = champ.geo_areas.first
geo_area.geometry = { "i'm" => "invalid" }
geo_area.save!(validate: false)
end

View file

@ -127,7 +127,7 @@ RSpec.describe DossierPrefillableConcern do
private
def find_champ_by_stable_id(dossier, stable_id)
dossier.champs.joins(:type_de_champ).find_by(types_de_champ: { stable_id: stable_id })
dossier.champs.find_by(stable_id:)
end
end
end

View file

@ -87,7 +87,7 @@ describe DossierRebaseConcern do
context 'with a value' do
before do
dossier.champs.find_by(type_de_champ: type_de_champ).update(value: 'a value')
dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'a value')
end
it 'should be true' do
@ -364,7 +364,7 @@ describe DossierRebaseConcern do
expect(dossier.champs_public.size).to eq(6)
expect(dossier.champs.count(&:public?)).to eq(12)
expect(rebased_text_champ.value).to eq(text_champ.value)
expect(rebased_text_champ.type_de_champ_id).not_to eq(text_champ.type_de_champ_id)
expect(rebased_text_champ.type_de_champ).not_to eq(text_champ.type_de_champ)
expect(rebased_datetime_champ.type_champ).to eq(TypeDeChamp.type_champs.fetch(:date))
expect(rebased_datetime_champ.value).to be_nil
expect(rebased_repetition_champ.rows.size).to eq(2)
@ -562,7 +562,7 @@ describe DossierRebaseConcern do
context 'and the cadastre are removed' do
before do
dossier.champs_public.first.update(value: 'v1', geo_areas: [create(:geo_area, :cadastre)])
dossier.champs_public.first.update(value: 'v1', geo_areas: [build(:geo_area, :cadastre)])
stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1')
tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id)
@ -623,7 +623,7 @@ describe DossierRebaseConcern do
def first_champ = dossier.champs_public.first
before do
first_champ.update(value: 'v1', external_id: '123', geo_areas: [create(:geo_area)])
first_champ.update(value: 'v1', external_id: '123', geo_areas: [build(:geo_area)])
first_champ.update(data: { a: 1 })
first_champ.piece_justificative_file.attach(

View file

@ -1,6 +1,12 @@
RSpec.describe RNAChampAssociationFetchableConcern do
describe '.fetch_association!' do
let!(:champ) { create(:champ_rna, data: "not nil data", value: 'W173847273') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :rna }]) }
let(:dossier) do
create(:dossier, :with_populated_champs, procedure:).tap do
_1.champs.first.update(data: "not nil data", value: 'W173847273')
end
end
let!(:champ) { dossier.champs.first }
before do
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v4\/djepva\/api-association\/associations\/open_data\/#{rna}/)

View file

@ -3,7 +3,9 @@ RSpec.describe SiretChampEtablissementFetchableConcern do
let(:api_etablissement_status) { 200 }
let(:api_etablissement_body) { File.read('spec/fixtures/files/api_entreprise/etablissements.json') }
let(:token_expired) { false }
let!(:champ) { create(:champ_siret) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :siret }]) }
let(:dossier) { create(:dossier, procedure:) }
let!(:champ) { dossier.champs.first.tap { _1.update!(etablissement: create(:etablissement)) } }
before do
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v3\/insee\/sirene\/etablissements\/#{siret}/)

View file

@ -257,7 +257,7 @@ describe TagsSubstitutionConcern, type: :model do
context 'and the champ has a primary value' do
before do
dossier.champs_public.find_by(type_de_champ: type_de_champ).update(primary_value: 'primo')
dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(primary_value: 'primo')
dossier.reload
end
@ -265,7 +265,7 @@ describe TagsSubstitutionConcern, type: :model do
context 'and the champ has a secondary value' do
before do
dossier.champs_public.find_by(type_de_champ: type_de_champ).update(secondary_value: 'secundo')
dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(secondary_value: 'secundo')
dossier.reload
end

View file

@ -10,28 +10,40 @@ describe TreeableConcern do
subject { ChampsToTree.new(types_de_champ:).root }
describe "to_tree" do
let(:header_1) { build(:champ_header_section_level_1).type_de_champ }
let(:header_1_2) { build(:champ_header_section_level_2).type_de_champ }
let(:header_2) { build(:champ_header_section_level_1).type_de_champ }
let(:champ_text) { build(:champ_text).type_de_champ }
let(:champ_textarea) { build(:champ_textarea).type_de_champ }
let(:champ_explication) { build(:champ_explication).type_de_champ }
let(:champ_communes) { build(:champ_communes).type_de_champ }
let(:procedure) { create(:procedure, types_de_champ_public:) }
let(:types_de_champ_public) { [] }
let(:types_de_champ) { procedure.active_revision.types_de_champ_public }
let(:header_1) { { type: :header_section, level: 1, stable_id: 99 } }
let(:header_1_2) { { type: :header_section, level: 2, stable_id: 199 } }
let(:header_2) { { type: :header_section, level: 1, stable_id: 299 } }
let(:champ_text) { { stable_id: 399 } }
let(:champ_textarea) { { type: :textarea, stable_id: 499 } }
let(:champ_explication) { { type: :explication, stable_id: 599 } }
let(:champ_communes) { { type: :communes, stable_id: 699 } }
let(:header_1_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 99 } }
let(:header_1_2_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 199 } }
let(:header_2_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 299 } }
let(:champ_text_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 399 } }
let(:champ_textarea_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 499 } }
let(:champ_explication_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 599 } }
let(:champ_communes_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 699 } }
context 'without section' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
champ_text, champ_textarea
]
end
it 'inlines champs at root level' do
expect(subject.size).to eq(types_de_champ.size)
expect(subject).to eq(types_de_champ)
expect(subject).to eq([champ_text_tdc, champ_textarea_tdc])
end
end
context 'with header_section and champs' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
header_1,
champ_explication,
@ -44,14 +56,16 @@ describe TreeableConcern do
it 'wraps champs within preview header section' do
expect(subject.size).to eq(2)
expect(subject).to eq([
[header_1, champ_explication, champ_text],
[header_2, champ_textarea]
[header_1_tdc, champ_explication_tdc, champ_text_tdc],
[header_2_tdc, champ_textarea_tdc]
])
end
end
context 'leading champs, and in between sections only' do
let(:types_de_champ) do
let(:champ_textarea_bis) { { type: :textarea, stable_id: 799 } }
let(:champ_textarea_bis_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 799 } }
let(:types_de_champ_public) do
[
champ_text,
champ_textarea,
@ -59,22 +73,22 @@ describe TreeableConcern do
champ_explication,
champ_communes,
header_2,
champ_textarea
champ_textarea_bis
]
end
it 'chunk by uniq champs' do
expect(subject.size).to eq(4)
expect(subject).to eq([
champ_text,
champ_textarea,
[header_1, champ_explication, champ_communes],
[header_2, champ_textarea]
champ_text_tdc,
champ_textarea_tdc,
[header_1_tdc, champ_explication_tdc, champ_communes_tdc],
[header_2_tdc, champ_textarea_bis_tdc]
])
end
end
context 'with one sub sections' do
let(:types_de_champ) do
let(:types_de_champ_public) do
[
header_1,
champ_explication,
@ -87,18 +101,22 @@ describe TreeableConcern do
it 'chunk by uniq champs' do
expect(subject.size).to eq(2)
expect(subject).to eq([
[header_1, champ_explication, [header_1_2, champ_communes]],
[header_2, champ_textarea]
[header_1_tdc, champ_explication_tdc, [header_1_2_tdc, champ_communes_tdc]],
[header_2_tdc, champ_textarea_tdc]
])
end
end
context 'with consecutive subsection' do
let(:header_1) { build(:champ_header_section_level_1).type_de_champ }
let(:header_1_2_1) { build(:champ_header_section_level_2).type_de_champ }
let(:header_1_2_2) { build(:champ_header_section_level_2).type_de_champ }
let(:header_1_2_3) { build(:champ_header_section_level_2).type_de_champ }
let(:types_de_champ) do
let(:header_1_2_1) { { type: :header_section, level: 2, stable_id: 799 } }
let(:header_1_2_2) { { type: :header_section, level: 2, stable_id: 899 } }
let(:header_1_2_3) { { type: :header_section, level: 2, stable_id: 999 } }
let(:header_1_2_1_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 799 } }
let(:header_1_2_2_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 899 } }
let(:header_1_2_3_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 999 } }
let(:types_de_champ_public) do
[
header_1,
header_1_2_1,
@ -113,19 +131,20 @@ describe TreeableConcern do
expect(subject.size).to eq(1)
expect(subject).to eq([
[
header_1,
[header_1_2_1, champ_text],
[header_1_2_2, champ_textarea],
[header_1_2_3, champ_communes]
header_1_tdc,
[header_1_2_1_tdc, champ_text_tdc],
[header_1_2_2_tdc, champ_textarea_tdc],
[header_1_2_3_tdc, champ_communes_tdc]
]
])
end
end
context 'with one sub sections and one subsub section' do
let(:header_1_2_3) { build(:champ_header_section_level_3).type_de_champ }
let(:header_1_2_3) { { type: :header_section, level: 3, stable_id: 799 } }
let(:header_1_2_3_tdc) { procedure.active_revision.types_de_champ_public.find { _1.stable_id == 799 } }
let(:types_de_champ) do
let(:types_de_champ_public) do
[
header_1,
champ_explication,
@ -142,19 +161,19 @@ describe TreeableConcern do
expect(subject.size).to eq(2)
expect(subject).to eq([
[
header_1,
champ_explication,
header_1_tdc,
champ_explication_tdc,
[
header_1_2,
champ_communes,
header_1_2_tdc,
champ_communes_tdc,
[
header_1_2_3, champ_text
header_1_2_3_tdc, champ_text_tdc
]
]
],
[
header_2,
champ_textarea
header_2_tdc,
champ_textarea_tdc
]
])
end

View file

@ -1575,12 +1575,12 @@ describe Dossier, type: :model do
end
context "when a SIRET champ has etablissement in degraded mode" do
let(:dossier_incomplete) { create(:dossier, :en_instruction) }
let(:dossier_ok) { create(:dossier, :en_instruction) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :siret }]) }
let(:dossier_incomplete) { create(:dossier, :en_instruction, :with_populated_champs, procedure:) }
let(:dossier_ok) { create(:dossier, :en_instruction, :with_populated_champs, procedure:) }
before do
dossier_incomplete.champs_public << create(:champ_siret, dossier: dossier_incomplete, etablissement: Etablissement.new(siret: build(:etablissement).siret))
dossier_ok.champs_public << create(:champ_siret, dossier: dossier_ok)
dossier_incomplete.champs.first.update(etablissement: Etablissement.new(siret: build(:etablissement).siret))
end
it "can't accepter" do
@ -1837,43 +1837,42 @@ describe Dossier, type: :model do
end
describe '#geo_data' do
let(:dossier) { create(:dossier) }
let(:type_de_champ_carte) { create(:type_de_champ_carte, procedure: dossier.procedure) }
let(:geo_area) { create(:geo_area) }
let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) }
let(:procedure) { create(:procedure, types_de_champ_public:, types_de_champ_private:) }
let(:dossier) { create(:dossier, :with_populated_champs, :with_populated_annotations, procedure:) }
let(:types_de_champ_public) { [] }
let(:types_de_champ_private) { [] }
context "without data" do
it { expect(dossier.geo_data?).to be_falsey }
end
context "with geo data in public champ" do
before do
dossier.champs_public << champ_carte
end
let(:types_de_champ_public) { [{ type: :carte }] }
it { expect(dossier.geo_data?).to be_truthy }
end
context "with geo data in private champ" do
before do
dossier.champs_private << champ_carte
end
let(:types_de_champ_private) { [{ type: :carte }] }
it { expect(dossier.geo_data?).to be_truthy }
end
it "should solve N+1 problem" do
dossier.champs_public << create_list(:champ_carte, 3, type_de_champ: type_de_champ_carte, geo_areas: [create(:geo_area)])
dossier.champs_for_revision
context "should solve N+1 problem" do
let(:types_de_champ_public) { [{ type: :carte }, { type: :carte }, { type: :carte }] }
count = 0
it do
dossier.champs_for_revision
callback = lambda { |*_args| count += 1 }
ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
dossier.geo_data?
count = 0
callback = lambda { |*_args| count += 1 }
ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
dossier.geo_data?
end
expect(count).to eq(1)
end
expect(count).to eq(1)
end
end
@ -1930,13 +1929,13 @@ describe Dossier, type: :model do
end
describe "to_feature_collection" do
let(:dossier) { create(:dossier) }
let(:type_de_champ_carte) { create(:type_de_champ_carte, procedure: dossier.procedure) }
let(:geo_area) { create(:geo_area, :selection_utilisateur, :polygon) }
let(:champ_carte) { create(:champ_carte, type_de_champ: type_de_champ_carte, geo_areas: [geo_area]) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :carte }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ_carte) { dossier.champs.first }
let(:geo_area) { build(:geo_area, :selection_utilisateur, :polygon) }
before do
dossier.champs_public << champ_carte
champ_carte.update(geo_areas: [geo_area])
end
it 'should have all champs carto' do
@ -2120,16 +2119,14 @@ describe Dossier, type: :model do
end
describe "remove_titres_identite!" do
let(:dossier) { create(:dossier, :en_instruction, :followed, :with_individual) }
let(:type_de_champ_titre_identite) { create(:type_de_champ_titre_identite, procedure: dossier.procedure) }
let(:champ_titre_identite) { create(:champ_titre_identite, type_de_champ: type_de_champ_titre_identite) }
let(:type_de_champ_titre_identite_vide) { create(:type_de_champ_titre_identite, procedure: dossier.procedure) }
let(:champ_titre_identite_vide) { create(:champ_titre_identite, type_de_champ: type_de_champ_titre_identite_vide) }
let(:declarative_with_state) { nil }
let(:procedure) { create(:procedure, declarative_with_state:, types_de_champ_public: [{ type: :titre_identite }, { type: :titre_identite }]) }
let(:dossier) { create(:dossier, :en_instruction, :followed, :with_populated_champs, procedure:) }
let(:champ_titre_identite) { dossier.champs.first }
let(:champ_titre_identite_vide) { dossier.champs.second }
before do
champ_titre_identite_vide.piece_justificative_file.purge
dossier.champs_public << champ_titre_identite
dossier.champs_public << champ_titre_identite_vide
end
it "clean up titres identite on accepter" do
@ -2154,7 +2151,8 @@ describe Dossier, type: :model do
end
context 'en_construction' do
let(:dossier) { create(:dossier, :en_construction, :followed, :with_individual, :with_declarative_accepte) }
let(:declarative_with_state) { 'accepte' }
let(:dossier) { create(:dossier, :en_construction, :followed, :with_populated_champs, procedure:) }
it "clean up titres identite on accepter_automatiquement" do
expect(champ_titre_identite.piece_justificative_file.attached?).to be_truthy

View file

@ -1,6 +1,11 @@
describe Champs::EngagementJuridiqueChamp do
describe 'validation' do
let(:champ) { build(:champ_engagement_juridique, value: value) }
let(:champ) do
described_class
.new(dossier: build(:dossier))
.tap { _1.value = value }
end
before { allow(champ).to receive(:type_de_champ).and_return(build(:type_de_champ_engagement_juridique)) }
subject { champ.validate(:champs_public_value) }
context 'with [A-Z]' do

View file

@ -119,6 +119,9 @@ describe Etablissement do
let(:etablissement) { create(:etablissement, dossier: build(:dossier)) }
it "schedule update search terms" do
etablissement
etablissement.dossier.debounce_index_search_terms_flag.remove
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
etablissement.update(entreprise_nom: "nom")
end

View file

@ -136,46 +136,33 @@ describe ExportTemplate do
end
context 'for pj' do
let(:dossier) { procedure.dossiers.first }
let(:type_de_champ_pj) { create(:type_de_champ_piece_justificative, stable_id: 3, procedure:) }
let(:champ_pj) { create(:champ_piece_justificative, type_de_champ: type_de_champ_pj) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ_pj) { dossier.champs.find(&:piece_justificative?) }
let(:attachment) { ActiveStorage::Attachment.new(name: 'pj', record: champ_pj, blob: ActiveStorage::Blob.new(filename: "superpj.png")) }
before do
dossier.champs_public << champ_pj
end
it 'returns pj and custom name for pj' do
expect(export_template.attachment_and_path(dossier, attachment, champ: champ_pj)).to eq([attachment, "DOSSIER_#{dossier.id}/superpj_justif-1.png"])
end
end
context 'pj repetable' do
let(:procedure) do
create(:procedure_with_dossiers, :for_individual, types_de_champ_public: [{ type: :repetition, mandatory: true, children: [{ libelle: 'sub type de champ' }] }])
end
let(:type_de_champ_repetition) do
repetition = draft.types_de_champ_public.repetition.first
repetition.update(stable_id: 3333)
repetition
end
let(:procedure) { create(:procedure, :for_individual, types_de_champ_public:) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:draft) { procedure.draft_revision }
let(:dossier) { procedure.dossiers.first }
let(:type_de_champ_pj) do
draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:piece_justificative),
libelle: "pj repet",
stable_id: 10,
parent_stable_id: type_de_champ_repetition.stable_id
})
let(:types_de_champ_public) do
[
{
type: :repetition,
stable_id: 3333,
mandatory: true, children: [
{ type: :text, libelle: 'sub type de champ' },
{ type: :piece_justificative, stable_id: 10, libelle: 'pj repet' }
]
}
]
end
let(:champ_pj) { create(:champ_piece_justificative, type_de_champ: type_de_champ_pj) }
let(:champ_pj) { dossier.champs.find(&:piece_justificative?) }
let(:attachment) { ActiveStorage::Attachment.new(name: 'pj', record: champ_pj, blob: ActiveStorage::Blob.new(filename: "superpj.png")) }
before do
dossier.champs_public << champ_pj
end
it 'rename repetable pj' do
expect(export_template.attachment_and_path(dossier, attachment, champ: champ_pj)).to eq([attachment, "DOSSIER_#{dossier.id}/pj_repet_#{dossier.id}-1.png"])
end
@ -201,13 +188,14 @@ describe ExportTemplate do
end
describe '#tiptap_convert_pj' do
let(:type_de_champ_pj) { create(:type_de_champ_piece_justificative, stable_id: 3, libelle: 'Justificatif de domicile', procedure:) }
let(:champ_pj) { create(:champ_piece_justificative, type_de_champ: type_de_champ_pj) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative, stable_id: 3, libelle: 'Justificatif de domicile' }]) }
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:champ_pj) { dossier.champs.first }
let(:attachment) { ActiveStorage::Attachment.new(name: 'pj', record: champ_pj, blob: ActiveStorage::Blob.new(filename: "superpj.png")) }
it 'convert pj' do
attachment
expect(export_template.tiptap_convert_pj(dossier, type_de_champ_pj.stable_id, attachment)).to eq "superpj_justif"
expect(export_template.tiptap_convert_pj(dossier, 3, attachment)).to eq "superpj_justif"
end
end

View file

@ -20,8 +20,8 @@ describe Logic::BinaryOperator do
end
describe '#sources' do
let(:champ) { create(:champ_integer_number, value: nil) }
let(:champ2) { create(:champ_integer_number, value: nil) }
let(:champ) { Champs::IntegerNumberChamp.new(value: nil, stable_id: 1) }
let(:champ2) { Champs::IntegerNumberChamp.new(value: nil, stable_id: 2) }
it { expect(two_greater_than_one.sources).to eq([]) }
it { expect(greater_than(champ_value(champ.stable_id), constant(2)).sources).to eq([champ.stable_id]) }
@ -32,7 +32,10 @@ end
describe Logic::GreaterThan do
include Logic
let(:champ) { create(:champ_integer_number, value: nil) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :integer_number }]) }
let(:tdc) { procedure.active_revision.types_de_champ.first }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { Champs::IntegerNumberChamp.new(value: nil, stable_id: tdc.stable_id, dossier:) }
it 'computes' do
expect(greater_than(constant(1), constant(1)).compute).to be(false)
@ -43,8 +46,6 @@ end
describe Logic::GreaterThanEq do
include Logic
let(:champ) { create(:champ_integer_number, value: nil) }
it 'computes' do
expect(greater_than_eq(constant(0), constant(1)).compute).to be(false)
expect(greater_than_eq(constant(1), constant(1)).compute).to be(true)

View file

@ -2,11 +2,18 @@ describe Logic::ChampValue do
include Logic
describe '#compute' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: tdc_type, drop_down_other: }]) }
let(:drop_down_other) { nil }
let(:tdc_type) { :text }
let(:tdc) { procedure.active_revision.types_de_champ.first }
let(:dossier) { create(:dossier, procedure:) }
subject { champ_value(champ.stable_id).compute([champ]) }
context 'yes_no tdc' do
let(:tdc_type) { :yes_no }
let(:champ) { Champs::YesNoChamp.new(value: value, stable_id: tdc.stable_id, dossier:) }
let(:value) { 'true' }
let(:champ) { create(:champ_yes_no, value: value) }
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:boolean) }
@ -30,92 +37,117 @@ describe Logic::ChampValue do
end
context 'integer tdc' do
let(:champ) { create(:champ_integer_number, value: '42') }
let(:tdc_type) { :integer_number }
let(:champ) { Champs::IntegerNumberChamp.new(value:, stable_id: tdc.stable_id, dossier:) }
let(:value) { '42' }
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:number) }
it { is_expected.to eq(42) }
context 'with a blank value' do
let(:champ) { create(:champ_integer_number, value: '') }
let(:value) { '' }
it { is_expected.to be nil }
end
end
context 'decimal tdc' do
let(:champ) { create(:champ_decimal_number, value: '42.01') }
let(:tdc_type) { :decimal_number }
let(:champ) { Champs::DecimalNumberChamp.new(value:, stable_id: tdc.stable_id, dossier:) }
let(:value) { '42.01' }
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:number) }
it { is_expected.to eq(42.01) }
end
context 'dropdown tdc' do
let(:champ) { create(:champ_drop_down_list, value: 'val1') }
let(:tdc_type) { :drop_down_list }
let(:champ) { Champs::DropDownListChamp.new(value:, other:, stable_id: tdc.stable_id, dossier:) }
let(:value) { 'val1' }
let(:other) { nil }
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:enum) }
it { is_expected.to eq('val1') }
it { expect(champ_value(champ.stable_id).options([champ.type_de_champ])).to match_array([["val1", "val1"], ["val2", "val2"], ["val3", "val3"]]) }
context 'with other enabled' do
let(:champ) { create(:champ_drop_down_list, value: 'val1', other: true) }
let(:tdc_type) { :drop_down_list }
let(:drop_down_other) { true }
it { is_expected.to eq('val1') }
it { expect(champ_value(champ.stable_id).options([champ.type_de_champ])).to match_array([["val1", "val1"], ["val2", "val2"], ["val3", "val3"], ["Autre", "__other__"]]) }
end
context 'with other filled' do
let(:champ) { create(:champ_drop_down_list, value: 'other value', other: true) }
context 'with other filled' do
let(:other) { true }
it { is_expected.to eq(Champs::DropDownListChamp::OTHER) }
it { is_expected.to eq(Champs::DropDownListChamp::OTHER) }
end
end
end
context 'checkbox tdc' do
let(:champ) { create(:champ_checkbox, value: 'true') }
let(:tdc_type) { :checkbox }
let(:champ) { Champs::CheckboxChamp.new(value:, stable_id: tdc.stable_id, dossier:) }
let(:value) { 'true' }
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:boolean) }
it { is_expected.to eq(true) }
end
context 'departement tdc' do
let(:champ) { create(:champ_departements, value: '02') }
let(:tdc_type) { :departements }
let(:champ) { Champs::DepartementChamp.new(value:, stable_id: tdc.stable_id, dossier:) }
let(:value) { '02' }
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:departement_enum) }
it { is_expected.to eq({ value: '02', code_region: '32' }) }
end
context 'region tdc' do
let(:champ) { create(:champ_regions, value: 'La Réunion') }
let(:tdc_type) { :regions }
let(:champ) { Champs::RegionChamp.new(value:, stable_id: tdc.stable_id, dossier:) }
let(:value) { 'La Réunion' }
it { is_expected.to eq('04') }
end
context 'commune tdc' do
let(:champ) { create(:champ_communes, code_postal: '92500', external_id: '92063') }
let(:tdc_type) { :communes }
let(:champ) do
Champs::CommuneChamp.new(code_postal:, external_id:, stable_id: tdc.stable_id, dossier:)
.tap { |c| c.send(:on_codes_change) } # private method called before save to fill value, which is required for compute
end
let(:code_postal) { '92500' }
let(:external_id) { '92063' }
it { is_expected.to eq({ code_departement: '92', code_region: '11' }) }
it do
is_expected.to eq({ code_departement: '92', code_region: '11' })
end
end
context 'epci tdc' do
let(:champ) { build(:champ_epci, code_departement: '43') }
before do
champ.save!
champ.update_columns(external_id: '244301016', value: 'CC des Sucs')
let(:tdc_type) { :epci }
let(:champ) do
Champs::EpciChamp.new(code_departement:, external_id:, stable_id: tdc.stable_id, dossier:)
.tap { |c| c.send(:on_epci_name_changes) } # private method called before save to fill value, which is required for compute
end
let(:code_departement) { '43' }
let(:external_id) { '244301016' }
it { is_expected.to eq({ code_departement: '43', code_region: '84' }) }
end
describe 'errors' do
let(:champ) { create(:champ) }
let(:tdc_type) { :number }
let(:champ) { Champs::IntegerNumberChamp.new(value: nil, stable_id: tdc.stable_id, dossier:) }
it { expect(champ_value(champ.stable_id).errors([champ.type_de_champ])).to be_empty }
it { expect(champ_value(champ.stable_id).errors([])).to eq([{ type: :not_available }]) }
end
describe '#sources' do
let(:champ) { create(:champ) }
let(:tdc_type) { :number }
let(:champ) { Champs::IntegerNumberChamp.new(value: nil, stable_id: tdc.stable_id, dossier:) }
it { expect(champ_value(champ.stable_id).sources).to eq([champ.stable_id]) }
end

View file

@ -1,7 +1,10 @@
describe Logic::ExcludeOperator do
include Logic
let(:champ) { create(:champ_multiple_drop_down_list, value: '["val1", "val2"]') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :multiple_drop_down_list }]) }
let(:tdc) { procedure.active_revision.types_de_champ.first }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { Champs::MultipleDropDownListChamp.new(value: '["val1", "val2"]', stable_id: tdc.stable_id, dossier:) }
describe '#compute' do
it { expect(ds_exclude(champ_value(champ.stable_id), constant('val1')).compute([champ])).to be(false) }

View file

@ -1,8 +1,22 @@
describe Logic::InDepartementOperator do
include Logic
let(:champ_commune) { create(:champ_communes, code_postal: '92500', external_id: '92063') }
let(:champ_epci) { create(:champ_epci, code_departement: '02', code_region: "32") }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :communes }, { type: :epci }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:tdc_commune) { procedure.active_revision.types_de_champ.first }
let(:champ_commune) do
Champs::CommuneChamp.new(code_postal: '92500', external_id: '92063', stable_id: tdc_commune.stable_id, dossier:)
.tap { |c| c.send(:on_codes_change) } # private method called before save to fill value, which is required for compute
end
let(:tdc_epci) { procedure.active_revision.types_de_champ.second }
let(:champ_epci) do
Champs::EpciChamp.new(code_departement: '43', code_region: '32', external_id: '244301016', stable_id: tdc_epci.stable_id, dossier:)
.tap do |c|
c.send(:on_epci_name_changes)
end # private method called before save to fill value, which is required for compute
end
describe '#compute' do
context 'commune' do
@ -11,8 +25,7 @@ describe Logic::InDepartementOperator do
context 'epci' do
it do
champ_epci.update_columns(external_id: "200071991", value: "CC Retz en Valois")
expect(ds_in_departement(champ_value(champ_epci.stable_id), constant('02')).compute([champ_epci])).to be(true)
expect(ds_in_departement(champ_value(champ_epci.stable_id), constant('43')).compute([champ_epci])).to be(true)
end
end
end

View file

@ -1,9 +1,25 @@
describe Logic::InRegionOperator do
include Logic
let(:champ_commune) { create(:champ_communes, code_postal: '92500', external_id: '92063') }
let(:champ_epci) { create(:champ_epci, code_departement: '02', code_region: "32") }
let(:champ_departement) { create(:champ_departements, value: '01', code_region: '84') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :communes }, { type: :epci }, { type: :departements }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:tdc_commune) { procedure.active_revision.types_de_champ.first }
let(:champ_commune) do
Champs::CommuneChamp.new(code_postal: '92500', external_id: '92063', stable_id: tdc_commune.stable_id, dossier:)
.tap { |c| c.send(:on_codes_change) } # private method called before save to fill value, which is required for compute
end
let(:tdc_epci) { procedure.active_revision.types_de_champ.second }
let(:champ_epci) do
Champs::EpciChamp.new(code_departement: '43', code_region: '32', external_id: '244301016', stable_id: tdc_epci.stable_id, dossier:)
.tap do |c|
c.send(:on_epci_name_changes)
end # private method called before save to fill value, which is required for compute
end
let(:tdc_departement) { procedure.active_revision.types_de_champ.third }
let(:champ_departement) { Champs::DepartementChamp.new(value: '01', stable_id: tdc_departement.stable_id, dossier:) }
describe '#compute' do
context 'commune' do
@ -12,8 +28,7 @@ describe Logic::InRegionOperator do
context 'epci' do
it do
champ_epci.update_columns(external_id: "200071991", value: "CC Retz en Valois")
expect(ds_in_region(champ_value(champ_epci.stable_id), constant('32')).compute([champ_epci])).to be(true)
expect(ds_in_region(champ_value(champ_epci.stable_id), constant('84')).compute([champ_epci])).to be(true)
end
end

View file

@ -1,7 +1,10 @@
describe Logic::IncludeOperator do
include Logic
let(:champ) { create(:champ_multiple_drop_down_list, value: '["val1", "val2"]') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :multiple_drop_down_list }]) }
let(:tdc) { procedure.active_revision.types_de_champ.first }
let(:dossier) { create(:dossier, procedure:) }
let(:champ) { Champs::MultipleDropDownListChamp.new(value: '["val1", "val2"]', stable_id: tdc.stable_id, dossier:) }
describe '#compute' do
it { expect(ds_include(champ_value(champ.stable_id), constant('val1')).compute([champ])).to be(true) }

View file

@ -1,8 +1,22 @@
describe Logic::NotInDepartementOperator do
include Logic
let(:champ_commune) { create(:champ_communes, code_postal: '92500', external_id: '92063') }
let(:champ_epci) { create(:champ_epci, code_departement: '02', code_region: "32") }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :communes }, { type: :epci }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:tdc_commune) { procedure.active_revision.types_de_champ.first }
let(:champ_commune) do
Champs::CommuneChamp.new(code_postal: '92500', external_id: '92063', stable_id: tdc_commune.stable_id, dossier:)
.tap { |c| c.send(:on_codes_change) } # private method called before save to fill value, which is required for compute
end
let(:tdc_epci) { procedure.active_revision.types_de_champ.second }
let(:champ_epci) do
Champs::EpciChamp.new(code_departement: '43', code_region: '32', external_id: '244301016', stable_id: tdc_epci.stable_id, dossier:)
.tap do |c|
c.send(:on_epci_name_changes)
end # private method called before save to fill value, which is required for compute
end
describe '#compute' do
context 'commune' do
@ -14,8 +28,7 @@ describe Logic::NotInDepartementOperator do
context 'epci' do
it do
champ_epci.update_columns(external_id: "200071991", value: "CC Retz en Valois")
expect(ds_not_in_departement(champ_value(champ_epci.stable_id), constant('02')).compute([champ_epci])).to be(false)
expect(ds_not_in_departement(champ_value(champ_epci.stable_id), constant('43')).compute([champ_epci])).to be(false)
expect(ds_not_in_departement(champ_value(champ_epci.stable_id), constant('03')).compute([champ_epci])).to be(true)
end
end

View file

@ -1,9 +1,29 @@
describe Logic::NotInRegionOperator do
include Logic
let(:champ_commune) { create(:champ_communes, code_postal: '92500', external_id: '92063') }
let(:champ_epci) { create(:champ_epci, code_departement: '02', code_region: "32") }
let(:champ_departement) { create(:champ_departements, value: '01', code_region: '84') }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :communes }, { type: :epci }, { type: :departements }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:tdc_commune) { procedure.active_revision.types_de_champ.first }
let(:champ_commune) do
Champs::CommuneChamp.new(code_postal: '92500', external_id: '92063', stable_id: tdc_commune.stable_id, dossier:)
.tap { |c| c.send(:on_codes_change) } # private method called before save to fill value, which is required for compute
end
let(:tdc_epci) { procedure.active_revision.types_de_champ.second }
let(:champ_epci) do
Champs::EpciChamp.new(code_departement: '43', code_region: '32', external_id: '244301016', stable_id: tdc_epci.stable_id, dossier:)
.tap do |c|
c.send(:on_epci_name_changes)
end # private method called before save to fill value, which is required for compute
end
let(:tdc_departement) { procedure.active_revision.types_de_champ.third }
let(:champ_departement) { Champs::DepartementChamp.new(value: '01', stable_id: tdc_departement.stable_id, dossier:) }
# let(:champ_commune) { create(:champ_communes, code_postal: '92500', external_id: '92063') }
# let(:champ_epci) { create(:champ_epci, code_departement: '02', code_region: "32") }
# let(:champ_departement) { create(:champ_departements, value: '01', code_region: '84') }
describe '#compute' do
context 'commune' do
@ -15,8 +35,7 @@ describe Logic::NotInRegionOperator do
context 'epci' do
it do
champ_epci.update_columns(external_id: "200071991", value: "CC Retz en Valois")
expect(ds_not_in_region(champ_value(champ_epci.stable_id), constant('32')).compute([champ_epci])).to be(false)
expect(ds_not_in_region(champ_value(champ_epci.stable_id), constant('84')).compute([champ_epci])).to be(false)
expect(ds_not_in_region(champ_value(champ_epci.stable_id), constant('11')).compute([champ_epci])).to be(true)
end
end

View file

@ -135,11 +135,12 @@ RSpec.describe PrefillChamps do
let(:type_de_champ_child) { procedure.published_revision.children_of(type_de_champ).first }
let(:type_de_champ_child_value) { "value" }
let(:type_de_champ_child_value2) { "value2" }
let(:child_champs) { dossier.champs.where(stable_id: type_de_champ_child.stable_id) }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => [{ "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value }, { "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value2 }] } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_champs_array).to match([{ id: type_de_champ_child.champ.first.id, value: type_de_champ_child_value }, { id: type_de_champ_child.champ.second.id, value: type_de_champ_child_value2 }])
expect(prefill_champs_array).to match([{ id: child_champs.first.id, value: type_de_champ_child_value }, { id: child_champs.second.id, value: type_de_champ_child_value2 }])
end
end
@ -177,11 +178,12 @@ RSpec.describe PrefillChamps do
let(:type_de_champ_child) { procedure.published_revision.children_of(type_de_champ).first }
let(:type_de_champ_child_value) { "value" }
let(:type_de_champ_child_value2) { "value2" }
let(:child_champs) { dossier.champs.where(stable_id: type_de_champ_child.stable_id) }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => [{ "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value }, { "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value2 }] } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_champs_array).to match([{ id: type_de_champ_child.champ.first.id, value: type_de_champ_child_value }, { id: type_de_champ_child.champ.second.id, value: type_de_champ_child_value2 }])
expect(prefill_champs_array).to match([{ id: child_champs.first.id, value: type_de_champ_child_value }, { id: child_champs.second.id, value: type_de_champ_child_value2 }])
end
end
@ -237,7 +239,7 @@ RSpec.describe PrefillChamps do
private
def find_champ_by_stable_id(dossier, stable_id)
dossier.champs.joins(:type_de_champ).find_by(types_de_champ: { stable_id: stable_id })
dossier.champs.find_by(stable_id:)
end
def attributes(champ, value)

View file

@ -595,8 +595,8 @@ describe ProcedurePresentation do
context 'with single value' do
before do
kept_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'keep me')
discarded_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'discard me')
kept_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'keep me')
discarded_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'discard me')
end
it { is_expected.to contain_exactly(kept_dossier.id) }
@ -613,9 +613,9 @@ describe ProcedurePresentation do
let(:other_kept_dossier) { create(:dossier, procedure: procedure) }
before do
kept_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'keep me')
discarded_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'discard me')
other_kept_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'and me too')
kept_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'keep me')
discarded_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'discard me')
other_kept_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'and me too')
end
it 'returns every dossier that matches any of the search criteria for a given column' do
@ -628,8 +628,8 @@ describe ProcedurePresentation do
let(:types_de_champ_public) { [{ type: :yes_no }] }
before do
kept_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'true')
discarded_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(value: 'false')
kept_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'true')
discarded_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(value: 'false')
end
it { is_expected.to contain_exactly(kept_dossier.id) }
@ -640,8 +640,8 @@ describe ProcedurePresentation do
let(:types_de_champ_public) { [{ type: :departements }] }
before do
kept_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(external_id: '13')
discarded_dossier.champs_public.find_by(type_de_champ: type_de_champ).update(external_id: '69')
kept_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(external_id: '13')
discarded_dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(external_id: '69')
end
it { is_expected.to contain_exactly(kept_dossier.id) }
@ -656,8 +656,8 @@ describe ProcedurePresentation do
let(:type_de_champ_private) { procedure.active_revision.types_de_champ_private.first }
before do
kept_dossier.champs_private.find_by(type_de_champ: type_de_champ_private).update(value: 'keep me')
discarded_dossier.champs_private.find_by(type_de_champ: type_de_champ_private).update(value: 'discard me')
kept_dossier.champs.find_by(stable_id: type_de_champ_private.stable_id).update(value: 'keep me')
discarded_dossier.champs.find_by(stable_id: type_de_champ_private.stable_id).update(value: 'discard me')
end
it { is_expected.to contain_exactly(kept_dossier.id) }
@ -673,7 +673,7 @@ describe ProcedurePresentation do
let(:other_kept_dossier) { create(:dossier, procedure: procedure) }
before do
other_kept_dossier.champs_private.find_by(type_de_champ: type_de_champ_private).update(value: 'and me too')
other_kept_dossier.champs.find_by(stable_id: type_de_champ_private.stable_id).update(value: 'and me too')
end
it 'returns every dossier that matches any of the search criteria for a given column' do

View file

@ -1584,12 +1584,15 @@ describe Procedure do
end
describe '#average_dossier_weight' do
let(:procedure) { create(:procedure, :published) }
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{ type: :piece_justificative }]) }
before do
create_dossier_with_pj_of_size(4, procedure)
create_dossier_with_pj_of_size(5, procedure)
create_dossier_with_pj_of_size(6, procedure)
create(:dossier, :accepte, :with_populated_champs, procedure:)
create(:dossier, :accepte, :with_populated_champs, procedure:)
create(:dossier, :accepte, :with_populated_champs, procedure:)
ActiveStorage::Blob.first.update!(byte_size: 4)
ActiveStorage::Blob.second.update!(byte_size: 5)
ActiveStorage::Blob.third.update!(byte_size: 6)
end
it 'estimates average dossier weight' do

View file

@ -83,7 +83,7 @@ describe Stat, type: :model do
describe '.cumulative_hash' do
it 'works count and cumulate counters by month for both dossier and deleted dossiers' do
12.downto(1).map do |i|
2.downto(1).map do |i|
create(:dossier, state: :en_construction, depose_at: i.months.ago)
create(:deleted_dossier, dossier_id: i + 100, state: :en_construction, deleted_at: i.month.ago)
end
@ -98,18 +98,8 @@ describe Stat, type: :model do
s.reload
# Use `Hash#to_a` to also test the key ordering
expect(s.dossiers_cumulative.to_a).to eq([
[formatted_n_months_ago(12), 2],
[formatted_n_months_ago(11), 4],
[formatted_n_months_ago(10), 6],
[formatted_n_months_ago(9), 8],
[formatted_n_months_ago(8), 10],
[formatted_n_months_ago(7), 12],
[formatted_n_months_ago(6), 14],
[formatted_n_months_ago(5), 16],
[formatted_n_months_ago(4), 18],
[formatted_n_months_ago(3), 20],
[formatted_n_months_ago(2), 22],
[formatted_n_months_ago(1), 24]
[formatted_n_months_ago(2), 2],
[formatted_n_months_ago(1), 4]
])
end
end

View file

@ -10,17 +10,12 @@ describe TypeDeChamp do
it { is_expected.not_to allow_value(nil).for(:type_champ) }
it { is_expected.not_to allow_value('').for(:type_champ) }
it { is_expected.to allow_value(TypeDeChamp.type_champs.fetch(:text)).for(:type_champ) }
it { is_expected.to allow_value(TypeDeChamp.type_champs.fetch(:textarea)).for(:type_champ) }
it { is_expected.to allow_value(TypeDeChamp.type_champs.fetch(:datetime)).for(:type_champ) }
it { is_expected.to allow_value(TypeDeChamp.type_champs.fetch(:number)).for(:type_champ) }
it { is_expected.to allow_value(TypeDeChamp.type_champs.fetch(:checkbox)).for(:type_champ) }
let(:procedure) { create(:procedure, :with_all_champs) }
let(:dossier) { create(:dossier, procedure:) }
it do
TypeDeChamp.type_champs.each do |(type_champ, _)|
type_de_champ = create(:"type_de_champ_#{type_champ}")
champ = type_de_champ.champ.create
dossier.revision.types_de_champ_public.each do |type_de_champ|
champ = dossier.project_champ(type_de_champ, nil)
expect(type_de_champ.dynamic_type.class.name).to match(/^TypesDeChamp::/)
expect(champ.class.name).to match(/^Champs::/)
end

View file

@ -1,8 +1,9 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillAddressTypeDeChamp do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_address, procedure: procedure) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :address }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:type_de_champ) { procedure.active_revision.types_de_champ.first }
describe 'ancestors' do
subject { described_class.new(type_de_champ, procedure.active_revision) }
@ -11,7 +12,7 @@ RSpec.describe TypesDeChamp::PrefillAddressTypeDeChamp do
end
describe '#to_assignable_attributes' do
let(:champ) { create(:champ_address, type_de_champ: type_de_champ) }
let(:champ) { dossier.champs.first }
subject { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
context 'when the value is nil' do

View file

@ -1,8 +1,9 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_annuaire_education, procedure: procedure) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :annuaire_education }]) }
let(:dossier) { create(:dossier, procedure:) }
let(:type_de_champ) { procedure.active_revision.types_de_champ.first }
describe 'ancestors' do
subject { described_class.new(type_de_champ, procedure.active_revision) }
@ -11,7 +12,7 @@ RSpec.describe TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp do
end
describe '#to_assignable_attributes' do
let(:champ) { create(:champ_annuaire_education, type_de_champ: type_de_champ) }
let(:champ) { dossier.champs.first }
subject { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
context 'when the value is nil' do

Some files were not shown because too many files have changed in this diff Show more