Merge pull request #7810 from betagouv/fix_conditional_tdc
Fix(conditional and revision): restreint les calculs conditionels a la révision courante
This commit is contained in:
commit
3612c722a6
22 changed files with 160 additions and 115 deletions
|
@ -105,7 +105,7 @@ class TypesDeChampEditor::ConditionsComponent < ApplicationComponent
|
|||
end
|
||||
|
||||
def compatibles_operators_for_select(left)
|
||||
case left.type
|
||||
case left.type(@upper_tdcs)
|
||||
when ChampValue::CHAMP_VALUE_TYPE.fetch(:boolean)
|
||||
[
|
||||
[t('is', scope: 'logic'), Eq.name]
|
||||
|
@ -135,7 +135,7 @@ class TypesDeChampEditor::ConditionsComponent < ApplicationComponent
|
|||
def right_operand_tag(left, right, row_index)
|
||||
right_invalid = !current_right_valid?(left, right)
|
||||
|
||||
case left.type
|
||||
case left.type(@upper_tdcs)
|
||||
when :boolean
|
||||
booleans_for_select = [[t('utils.yes'), constant(true).to_json], [t('utils.no'), constant(false).to_json]]
|
||||
|
||||
|
@ -156,7 +156,7 @@ class TypesDeChampEditor::ConditionsComponent < ApplicationComponent
|
|||
id: input_id_for('value', row_index)
|
||||
)
|
||||
when :enum, :enums
|
||||
enums_for_select = left.options
|
||||
enums_for_select = left.options(@upper_tdcs)
|
||||
|
||||
if right_invalid
|
||||
enums_for_select = empty_target_for_select + enums_for_select
|
||||
|
@ -182,7 +182,7 @@ class TypesDeChampEditor::ConditionsComponent < ApplicationComponent
|
|||
end
|
||||
|
||||
def current_right_valid?(left, right)
|
||||
Logic.compatible_type?(left, right)
|
||||
Logic.compatible_type?(left, right, @upper_tdcs)
|
||||
end
|
||||
|
||||
def add_condition_tag
|
||||
|
|
|
@ -6,10 +6,24 @@ class TypesDeChampEditor::ConditionsErrorsComponent < ApplicationComponent
|
|||
private
|
||||
|
||||
def errors
|
||||
@conditions
|
||||
.flat_map { |condition| condition.errors(@upper_tdcs.map(&:stable_id)) }
|
||||
.map { |error| humanize(error) }
|
||||
errors = @conditions
|
||||
.flat_map { |condition| condition.errors(@upper_tdcs) }
|
||||
.uniq
|
||||
|
||||
# if a tdc is not available (has been removed for example)
|
||||
# it causes a lot of errors (incompatible type for example)
|
||||
# only the root cause is displayed
|
||||
messages = if errors.include?({ type: :not_available })
|
||||
[t('not_available', scope: '.errors')]
|
||||
else
|
||||
errors.map { |error| humanize(error) }
|
||||
end
|
||||
|
||||
to_html_list(messages)
|
||||
end
|
||||
|
||||
def to_html_list(messages)
|
||||
messages
|
||||
.map { |message| tag.li(message) }
|
||||
.then { |lis| tag.ul(lis.reduce(&:+)) }
|
||||
end
|
||||
|
@ -48,7 +62,7 @@ class TypesDeChampEditor::ConditionsErrorsComponent < ApplicationComponent
|
|||
|
||||
def render?
|
||||
@conditions
|
||||
.filter { |condition| condition.errors(@upper_tdcs.map(&:stable_id)).present? }
|
||||
.filter { |condition| condition.errors(@upper_tdcs).present? }
|
||||
.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,7 +49,7 @@ module Administrateurs
|
|||
end
|
||||
|
||||
def condition_form
|
||||
ConditionForm.new(condition_params)
|
||||
ConditionForm.new(condition_params.merge({ upper_tdcs: @upper_tdcs }))
|
||||
end
|
||||
|
||||
def retrieve_coordinate_and_uppers
|
||||
|
|
|
@ -2,7 +2,7 @@ class ConditionForm
|
|||
include ActiveModel::Model
|
||||
include Logic
|
||||
|
||||
attr_accessor :top_operator_name, :rows
|
||||
attr_accessor :top_operator_name, :rows, :upper_tdcs
|
||||
|
||||
def to_condition
|
||||
case sub_conditions.count
|
||||
|
@ -22,7 +22,7 @@ class ConditionForm
|
|||
end
|
||||
|
||||
def change_champ(i)
|
||||
sub_conditions[i] = Logic.ensure_compatibility_from_left(sub_conditions[i])
|
||||
sub_conditions[i] = Logic.ensure_compatibility_from_left(sub_conditions[i], upper_tdcs)
|
||||
|
||||
self
|
||||
end
|
||||
|
@ -39,7 +39,7 @@ class ConditionForm
|
|||
|
||||
def row_to_condition(row)
|
||||
left = Logic.from_json(row[:targeted_champ])
|
||||
right = parse_value(left.type, row[:value])
|
||||
right = parse_value(left.type(upper_tdcs), row[:value])
|
||||
|
||||
Logic.class_from_name(row[:operator_name]).new(left, right)
|
||||
end
|
||||
|
|
|
@ -12,12 +12,12 @@ module Logic
|
|||
.find { |c| c.name == name }
|
||||
end
|
||||
|
||||
def self.ensure_compatibility_from_left(condition)
|
||||
def self.ensure_compatibility_from_left(condition, type_de_champs)
|
||||
left = condition.left
|
||||
right = condition.right
|
||||
operator_class = condition.class
|
||||
|
||||
case [left.type, condition]
|
||||
case [left.type(type_de_champs), condition]
|
||||
in [:boolean, _]
|
||||
operator_class = Eq
|
||||
in [:empty, _]
|
||||
|
@ -31,14 +31,14 @@ module Logic
|
|||
in [:number, _]
|
||||
end
|
||||
|
||||
if !compatible_type?(left, right)
|
||||
right = case left.type
|
||||
if !compatible_type?(left, right, type_de_champs)
|
||||
right = case left.type(type_de_champs)
|
||||
when :boolean
|
||||
Constant.new(true)
|
||||
when :empty
|
||||
Empty.new
|
||||
when :enum, :enums
|
||||
Constant.new(left.options.first.second)
|
||||
Constant.new(left.options(type_de_champs).first.second)
|
||||
when :number
|
||||
Constant.new(0)
|
||||
end
|
||||
|
@ -47,8 +47,8 @@ module Logic
|
|||
operator_class.new(left, right)
|
||||
end
|
||||
|
||||
def self.compatible_type?(left, right)
|
||||
case [left.type, right.type]
|
||||
def self.compatible_type?(left, right, type_de_champs)
|
||||
case [left.type(type_de_champs), right.type(type_de_champs)]
|
||||
in [a, ^a] # syntax for same type
|
||||
true
|
||||
in [:enum, :string] | [:enums, :string]
|
||||
|
|
|
@ -7,5 +7,5 @@ class Logic::And < Logic::NAryOperator
|
|||
@operands.map { |operand| operand.compute(champs) }.all?
|
||||
end
|
||||
|
||||
def to_s = "(#{@operands.map(&:to_s).join(' && ')})"
|
||||
def to_s(type_de_champs) = "(#{@operands.map { |o| o.to_s(type_de_champs) }.join(' && ')})"
|
||||
end
|
||||
|
|
|
@ -17,17 +17,17 @@ class Logic::BinaryOperator < Logic::Term
|
|||
self.new(Logic.from_h(h['left']), Logic.from_h(h['right']))
|
||||
end
|
||||
|
||||
def errors(stable_ids = [])
|
||||
def errors(type_de_champs = [])
|
||||
errors = []
|
||||
|
||||
if @left.type != :number || @right.type != :number
|
||||
if @left.type(type_de_champs) != :number || @right.type(type_de_champs) != :number
|
||||
errors << { type: :required_number, operator_name: self.class.name }
|
||||
end
|
||||
|
||||
errors + @left.errors(stable_ids) + @right.errors(stable_ids)
|
||||
errors + @left.errors(type_de_champs) + @right.errors(type_de_champs)
|
||||
end
|
||||
|
||||
def type = :boolean
|
||||
def type(type_de_champs = []) = :boolean
|
||||
|
||||
def compute(champs = [])
|
||||
l = @left.compute(champs)
|
||||
|
@ -36,7 +36,7 @@ class Logic::BinaryOperator < Logic::Term
|
|||
l&.send(operation, r) || false
|
||||
end
|
||||
|
||||
def to_s = "(#{@left} #{operation} #{@right})"
|
||||
def to_s(type_de_champs) = "(#{@left.to_s(type_de_champs)} #{operation} #{@right.to_s(type_de_champs)})"
|
||||
|
||||
def ==(other)
|
||||
self.class == other.class &&
|
||||
|
|
|
@ -29,7 +29,7 @@ class Logic::ChampValue < Logic::Term
|
|||
return nil if !targeted_champ.visible?
|
||||
return nil if targeted_champ.blank?
|
||||
|
||||
case type_de_champ.type_champ
|
||||
case type_de_champ(champs.map(&:type_de_champ)).type_champ
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:yes_no),
|
||||
MANAGED_TYPE_DE_CHAMP.fetch(:checkbox)
|
||||
targeted_champ.true?
|
||||
|
@ -42,10 +42,10 @@ class Logic::ChampValue < Logic::Term
|
|||
end
|
||||
end
|
||||
|
||||
def to_s = type_de_champ&.libelle # TODO: gerer le cas ou un tdc est supprimé
|
||||
def to_s(type_de_champs) = type_de_champ(type_de_champs)&.libelle # TODO: gerer le cas ou un tdc est supprimé
|
||||
|
||||
def type
|
||||
case type_de_champ&.type_champ # TODO: gerer le cas ou un tdc est supprimé
|
||||
def type(type_de_champs)
|
||||
case type_de_champ(type_de_champs)&.type_champ # TODO: gerer le cas ou un tdc est supprimé
|
||||
when MANAGED_TYPE_DE_CHAMP.fetch(:yes_no),
|
||||
MANAGED_TYPE_DE_CHAMP.fetch(:checkbox)
|
||||
CHAMP_VALUE_TYPE.fetch(:boolean)
|
||||
|
@ -60,8 +60,8 @@ class Logic::ChampValue < Logic::Term
|
|||
end
|
||||
end
|
||||
|
||||
def errors(stable_ids)
|
||||
if !stable_ids.include?(stable_id)
|
||||
def errors(type_de_champs)
|
||||
if !type_de_champs.map(&:stable_id).include?(stable_id)
|
||||
[{ type: :not_available }]
|
||||
else
|
||||
[]
|
||||
|
@ -83,9 +83,10 @@ class Logic::ChampValue < Logic::Term
|
|||
self.class == other.class && @stable_id == other.stable_id
|
||||
end
|
||||
|
||||
def options
|
||||
opts = type_de_champ.drop_down_list_enabled_non_empty_options.map { |option| [option, option] }
|
||||
if type_de_champ.drop_down_other?
|
||||
def options(type_de_champs)
|
||||
tdc = type_de_champ(type_de_champs)
|
||||
opts = tdc.drop_down_list_enabled_non_empty_options.map { |option| [option, option] }
|
||||
if tdc.drop_down_other?
|
||||
opts + [["Autre", Champs::DropDownListChamp::OTHER]]
|
||||
else
|
||||
opts
|
||||
|
@ -94,8 +95,8 @@ class Logic::ChampValue < Logic::Term
|
|||
|
||||
private
|
||||
|
||||
def type_de_champ
|
||||
TypeDeChamp.find_by(stable_id: stable_id)
|
||||
def type_de_champ(type_de_champs)
|
||||
type_de_champs.find { |c| c.stable_id == stable_id }
|
||||
end
|
||||
|
||||
def champ(champs)
|
||||
|
|
|
@ -7,7 +7,7 @@ class Logic::Constant < Logic::Term
|
|||
|
||||
def compute(_champs = nil) = @value
|
||||
|
||||
def to_s
|
||||
def to_s(_type_de_champs = [])
|
||||
case @value
|
||||
when TrueClass
|
||||
I18n.t('utils.yes')
|
||||
|
@ -18,7 +18,7 @@ class Logic::Constant < Logic::Term
|
|||
end
|
||||
end
|
||||
|
||||
def type
|
||||
def type(_type_de_champs = [])
|
||||
case @value
|
||||
when TrueClass, FalseClass
|
||||
:boolean
|
||||
|
@ -29,7 +29,7 @@ class Logic::Constant < Logic::Term
|
|||
end
|
||||
end
|
||||
|
||||
def errors(_stable_ids = nil) = []
|
||||
def errors(_type_de_champs = nil) = []
|
||||
|
||||
def to_h
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
class Logic::Empty < Logic::Term
|
||||
def to_s = I18n.t('logic.empty')
|
||||
def to_s(_type_de_champs = []) = I18n.t('logic.empty')
|
||||
|
||||
def type = :empty
|
||||
def type(_type_de_champs = []) = :empty
|
||||
|
||||
def errors(_stable_ids = nil) = ['empty']
|
||||
def errors(_type_de_champs = []) = ['empty']
|
||||
|
||||
def to_h
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
class Logic::EmptyOperator < Logic::BinaryOperator
|
||||
def to_s = "empty operator"
|
||||
def to_s(_type_de_champs = []) = "empty operator"
|
||||
|
||||
def type = :empty
|
||||
def type(_type_de_champs = []) = :empty
|
||||
|
||||
def errors(_stable_ids = nil) = []
|
||||
def errors(_type_de_champs = []) = []
|
||||
|
||||
def compute(_champs = [])
|
||||
true
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
class Logic::Eq < Logic::BinaryOperator
|
||||
def operation = :==
|
||||
|
||||
def errors(stable_ids = [])
|
||||
def errors(type_de_champs = [])
|
||||
errors = [@left, @right]
|
||||
.filter { |term| term.type == :unmanaged }
|
||||
.filter { |term| term.type(type_de_champs) == :unmanaged }
|
||||
.map { |term| { type: :unmanaged, stable_id: term.stable_id } }
|
||||
|
||||
if !Logic.compatible_type?(@left, @right)
|
||||
if !Logic.compatible_type?(@left, @right, type_de_champs)
|
||||
errors << {
|
||||
type: :incompatible,
|
||||
stable_id: @left.try(:stable_id),
|
||||
right: @right,
|
||||
operator_name: self.class.name
|
||||
}
|
||||
elsif @left.type == :enum &&
|
||||
!left.options.map(&:second).include?(right.value)
|
||||
elsif @left.type(type_de_champs) == :enum &&
|
||||
!left.options(type_de_champs).map(&:second).include?(right.value)
|
||||
errors << {
|
||||
type: :not_included,
|
||||
stable_id: @left.stable_id,
|
||||
|
@ -22,7 +22,7 @@ class Logic::Eq < Logic::BinaryOperator
|
|||
}
|
||||
end
|
||||
|
||||
errors + @left.errors(stable_ids) + @right.errors(stable_ids)
|
||||
errors + @left.errors(type_de_champs) + @right.errors(type_de_champs)
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
class Logic::IncludeOperator < Logic::BinaryOperator
|
||||
def operation = :include?
|
||||
|
||||
def errors(stable_ids = [])
|
||||
def errors(type_de_champs = [])
|
||||
result = []
|
||||
|
||||
if left_not_a_list?
|
||||
if left_not_a_list?(type_de_champs)
|
||||
result << { type: :required_list }
|
||||
elsif right_value_not_in_list?
|
||||
elsif right_value_not_in_list?(type_de_champs)
|
||||
result << {
|
||||
type: :not_included,
|
||||
stable_id: @left.stable_id,
|
||||
|
@ -14,16 +14,16 @@ class Logic::IncludeOperator < Logic::BinaryOperator
|
|||
}
|
||||
end
|
||||
|
||||
result + @left.errors(stable_ids) + @right.errors(stable_ids)
|
||||
result + @left.errors(type_de_champs) + @right.errors(type_de_champs)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def left_not_a_list?
|
||||
@left.type != :enums
|
||||
def left_not_a_list?(type_de_champs)
|
||||
@left.type(type_de_champs) != :enums
|
||||
end
|
||||
|
||||
def right_value_not_in_list?
|
||||
!@left.options.map(&:second).include?(@right.value)
|
||||
def right_value_not_in_list?(type_de_champs)
|
||||
!@left.options(type_de_champs).map(&:second).include?(@right.value)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,22 +16,22 @@ class Logic::NAryOperator < Logic::Term
|
|||
self.new(h['operands'].map { |operand_h| Logic.from_h(operand_h) })
|
||||
end
|
||||
|
||||
def errors(stable_ids = [])
|
||||
def errors(type_de_champs = [])
|
||||
errors = []
|
||||
|
||||
if @operands.empty?
|
||||
errors += ["opérateur '#{operator_name}' vide"]
|
||||
end
|
||||
|
||||
not_booleans = @operands.filter { |operand| operand.type != :boolean }
|
||||
not_booleans = @operands.filter { |operand| operand.type(type_de_champs) != :boolean }
|
||||
if not_booleans.present?
|
||||
errors += ["'#{operator_name}' ne contient pas que des booléens : #{not_booleans.map(&:to_s).join(', ')}"]
|
||||
errors += ["'#{operator_name}' ne contient pas que des booléens : #{not_booleans.map { |o| o.to_s(type_de_champs) }.join(', ')}"]
|
||||
end
|
||||
|
||||
errors + @operands.flat_map { |operand| operand.errors(stable_ids) }
|
||||
errors + @operands.flat_map { |operand| operand.errors(type_de_champs) }
|
||||
end
|
||||
|
||||
def type = :boolean
|
||||
def type(_type_de_champs = []) = :boolean
|
||||
|
||||
def ==(other)
|
||||
self.class == other.class &&
|
||||
|
|
|
@ -7,5 +7,5 @@ class Logic::Or < Logic::NAryOperator
|
|||
@operands.map { |operand| operand.compute(champs) }.any?
|
||||
end
|
||||
|
||||
def to_s = "(#{@operands.map(&:to_s).join(' || ')})"
|
||||
def to_s(type_de_champs = []) = "(#{@operands.map { |o| o.to_s(type_de_champs) }.join(' || ')})"
|
||||
end
|
||||
|
|
|
@ -317,7 +317,7 @@ class ProcedureRevision < ApplicationRecord
|
|||
changed = kept
|
||||
.map { |sid| [sid, from_h[sid], to_h[sid]] }
|
||||
.flat_map do |sid, from, to|
|
||||
compare_type_de_champ(from.type_de_champ, to.type_de_champ)
|
||||
compare_type_de_champ(from.type_de_champ, to.type_de_champ, from_coordinates, to_coordinates)
|
||||
.each { |h| h[:_position] = to_sids.index(sid) }
|
||||
end
|
||||
|
||||
|
@ -327,7 +327,7 @@ class ProcedureRevision < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def compare_type_de_champ(from_type_de_champ, to_type_de_champ)
|
||||
def compare_type_de_champ(from_type_de_champ, to_type_de_champ, from_coordinates, to_coordinates)
|
||||
changes = []
|
||||
if from_type_de_champ.type_champ != to_type_de_champ.type_champ
|
||||
changes << {
|
||||
|
@ -385,8 +385,8 @@ class ProcedureRevision < ApplicationRecord
|
|||
attribute: :condition,
|
||||
label: from_type_de_champ.libelle,
|
||||
private: from_type_de_champ.private?,
|
||||
from: from_type_de_champ.condition&.to_s,
|
||||
to: to_type_de_champ.condition&.to_s,
|
||||
from: from_type_de_champ.condition&.to_s(from_coordinates.map(&:type_de_champ)),
|
||||
to: to_type_de_champ.condition&.to_s(to_coordinates.map(&:type_de_champ)),
|
||||
stable_id: from_type_de_champ.stable_id
|
||||
}
|
||||
end
|
||||
|
@ -479,12 +479,12 @@ class ProcedureRevision < ApplicationRecord
|
|||
end
|
||||
|
||||
def conditions_are_valid?
|
||||
stable_ids = types_de_champ_public.map(&:stable_id)
|
||||
public_tdcs = types_de_champ_public.to_a
|
||||
|
||||
types_de_champ_public
|
||||
public_tdcs
|
||||
.map.with_index
|
||||
.filter_map { |tdc, i| tdc.condition.present? ? [tdc, i] : nil }
|
||||
.map { |tdc, i| [tdc, tdc.condition.errors(stable_ids.take(i))] }
|
||||
.map { |tdc, i| [tdc, tdc.condition.errors(public_tdcs.take(i))] }
|
||||
.filter { |_tdc, errors| errors.present? }
|
||||
.each { |tdc, message| errors.add(:condition, message, type_de_champ: tdc) }
|
||||
end
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
describe Administrateurs::ConditionsController, type: :controller do
|
||||
include Logic
|
||||
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, types_de_champ_count: 2) }
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :integer_number }] * 3) }
|
||||
let(:first_coordinate) { procedure.draft_revision.revision_types_de_champ.first }
|
||||
let(:second_coordinate) { procedure.draft_revision.revision_types_de_champ.first }
|
||||
let(:third_coordinate) { procedure.draft_revision.revision_types_de_champ.first }
|
||||
let(:first_tdc) { procedure.draft_revision.types_de_champ.first }
|
||||
let(:second_tdc) { procedure.draft_revision.types_de_champ.second }
|
||||
let(:third_tdc) { procedure.draft_revision.types_de_champ.third }
|
||||
|
||||
before { sign_in(procedure.administrateurs.first.user) }
|
||||
|
||||
let(:default_params) do
|
||||
{
|
||||
procedure_id: procedure.id,
|
||||
stable_id: second_tdc.stable_id
|
||||
stable_id: third_tdc.stable_id
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -23,7 +27,7 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
{
|
||||
rows: [
|
||||
{
|
||||
targeted_champ: champ_value(1).to_json,
|
||||
targeted_champ: champ_value(first_tdc.stable_id).to_json,
|
||||
operator_name: Logic::Eq.name,
|
||||
value: '2'
|
||||
}
|
||||
|
@ -32,9 +36,9 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
end
|
||||
|
||||
it do
|
||||
expect(second_tdc.reload.condition).to eq(ds_eq(champ_value(1), constant('2')))
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(second_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_coordinate.type_de_champ])
|
||||
expect(third_tdc.reload.condition).to eq(ds_eq(champ_value(first_tdc.stable_id), constant(2)))
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -42,9 +46,9 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
before { post :add_row, params: default_params, format: :turbo_stream }
|
||||
|
||||
it do
|
||||
expect(second_tdc.reload.condition).to eq(empty_operator(empty, empty))
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(second_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_coordinate.type_de_champ])
|
||||
expect(third_tdc.reload.condition).to eq(empty_operator(empty, empty))
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,9 +70,9 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
end
|
||||
|
||||
it do
|
||||
expect(second_tdc.reload.condition).to eq(nil)
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(second_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_coordinate.type_de_champ])
|
||||
expect(third_tdc.reload.condition).to eq(nil)
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -79,15 +83,13 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
end
|
||||
|
||||
it do
|
||||
expect(second_tdc.reload.condition).to eq(nil)
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(second_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_coordinate.type_de_champ])
|
||||
expect(third_tdc.reload.condition).to eq(nil)
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#change_targeted_champ' do
|
||||
let!(:number_tdc) { create(:type_de_champ_integer_number) }
|
||||
|
||||
before do
|
||||
second_tdc.update(condition: empty_operator(empty, empty))
|
||||
patch :change_targeted_champ, params: params, format: :turbo_stream
|
||||
|
@ -99,7 +101,7 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
{
|
||||
rows: [
|
||||
{
|
||||
targeted_champ: champ_value(number_tdc.stable_id).to_json,
|
||||
targeted_champ: champ_value(second_tdc.stable_id).to_json,
|
||||
operator_name: Logic::EmptyOperator.name,
|
||||
value: empty.to_json
|
||||
}
|
||||
|
@ -108,9 +110,9 @@ describe Administrateurs::ConditionsController, type: :controller do
|
|||
end
|
||||
|
||||
it do
|
||||
expect(second_tdc.reload.condition).to eq(ds_eq(champ_value(number_tdc.stable_id), constant(0)))
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(second_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_coordinate.type_de_champ])
|
||||
expect(third_tdc.reload.condition).to eq(ds_eq(champ_value(second_tdc.stable_id), constant(0)))
|
||||
expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc))
|
||||
expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ describe Logic::And do
|
|||
|
||||
describe '#to_s' do
|
||||
it do
|
||||
expect(and_from([true, false, true]).to_s).to eq "(Oui && Non && Oui)"
|
||||
expect(and_from([true, false, true]).to_s([])).to eq "(Oui && Non && Oui)"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ describe Logic::BinaryOperator do
|
|||
end
|
||||
|
||||
describe '#to_s' do
|
||||
it { expect(two_greater_than_one.to_s).to eq('(2 > 1)') }
|
||||
it { expect(two_greater_than_one.to_s([])).to eq('(2 > 1)') }
|
||||
end
|
||||
|
||||
describe '#==' do
|
||||
|
|
|
@ -7,7 +7,7 @@ describe Logic::ChampValue do
|
|||
let(:value) { 'true' }
|
||||
let(:champ) { create(:champ_yes_no, value: value) }
|
||||
|
||||
it { expect(champ_value(champ.stable_id).type).to eq(:boolean) }
|
||||
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:boolean) }
|
||||
|
||||
context 'with true value' do
|
||||
it { is_expected.to be(true) }
|
||||
|
@ -31,7 +31,7 @@ describe Logic::ChampValue do
|
|||
context 'integer tdc' do
|
||||
let(:champ) { create(:champ_integer_number, value: '42') }
|
||||
|
||||
it { expect(champ_value(champ.stable_id).type).to eq(:number) }
|
||||
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
|
||||
|
@ -44,22 +44,22 @@ describe Logic::ChampValue do
|
|||
context 'decimal tdc' do
|
||||
let(:champ) { create(:champ_decimal_number, value: '42.01') }
|
||||
|
||||
it { expect(champ_value(champ.stable_id).type).to eq(:number) }
|
||||
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') }
|
||||
|
||||
it { expect(champ_value(champ.stable_id).type).to eq(:enum) }
|
||||
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).to match_array([["val1", "val1"], ["val2", "val2"], ["val3", "val3"]]) }
|
||||
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) }
|
||||
|
||||
it { is_expected.to eq('val1') }
|
||||
it { expect(champ_value(champ.stable_id).options).to match_array([["val1", "val1"], ["val2", "val2"], ["val3", "val3"], ["Autre", "__other__"]]) }
|
||||
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
|
||||
|
@ -72,14 +72,39 @@ describe Logic::ChampValue do
|
|||
context 'checkbox tdc' do
|
||||
let(:champ) { create(:champ_checkbox, value: 'on') }
|
||||
|
||||
it { expect(champ_value(champ.stable_id).type).to eq(:boolean) }
|
||||
it { expect(champ_value(champ.stable_id).type([champ.type_de_champ])).to eq(:boolean) }
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
|
||||
describe 'errors' do
|
||||
let(:champ) { create(:champ) }
|
||||
|
||||
it { expect(champ_value(champ.stable_id).errors([champ.stable_id])).to be_empty }
|
||||
it { expect(champ_value(champ.stable_id).errors(['other stable ids'])).to eq([{ type: :not_available }]) }
|
||||
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
|
||||
|
||||
context 'with multiple revision' do
|
||||
let(:options) { ['revision_1'] }
|
||||
let(:procedure) do
|
||||
create(:procedure, :published, :for_individual, types_de_champ_public: [{ type: :drop_down_list, libelle: 'dropdown', options: options }])
|
||||
end
|
||||
let(:drop_down_r1) { procedure.published_revision.types_de_champ_public.first }
|
||||
let(:stable_id) { drop_down_r1.stable_id }
|
||||
|
||||
it { expect(champ_value(stable_id).options([drop_down_r1])).to match_array([["revision_1", "revision_1"]]) }
|
||||
|
||||
context 'with a new revision' do
|
||||
let(:drop_down_r2) { procedure.draft_revision.types_de_champ_public.first }
|
||||
|
||||
before do
|
||||
tdc = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id)
|
||||
tdc.drop_down_options = ['revision_2']
|
||||
tdc.save!
|
||||
end
|
||||
|
||||
it do
|
||||
expect(champ_value(stable_id).options([drop_down_r2])).to match_array([["revision_2", "revision_2"]])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ describe Logic::IncludeOperator do
|
|||
end
|
||||
|
||||
describe '#errors' do
|
||||
it { expect(ds_include(champ_value(champ.stable_id), constant('val1')).errors([champ.stable_id])).to be_empty }
|
||||
it { expect(ds_include(champ_value(champ.stable_id), constant('val1')).errors([champ.type_de_champ])).to be_empty }
|
||||
it do
|
||||
expected = {
|
||||
right: constant('something else'),
|
||||
|
@ -17,10 +17,10 @@ describe Logic::IncludeOperator do
|
|||
type: :not_included
|
||||
}
|
||||
|
||||
expect(ds_include(champ_value(champ.stable_id), constant('something else')).errors([champ.stable_id])).to eq([expected])
|
||||
expect(ds_include(champ_value(champ.stable_id), constant('something else')).errors([champ.type_de_champ])).to eq([expected])
|
||||
end
|
||||
|
||||
it { expect(ds_include(constant(1), constant('val1')).errors).to eq([{ type: :required_list }]) }
|
||||
it { expect(ds_include(constant(1), constant('val1')).errors([])).to eq([{ type: :required_list }]) }
|
||||
end
|
||||
|
||||
describe '#==' do
|
||||
|
|
|
@ -16,7 +16,8 @@ describe Logic do
|
|||
end
|
||||
|
||||
describe '.ensure_compatibility_from_left' do
|
||||
subject { Logic.ensure_compatibility_from_left(condition) }
|
||||
let(:type_de_champs) { [] }
|
||||
subject { Logic.ensure_compatibility_from_left(condition, type_de_champs) }
|
||||
|
||||
context 'when it s fine' do
|
||||
let(:condition) { greater_than(constant(1), constant(1)) }
|
||||
|
@ -44,31 +45,33 @@ describe Logic do
|
|||
|
||||
context 'when dropdown empty operator true' do
|
||||
let(:drop_down) { create(:type_de_champ_drop_down_list) }
|
||||
let(:type_de_champs) { [drop_down] }
|
||||
let(:first_option) { drop_down.drop_down_list_enabled_non_empty_options.first }
|
||||
let(:condition) { empty_operator(champ_value(drop_down), constant(true)) }
|
||||
let(:condition) { empty_operator(champ_value(drop_down.stable_id), constant(true)) }
|
||||
|
||||
it { is_expected.to eq(ds_eq(champ_value(drop_down), constant(first_option))) }
|
||||
it { is_expected.to eq(ds_eq(champ_value(drop_down.stable_id), constant(first_option))) }
|
||||
end
|
||||
|
||||
context 'when multiple dropdown empty operator true' do
|
||||
let(:multiple_drop_down) { create(:type_de_champ_multiple_drop_down_list) }
|
||||
let(:type_de_champs) { [multiple_drop_down] }
|
||||
let(:first_option) { multiple_drop_down.drop_down_list_enabled_non_empty_options.first }
|
||||
let(:condition) { empty_operator(champ_value(multiple_drop_down), constant(true)) }
|
||||
let(:condition) { empty_operator(champ_value(multiple_drop_down.stable_id), constant(true)) }
|
||||
|
||||
it { is_expected.to eq(ds_include(champ_value(multiple_drop_down), constant(first_option))) }
|
||||
it { is_expected.to eq(ds_include(champ_value(multiple_drop_down.stable_id), constant(first_option))) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.compatible_type?' do
|
||||
it { expect(Logic.compatible_type?(constant(true), constant(true))).to be true }
|
||||
it { expect(Logic.compatible_type?(constant(1), constant(true))).to be false }
|
||||
it { expect(Logic.compatible_type?(constant(true), constant(true), [])).to be true }
|
||||
it { expect(Logic.compatible_type?(constant(1), constant(true), [])).to be false }
|
||||
|
||||
context 'with a dropdown' do
|
||||
let(:drop_down) { create(:type_de_champ_drop_down_list) }
|
||||
let(:first_option) { drop_down.drop_down_list_enabled_non_empty_options.first }
|
||||
|
||||
it do
|
||||
expect(Logic.compatible_type?(champ_value(drop_down.stable_id), constant('a'))).to be true
|
||||
expect(Logic.compatible_type?(champ_value(drop_down.stable_id), constant('a'), [drop_down])).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue