tech(clean): simplify implementation of eligibilite rules, code, enhance wording and test coverage
This commit is contained in:
parent
a011576757
commit
f819da8921
29 changed files with 161 additions and 219 deletions
|
@ -57,5 +57,14 @@ form.form > .conditionnel {
|
|||
select.alert {
|
||||
border-color: $dark-red;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
%div{ id: dom_id(@draft_revision, :ineligibilite_rules) }
|
||||
= render Procedure::PendingRepublishComponent.new(procedure: @draft_revision.procedure, render_if: pending_changes?)
|
||||
= render Conditions::ConditionsErrorsComponent.new(conditions: condition_per_row, source_tdcs: @source_tdcs)
|
||||
%fieldset.fr-fieldset
|
||||
%legend.fr-mx-1w.fr-label.fr-py-0.fr-mb-1w.fr-mt-2w
|
||||
Règles d’inéligibilité
|
||||
%span.fr-hint-text Vous pouvez utiliser 1 ou plusieurs critère pour bloquer le dépot
|
||||
.fr-fieldset
|
||||
= form_for(@draft_revision, url: change_admin_procedure_ineligibilite_rules_path(@draft_revision.procedure_id), html: { id: 'ineligibilite_form', class: 'width-100' }) do |f|
|
||||
.fr-fieldset__element
|
||||
.fr-toggle.fr-toggle--label-left
|
||||
= f.check_box :ineligibilite_enabled, class: 'fr-toggle__input', data: @opt
|
||||
= f.label :ineligibilite_enabled, "Bloquer le dépôt des dossiers répondant à des conditions d’inéligibilité", data: { 'fr-checked-label': "Activé", 'fr-unchecked-label': "Désactivé" }, class: 'fr-toggle__label'
|
||||
|
||||
.fr-fieldset__element= render Dsfr::InputComponent.new(form: f, attribute: :ineligibilite_message, input_type: :text_area, opts: {rows: 5})
|
||||
|
||||
.fr-mx-1w.fr-label.fr-py-0.fr-mb-1w.fr-mt-2w
|
||||
Conditions d’inéligibilité
|
||||
%span.fr-hint-text Vous pouvez utiliser une ou plusieurs condtions pour bloquer le dépot.
|
||||
.fr-fieldset__element
|
||||
= form_tag admin_procedure_ineligibilite_rules_path(@draft_revision.procedure_id), method: :patch, data: { turbo: true, controller: 'autosave' }, class: 'form width-100' do
|
||||
.conditionnel.width-100
|
||||
%table.condition-table
|
||||
- if rows.size > 0
|
||||
%thead
|
||||
%tr
|
||||
%th.fr-pt-0.far-left
|
||||
|
@ -28,15 +37,13 @@
|
|||
%tr
|
||||
%td.text-right{ colspan: 5 }= add_condition_tag
|
||||
|
||||
|
||||
|
||||
= form_for(@draft_revision, url: change_admin_procedure_ineligibilite_rules_path(@draft_revision.procedure_id)) do |f|
|
||||
.fr-fieldset__element= render Dsfr::InputComponent.new(form: f, attribute: :ineligibilite_message, input_type: :text_area, opts: {rows: 5})
|
||||
.fr-fieldset__element
|
||||
.fr-toggle
|
||||
= f.check_box :ineligibilite_enabled, class: 'fr-toggle__input', data: @opt
|
||||
= f.label :ineligibilite_enabled, "Inéligibilité des dossiers", data: { 'fr-checked-label': "Actif", 'fr-unchecked-label': "Inactif" }, class: 'fr-toggle__label'
|
||||
%p.fr-hint-text Passer l’intérrupteur sur activé pour que les critères d’inéligibilité configurés s'appliquent
|
||||
|
||||
|
||||
= render Procedure::FixedFooterComponent.new(procedure: @draft_revision.procedure, form: f, extra_class_names: 'fr-col-offset-md-2 fr-col-md-8')
|
||||
.padded-fixed-footer
|
||||
.fixed-footer
|
||||
.fr-container
|
||||
.fr-grid-row.fr-col-offset-md-2.fr-col-md-8
|
||||
.fr-col-12
|
||||
%ul.fr-btns-group.fr-btns-group--inline-md
|
||||
%li
|
||||
= link_to "Annuler et revenir à l'écran de gestion", admin_procedure_path(id: @draft_revision.procedure), class: 'fr-btn fr-btn--secondary', data: { confirm: 'Si vous avez fait des modifications elles ne seront pas sauvegardées.'}
|
||||
%li
|
||||
= button_tag "Enregistrer", class: "fr-btn", form: 'ineligibilite_form'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Dossiers::EditFooterComponent < ApplicationComponent
|
||||
delegate :can_passer_en_construction?, :ineligibilite_rules_computable?, to: :@dossier
|
||||
delegate :can_passer_en_construction?, to: :@dossier
|
||||
|
||||
def initialize(dossier:, annotation:)
|
||||
@dossier = dossier
|
||||
|
@ -27,7 +27,7 @@ class Dossiers::EditFooterComponent < ApplicationComponent
|
|||
def submit_draft_button_options
|
||||
{
|
||||
class: 'fr-btn fr-btn--sm',
|
||||
disabled: !owner? || ineligibilite_rules_invalid?,
|
||||
disabled: !owner? || !can_passer_en_construction?,
|
||||
method: :post,
|
||||
data: { 'disable-with': t('.submitting'), controller: 'autosave-submit', turbo_force: :server }
|
||||
}
|
||||
|
@ -36,17 +36,13 @@ class Dossiers::EditFooterComponent < ApplicationComponent
|
|||
def submit_en_construction_button_options
|
||||
{
|
||||
class: 'fr-btn fr-btn--sm',
|
||||
disabled: ineligibilite_rules_invalid?,
|
||||
disabled: !can_passer_en_construction?,
|
||||
method: :post,
|
||||
data: { 'disable-with': t('.submitting'), controller: 'autosave-submit', turbo_force: :server },
|
||||
form: { id: "form-submit-en-construction" }
|
||||
}
|
||||
end
|
||||
|
||||
def ineligibilite_rules_invalid?
|
||||
ineligibilite_rules_computable? && !can_passer_en_construction?
|
||||
end
|
||||
|
||||
def render?
|
||||
!@dossier.for_procedure_preview?
|
||||
end
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
= render Dossiers::AutosaveFooterComponent.new(dossier: @dossier, annotation: annotation?)
|
||||
|
||||
- if !annotation? && @dossier.can_transition_to_en_construction?
|
||||
- if ineligibilite_rules_invalid?
|
||||
- if !can_passer_en_construction?
|
||||
= link_to t('.submit_disabled'), "#", disabled_submit_buttons_options
|
||||
= button_to t('.submit'), brouillon_dossier_url(@dossier), submit_draft_button_options
|
||||
|
||||
- if @dossier.forked_with_changes?
|
||||
- if ineligibilite_rules_invalid?
|
||||
- if !can_passer_en_construction?
|
||||
= link_to t('.submit_disabled'), "#", disabled_submit_buttons_options
|
||||
= button_to t('.submit_changes'), modifier_dossier_url(@dossier.editing_fork_origin), submit_en_construction_button_options
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Dossiers::InvalidIneligibiliteRulesComponent < ApplicationComponent
|
||||
delegate :can_passer_en_construction?, :ineligibilite_rules_computable?, to: :@dossier
|
||||
delegate :can_passer_en_construction?, to: :@dossier
|
||||
|
||||
def initialize(dossier:)
|
||||
@dossier = dossier
|
||||
|
@ -7,7 +7,7 @@ class Dossiers::InvalidIneligibiliteRulesComponent < ApplicationComponent
|
|||
end
|
||||
|
||||
def render?
|
||||
ineligibilite_rules_computable? && !can_passer_en_construction?
|
||||
!can_passer_en_construction?
|
||||
end
|
||||
|
||||
def error_message
|
||||
|
|
|
@ -6,7 +6,7 @@ class Procedure::Card::IneligibiliteDossierComponent < ApplicationComponent
|
|||
def ready?
|
||||
@procedure.draft_revision
|
||||
.conditionable_types_de_champ
|
||||
.present?
|
||||
.present? && @procedure.draft_revision.ineligibilite_enabled
|
||||
end
|
||||
|
||||
def error?
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
fr:
|
||||
title: Inéligibilité des dossiers
|
||||
state:
|
||||
pending: Champs à configurer
|
||||
pending: Désactivé
|
||||
ready: À configurer
|
||||
completed: Activé
|
||||
subtitle: Gérez vos critères d’inéligibilité en fonction des champs du formulaire
|
||||
subtitle: Gérez vos conditions d’inéligibilité en fonction des champs du formulaire
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
= link_to edit_admin_procedure_ineligibilite_rules_path(@procedure), class: 'fr-tile fr-enlarge-link' do
|
||||
.fr-tile__body.flex.column.align-center.justify-between
|
||||
- if !ready?
|
||||
%p.fr-badge.fr-badge--warning= t('.state.pending')
|
||||
%p.fr-badge.fr-badge= t('.state.pending')
|
||||
- elsif error?
|
||||
%p.fr-badge.fr-badge--error À modifier
|
||||
- elsif !completed?
|
||||
%p.fr-badge.fr-badge--info= t('.state.ready')
|
||||
- else
|
||||
%p.fr-badge.fr-badge--success= t('.state.completed')
|
||||
%div
|
||||
|
|
|
@ -302,13 +302,10 @@ module Users
|
|||
def update
|
||||
@dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
@ineligibilite_rules_was_computable = @dossier.ineligibilite_rules_computable?
|
||||
@can_passer_en_construction_was = @dossier.can_passer_en_construction?
|
||||
update_dossier_and_compute_errors
|
||||
@dossier.index_search_terms_later if @dossier.errors.empty?
|
||||
@ineligibilite_rules_is_computable = @dossier.ineligibilite_rules_computable?
|
||||
@can_passer_en_construction_is = @dossier.can_passer_en_construction?
|
||||
@ineligibilite_rules_computable_changed = !@ineligibilite_rules_was_computable && @ineligibilite_rules_is_computable
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.champs.filter(&:public?))
|
||||
|
|
|
@ -940,12 +940,9 @@ class Dossier < ApplicationRecord
|
|||
.filter(&:visible?)
|
||||
.filter(&:mandatory_blank?)
|
||||
.map do |champ|
|
||||
errors.import(champ.errors.add(:value, :missing))
|
||||
champ.errors.add(:value, :missing)
|
||||
end
|
||||
end
|
||||
|
||||
def ineligibilite_rules_computable?
|
||||
revision.ineligibilite_rules_computable?(champs_for_revision(scope: :public))
|
||||
.each { errors.import(_1) }
|
||||
end
|
||||
|
||||
def demander_un_avis!(avis)
|
||||
|
|
|
@ -7,12 +7,5 @@ class Logic::And < Logic::NAryOperator
|
|||
@operands.map { |operand| operand.compute(champs) }.all?
|
||||
end
|
||||
|
||||
def computable?(champs = [])
|
||||
return true if sources.blank?
|
||||
|
||||
champs.filter { _1.stable_id.in?(sources) && _1.visible? }
|
||||
.all? { _1.value.present? }
|
||||
end
|
||||
|
||||
def to_s(type_de_champs) = "(#{@operands.map { |o| o.to_s(type_de_champs) }.join(' && ')})"
|
||||
end
|
||||
|
|
|
@ -42,15 +42,6 @@ class Logic::BinaryOperator < Logic::Term
|
|||
l&.send(operation, r) || false
|
||||
end
|
||||
|
||||
def computable?(champs = [])
|
||||
return true if sources.blank?
|
||||
|
||||
visible_champs_sources = champs.filter { _1.stable_id.in?(sources) && _1.visible? }
|
||||
|
||||
return false if visible_champs_sources.size != sources.size
|
||||
visible_champs_sources.all? { _1.value.present? }
|
||||
end
|
||||
|
||||
def to_s(type_de_champs) = "(#{@left.to_s(type_de_champs)} #{operation} #{@right.to_s(type_de_champs)})"
|
||||
|
||||
def ==(other)
|
||||
|
|
|
@ -7,15 +7,5 @@ class Logic::Or < Logic::NAryOperator
|
|||
@operands.map { |operand| operand.compute(champs) }.any?
|
||||
end
|
||||
|
||||
|
||||
def computable?(champs = [])
|
||||
return true if sources.blank?
|
||||
|
||||
visible_champs_sources = champs.filter { _1.stable_id.in?(sources) && _1.visible? }
|
||||
|
||||
return false if visible_champs_sources.blank?
|
||||
visible_champs_sources.all? { _1.value.present? } || compute(visible_champs_sources)
|
||||
end
|
||||
|
||||
def to_s(type_de_champs = []) = "(#{@operands.map { |o| o.to_s(type_de_champs) }.join(' || ')})"
|
||||
end
|
||||
|
|
|
@ -269,12 +269,6 @@ class ProcedureRevision < ApplicationRecord
|
|||
types_de_champ_for(scope: :public).filter(&:conditionable?)
|
||||
end
|
||||
|
||||
def ineligibilite_rules_computable?(champs)
|
||||
ineligibilite_enabled && ineligibilite_rules&.computable?(champs)
|
||||
ensure
|
||||
champs.map(&:reset_visible) # otherwise @visible is cached, then dossier can be updated. champs are not updated
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_estimated_fill_duration
|
||||
|
@ -496,13 +490,6 @@ class ProcedureRevision < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def ineligibilite_rules_are_valid?
|
||||
if ineligibilite_rules
|
||||
ineligibilite_rules.errors(types_de_champ_for(scope: :public).to_a)
|
||||
.each { errors.add(:ineligibilite_rules, :invalid) }
|
||||
end
|
||||
end
|
||||
|
||||
def replace_type_de_champ_by_clone(coordinate)
|
||||
cloned_type_de_champ = coordinate.type_de_champ.deep_clone do |original, kopy|
|
||||
ClonePiecesJustificativesService.clone_attachments(original, kopy)
|
||||
|
|
|
@ -12,17 +12,17 @@
|
|||
= render Dsfr::AlertComponent.new(title: nil, size: :sm, state: :info, heading_level: 'h2', extra_class_names: 'fr-my-2w') do |c|
|
||||
- c.with_body do
|
||||
%p
|
||||
Les dossiers répondant à vos critères d’inéligibilité ne pourront pas être déposés. Plus d’informations sur l’inéligibilité des dossiers dans la
|
||||
Les dossiers répondant à vos conditions d’inéligibilité ne pourront pas être déposés. Plus d’informations sur l’inéligibilité des dossiers dans la
|
||||
= link_to('doc', ELIGIBILITE_URL, title: "Document sur l’inéligibilité des dossiers", **external_link_attributes)
|
||||
|
||||
- if !@procedure.draft_revision.conditionable_types_de_champ.present?
|
||||
%p.fr-mt-2w.fr-mb-2w
|
||||
Pour configurer l’inéligibilité des dossiers, votre formulaire doit comporter au moins un champ supportant les critères d’inéligibilité. Il vous faut donc ajouter au moins un des champs suivant à votre formulaire :
|
||||
Pour configurer l’inéligibilité des dossiers, votre formulaire doit comporter au moins un champ supportant les conditions d’inéligibilité. Il vous faut donc ajouter au moins un des champs suivant à votre formulaire :
|
||||
%ul
|
||||
- Logic::ChampValue::MANAGED_TYPE_DE_CHAMP.values.each do
|
||||
%li= "« #{t(_1, scope: [:activerecord, :attributes, :type_de_champ, :type_champs])} »"
|
||||
%p.fr-mt-2w
|
||||
= link_to 'Ajouter un champ supportant les critères d’inéligibilité', champs_admin_procedure_path(@procedure), class: 'fr-link fr-icon-arrow-right-line fr-link--icon-right'
|
||||
= link_to 'Ajouter un champ supportant les conditions d’inéligibilité', champs_admin_procedure_path(@procedure), class: 'fr-link fr-icon-arrow-right-line fr-link--icon-right'
|
||||
= render Procedure::FixedFooterComponent.new(procedure: @procedure)
|
||||
- else
|
||||
= render Conditions::IneligibiliteRulesComponent.new(draft_revision: @procedure.draft_revision)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
= render partial: 'shared/dossiers/update_champs', locals: { to_show: @to_show, to_hide: @to_hide, to_update: @to_update, dossier: @dossier }
|
||||
|
||||
- if !params.key?(:validate)
|
||||
- if @ineligibilite_rules_is_computable
|
||||
= turbo_stream.remove(dom_id(@dossier, :ineligibilite_rules_broken))
|
||||
|
||||
- if (@ineligibilite_rules_computable_changed && !@can_passer_en_construction_is) || (@can_passer_en_construction_was && !@can_passer_en_construction_is)
|
||||
- if @can_passer_en_construction_was && !@can_passer_en_construction_is
|
||||
= turbo_stream.append('contenu', render(Dossiers::InvalidIneligibiliteRulesComponent.new(dossier: @dossier)))
|
||||
- else @ineligibilite_rules_is_computable
|
||||
= turbo_stream.remove(dom_id(@dossier, :ineligibilite_rules_broken))
|
||||
|
|
|
@ -610,7 +610,7 @@ fr:
|
|||
otp_attempt: 'Code OTP (uniquement si vous avez déjà activé 2FA)'
|
||||
procedure:
|
||||
zone: La démarche est mise en œuvre par
|
||||
ineligibilite_rules: "Les règles d’Inéligibilité"
|
||||
ineligibilite_rules: "Les règles d’inéligibilité"
|
||||
champs:
|
||||
value: Valeur du champ
|
||||
default_mail_attributes: &default_mail_attributes
|
||||
|
|
|
@ -8,7 +8,7 @@ fr:
|
|||
procedure:
|
||||
hints:
|
||||
description: Décrivez en quelques lignes le contexte, la finalité, etc.
|
||||
description_target_audience: Décrivez en quelques lignes les destinataires finaux de la démarche, les critères d’éligibilité s’il y en a, les pré-requis, etc.
|
||||
description_target_audience: Décrivez en quelques lignes les destinataires finaux de la démarche, les conditions d’éligibilité s’il y en a, les pré-requis, etc.
|
||||
description_pj: Décrivez la liste des pièces jointes à fournir s’il y en a
|
||||
lien_site_web: "Il s'agit de la page de votre site web où le lien sera diffusé. Ex: https://exemple.gouv.fr/page_informant_sur_ma_demarche"
|
||||
cadre_juridique: "Exemple: 'https://www.legifrance.gouv.fr/'"
|
||||
|
|
7
config/locales/models/procedure_revision/fr.yml
Normal file
7
config/locales/models/procedure_revision/fr.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
fr:
|
||||
activerecord:
|
||||
attributes:
|
||||
procedure_revision:
|
||||
ineligibilite_message: Message d’inéligibilité
|
||||
hints:
|
||||
ineligibilite_message: "Ce message sera affiché à l’usager si son dossier est bloqué et lui expliquera la raison de son inéligibilité."
|
|
@ -10,14 +10,14 @@ RSpec.describe Dossiers::EditFooterComponent, type: :component do
|
|||
let(:dossier) { create(:dossier, :brouillon) }
|
||||
|
||||
context 'when dossier can be submitted' do
|
||||
before { allow(component).to receive(:ineligibilite_rules_invalid?).and_return(false) }
|
||||
before { allow(component).to receive(:can_passer_en_construction?).and_return(true) }
|
||||
it 'renders submit button without disabled' do
|
||||
expect(subject).to have_selector('button', text: 'Déposer le dossier')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dossier can not be submitted' do
|
||||
before { allow(component).to receive(:ineligibilite_rules_invalid?).and_return(true) }
|
||||
before { allow(component).to receive(:can_passer_en_construction?).and_return(false) }
|
||||
it 'renders submit button with disabled' do
|
||||
expect(subject).to have_selector('a', text: 'Pourquoi je ne peux pas déposer mon dossier ?')
|
||||
expect(subject).to have_selector('button[disabled]', text: 'Déposer le dossier')
|
||||
|
@ -31,7 +31,7 @@ RSpec.describe Dossiers::EditFooterComponent, type: :component do
|
|||
before { allow(dossier).to receive(:forked_with_changes?).and_return(true) }
|
||||
|
||||
context 'when dossier can be submitted' do
|
||||
before { allow(component).to receive(:ineligibilite_rules_invalid?).and_return(false) }
|
||||
before { allow(component).to receive(:can_passer_en_construction?).and_return(true) }
|
||||
|
||||
it 'renders submit button without disabled' do
|
||||
expect(subject).to have_selector('button', text: 'Déposer les modifications')
|
||||
|
@ -39,7 +39,7 @@ RSpec.describe Dossiers::EditFooterComponent, type: :component do
|
|||
end
|
||||
|
||||
context 'when dossier can not be submitted' do
|
||||
before { allow(component).to receive(:ineligibilite_rules_invalid?).and_return(true) }
|
||||
before { allow(component).to receive(:can_passer_en_construction?).and_return(false) }
|
||||
|
||||
it 'renders submit button with disabled' do
|
||||
expect(subject).to have_selector('a', text: 'Pourquoi je ne peux pas déposer mon dossier ?')
|
||||
|
|
|
@ -19,7 +19,7 @@ describe TypesDeChampEditor::EditorComponent, type: :component do
|
|||
context 'types_de_champ_private' do
|
||||
let(:is_annotation) { true }
|
||||
it 'does not render public champs errors' do
|
||||
expect(subject).to have_selector("a", "private")
|
||||
expect(subject).to have_selector("a", text: "private")
|
||||
expect(subject).to have_text("doit comporter au moins un choix sélectionnable")
|
||||
expect(subject).not_to have_text("public")
|
||||
end
|
||||
|
|
|
@ -197,14 +197,14 @@ describe Administrateurs::IneligibiliteRulesController, type: :controller do
|
|||
let(:types_de_champ_public) { [] }
|
||||
render_views
|
||||
|
||||
it { expect(response.body).to have_link("Ajouter un champ supportant les critères d’inéligibilité") }
|
||||
it { expect(response.body).to have_link("Ajouter un champ supportant les conditions d’inéligibilité") }
|
||||
end
|
||||
|
||||
context 'rendered with tdc' do
|
||||
let(:types_de_champ_public) { [{ type: :yes_no }] }
|
||||
render_views
|
||||
|
||||
it { expect(response.body).not_to have_link("Ajouter un champ supportant les critères d’inéligibilité") }
|
||||
it { expect(response.body).not_to have_link("Ajouter un champ supportant les conditions d’inéligibilité") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -791,26 +791,27 @@ describe Users::DossiersController, type: :controller do
|
|||
end
|
||||
render_views
|
||||
|
||||
context 'when it pass from undefined to true' do
|
||||
context 'when it switches from true to false' do
|
||||
let(:value) { must_be_greater_than + 1 }
|
||||
|
||||
it 'raises popup' do
|
||||
subject
|
||||
dossier.reload
|
||||
expect(dossier.can_passer_en_construction?).to be_falsey
|
||||
expect(assigns(:ineligibilite_rules_was_computable)).to eq(false)
|
||||
expect(assigns(:ineligibilite_rules_is_computable)).to eq(true)
|
||||
expect(assigns(:can_passer_en_construction_was)).to eq(true)
|
||||
expect(assigns(:can_passer_en_construction_is)).to eq(false)
|
||||
expect(response.body).to match(ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken))
|
||||
end
|
||||
end
|
||||
context 'when it pass from undefined to false' do
|
||||
|
||||
context 'when it stays true' do
|
||||
let(:value) { must_be_greater_than - 1 }
|
||||
it 'does nothing' do
|
||||
subject
|
||||
dossier.reload
|
||||
expect(dossier.can_passer_en_construction?).to be_truthy
|
||||
expect(assigns(:ineligibilite_rules_was_computable)).to eq(false)
|
||||
expect(assigns(:ineligibilite_rules_is_computable)).to eq(true)
|
||||
expect(assigns(:can_passer_en_construction_was)).to eq(true)
|
||||
expect(assigns(:can_passer_en_construction_is)).to eq(true)
|
||||
expect(response.body).not_to have_selector("##{ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken)}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,42 +6,6 @@ describe Logic::And do
|
|||
it { expect(and_from([true, true, false]).compute).to be false }
|
||||
end
|
||||
|
||||
describe '#computable?' do
|
||||
let(:champ_1) { create(:champ_integer_number, value: value_1) }
|
||||
let(:champ_2) { create(:champ_integer_number, value: value_2) }
|
||||
|
||||
let(:logic) do
|
||||
ds_and([
|
||||
greater_than(champ_value(champ_1.stable_id), constant(1)),
|
||||
less_than(champ_value(champ_2.stable_id), constant(10))
|
||||
])
|
||||
end
|
||||
|
||||
subject { logic.computable?([champ_1, champ_2]) }
|
||||
|
||||
context "when none of champs.value are filled, and logic can't be computed" do
|
||||
let(:value_1) { nil }
|
||||
let(:value_2) { nil }
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
context "when one champs has a value (that compute to false) the other has not, and logic keeps waiting for the 2nd value" do
|
||||
let(:value_1) { 1 }
|
||||
let(:value_2) { nil }
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
context 'when all champs.value are filled, and logic can be computed' do
|
||||
let(:value_1) { 1 }
|
||||
let(:value_2) { 10 }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
context 'when one champs is not visible and the other has a value, and logic can be computed' do
|
||||
let(:value_1) { 1 }
|
||||
let(:value_2) { nil }
|
||||
before { expect(champ_2).to receive(:visible?).and_return(false) }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_s' do
|
||||
it do
|
||||
expect(and_from([true, false, true]).to_s([])).to eq "(Oui && Non && Oui)"
|
||||
|
|
|
@ -28,19 +28,6 @@ describe Logic::BinaryOperator do
|
|||
it { expect(greater_than(constant(2), champ_value(champ.stable_id)).sources).to eq([champ.stable_id]) }
|
||||
it { expect(greater_than(champ_value(champ.stable_id), champ_value(champ2.stable_id)).sources).to eq([champ.stable_id, champ2.stable_id]) }
|
||||
end
|
||||
|
||||
describe '#computable?' do
|
||||
let(:champ) { create(:champ_integer_number, value: nil) }
|
||||
|
||||
it 'computable?' do
|
||||
expect(greater_than(champ_value(champ.stable_id), constant(1)).computable?([])).to be(false)
|
||||
expect(greater_than(champ_value(champ.stable_id), constant(1)).computable?([champ])).to be(false)
|
||||
allow(champ).to receive(:value).and_return(double(present?: true))
|
||||
expect(greater_than(champ_value(champ.stable_id), constant(1)).computable?([champ])).to be(true)
|
||||
allow(champ).to receive(:visible?).and_return(false)
|
||||
expect(greater_than(champ_value(champ.stable_id), constant(1)).computable?([champ])).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Logic::GreaterThan do
|
||||
|
|
|
@ -7,49 +7,6 @@ describe Logic::Or do
|
|||
it { expect(or_from([false, false, false]).compute).to be false }
|
||||
end
|
||||
|
||||
describe '#computable?' do
|
||||
let(:champ_1) { create(:champ_integer_number, value: value_1) }
|
||||
let(:champ_2) { create(:champ_integer_number, value: value_2) }
|
||||
|
||||
let(:logic) do
|
||||
ds_or([
|
||||
greater_than(champ_value(champ_1.stable_id), constant(1)),
|
||||
less_than(champ_value(champ_2.stable_id), constant(10))
|
||||
])
|
||||
end
|
||||
|
||||
context 'with all champs' do
|
||||
subject { logic.computable?([champ_1, champ_2]) }
|
||||
|
||||
context "when none of champs.value are filled, or logic can't be computed" do
|
||||
let(:value_1) { nil }
|
||||
let(:value_2) { nil }
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
context "when one champs has a value (that compute to false) the other has not, or logic keeps waiting for the 2nd value" do
|
||||
let(:value_1) { 1 }
|
||||
let(:value_2) { nil }
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
context 'when all champs.value are filled, or logic can be computed' do
|
||||
let(:value_1) { 1 }
|
||||
let(:value_2) { 10 }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
context 'when one champs.value and his condition is true, or logic can be computed' do
|
||||
let(:value_1) { 2 }
|
||||
let(:value_2) { nil }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
context 'when one champs is not visible and the other has a value that fails, or logic can be computed' do
|
||||
let(:value_1) { 1 }
|
||||
let(:value_2) { nil }
|
||||
before { expect(champ_2).to receive(:visible?).and_return(false) }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_s' do
|
||||
it { expect(or_from([true, false, true]).to_s).to eq "(Oui || Non || Oui)" }
|
||||
end
|
||||
|
|
|
@ -9,13 +9,13 @@ describe 'Administrateurs can edit procedures', js: true do
|
|||
scenario 'setup eligibilite' do
|
||||
# explain no champ compatible
|
||||
visit admin_procedure_path(procedure)
|
||||
expect(page).to have_content("Champs à configurer")
|
||||
expect(page).to have_content("Désactivé")
|
||||
|
||||
# explain which champs are compatible
|
||||
visit edit_admin_procedure_ineligibilite_rules_path(procedure)
|
||||
expect(page).to have_content("Inéligibilité des dossiers")
|
||||
expect(page).to have_content("Pour configurer l’inéligibilité des dossiers, votre formulaire doit comporter au moins un champ supportant les critères d’inéligibilité. Il vous faut donc ajouter au moins un des champs suivant à votre formulaire : ")
|
||||
click_on "Ajouter un champ supportant les critères d’inéligibilité"
|
||||
expect(page).to have_content("Pour configurer l’inéligibilité des dossiers, votre formulaire doit comporter au moins un champ supportant les conditions d’inéligibilité. Il vous faut donc ajouter au moins un des champs suivant à votre formulaire : ")
|
||||
click_on "Ajouter un champ supportant les conditions d’inéligibilité"
|
||||
|
||||
# setup a compatible champ
|
||||
expect(page).to have_content('Champs du formulaire')
|
||||
|
@ -32,7 +32,7 @@ describe 'Administrateurs can edit procedures', js: true do
|
|||
# setup rules and stuffs
|
||||
expect(page).to have_content("Inéligibilité des dossiers")
|
||||
fill_in "Message d’inéligibilité", with: "vous n'etes pas eligible"
|
||||
find('label', text: 'Inéligibilité des dossiers').click
|
||||
find('label', text: 'Bloquer le dépôt des dossiers répondant à des conditions d’inéligibilité').click
|
||||
click_on "Ajouter une règle d’inéligibilité"
|
||||
all('select').first.select 'Un champ oui non'
|
||||
click_on 'Enregistrer'
|
||||
|
|
|
@ -9,7 +9,7 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
|
||||
let(:published_revision) { procedure.published_revision }
|
||||
let(:first_tdc) { published_revision.types_de_champ.first }
|
||||
let(:second_tdc) { published_revision.types_de_champ.last }
|
||||
let(:second_tdc) { published_revision.types_de_champ.second }
|
||||
let(:ineligibilite_message) { 'sry vous pouvez aps soumettre votre dossier' }
|
||||
let(:eligibilite_params) { { ineligibilite_enabled: true, ineligibilite_message: } }
|
||||
|
||||
|
@ -18,8 +18,8 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
login_as user, scope: :user
|
||||
end
|
||||
|
||||
context 'single condition' do
|
||||
let(:types_de_champ_public) { [{ type: :yes_no }] }
|
||||
describe 'ineligibilite_rules with a single BinaryOperator' do
|
||||
let(:types_de_champ_public) { [{ type: :yes_no, stable_id: 1 }] }
|
||||
let(:ineligibilite_rules) { ds_eq(champ_value(first_tdc.stable_id), constant(true)) }
|
||||
|
||||
scenario 'can submit, can not submit, reload' do
|
||||
|
@ -28,24 +28,33 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# does raise error when dossier is filled with valid condition
|
||||
# does raise error when dossier is filled with condition that does not match
|
||||
within "#champ-1" do
|
||||
find("label", text: "Non").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# raise error when dossier is filled with invalid condition
|
||||
# raise error when dossier is filled with condition that matches
|
||||
within "#champ-1" do
|
||||
find("label", text: "Oui").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: true)
|
||||
expect(page).to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# reload page and see error because it was filled
|
||||
# reload page and see error
|
||||
visit brouillon_dossier_path(dossier)
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: true)
|
||||
expect(page).to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# modal is closable, and we can change our dossier response to be eligible
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
within("#modal-eligibilite-rules-dialog") { click_on "Fermer" }
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
within "#champ-1" do
|
||||
find("label", text: "Non").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
|
||||
# it works, yay
|
||||
|
@ -54,7 +63,7 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
end
|
||||
end
|
||||
|
||||
context 'or condition' do
|
||||
describe 'ineligibilite_rules with a Or' do
|
||||
let(:types_de_champ_public) { [{ type: :yes_no, libelle: 'l1' }, { type: :drop_down_list, libelle: 'l2', options: ['Paris', 'Marseille'] }] }
|
||||
let(:ineligibilite_rules) do
|
||||
ds_or([
|
||||
|
@ -69,15 +78,17 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# only one condition is matches, cannot submit dossier and error message is clear
|
||||
# first condition matches (so ineligible), cannot submit dossier and error message is clear
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
find("label", text: "Oui").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: true)
|
||||
expect(page).to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
within("#modal-eligibilite-rules-dialog") { click_on "Fermer" }
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# only one condition does not matches, I can conitnue
|
||||
# first condition does not matches, I can conitnue
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
find("label", text: "Non").click
|
||||
end
|
||||
|
@ -88,12 +99,15 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
click_on "Accéder à votre dossier"
|
||||
click_on "Modifier le dossier"
|
||||
|
||||
# one condition matches, means i'm blocked to send my file.
|
||||
# first matches, means i'm blocked to send my file.
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
find("label", text: "Oui").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer les modifications", disabled: true)
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
within("#modal-eligibilite-rules-dialog") { click_on "Fermer" }
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
find("label", text: "Non").click
|
||||
end
|
||||
|
@ -104,7 +118,56 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
find("label", text: 'Paris').click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer les modifications", disabled: true)
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
within("#modal-eligibilite-rules-dialog") { click_on "Fermer" }
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# none of conditions matches, i can submit
|
||||
within "#champ-#{second_tdc.stable_id}" do
|
||||
find("label", text: 'Marseille').click
|
||||
end
|
||||
|
||||
# it works, yay
|
||||
click_on "Déposer les modifications"
|
||||
wait_until { dossier.reload.en_construction? == true }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ineligibilite_rules with a And and all visible champs' do
|
||||
let(:types_de_champ_public) { [{ type: :yes_no, libelle: 'l1' }, { type: :drop_down_list, libelle: 'l2', options: ['Paris', 'Marseille'] }] }
|
||||
let(:ineligibilite_rules) do
|
||||
ds_and([
|
||||
ds_eq(champ_value(first_tdc.stable_id), constant(true)),
|
||||
ds_eq(champ_value(second_tdc.stable_id), constant('Paris'))
|
||||
])
|
||||
end
|
||||
|
||||
scenario 'can submit, can not submit, can edit, etc...' do
|
||||
visit brouillon_dossier_path(dossier)
|
||||
# no error while dossier is empty
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# only one condition is matches, can submit dossier
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
find("label", text: "Oui").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
|
||||
# Now test dossier modification
|
||||
click_on "Déposer le dossier"
|
||||
click_on "Accéder à votre dossier"
|
||||
click_on "Modifier le dossier"
|
||||
|
||||
# second condition matches, means i'm blocked to send my file
|
||||
within "#champ-#{second_tdc.stable_id}" do
|
||||
find("label", text: 'Paris').click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer les modifications", disabled: true)
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
within("#modal-eligibilite-rules-dialog") { click_on "Fermer" }
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# none of conditions matches, i can submit
|
||||
within "#champ-#{second_tdc.stable_id}" do
|
||||
|
|
|
@ -155,7 +155,6 @@ describe 'shared/dossiers/edit', type: :view do
|
|||
let(:dossier) { create(:dossier, procedure:) }
|
||||
|
||||
before do
|
||||
allow_any_instance_of(Dossiers::InvalidIneligibiliteRulesComponent).to receive(:ineligibilite_rules_computable?).and_return(true)
|
||||
allow(dossier).to receive(:can_passer_en_construction?).and_return(false)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue