diff --git a/source/engine/evaluation.js b/source/engine/evaluation.js index c0df56734..bf8beaeed 100644 --- a/source/engine/evaluation.js +++ b/source/engine/evaluation.js @@ -1,10 +1,16 @@ import { + add, + max, map, pluck, any, equals, reduce, + mergeWith, chain, + length, + flatten, + uniq, fromPairs, keys, values, @@ -17,17 +23,20 @@ export let makeJsx = node => ? node.jsx(node.nodeValue, node.explanation) : node.jsx -export let collectNodeMissing = node => - node.collectMissing ? node.collectMissing(node) : [] +export let collectNodeMissing = node => node.missingVariables || {} + +export let bonus = (missings, hasCondition=true) => hasCondition ? map(x=>x+.0001, missings || {}) : missings +export let mergeAllMissing = missings => reduce(mergeWith(add),{},map(collectNodeMissing,missings)) +export let mergeMissing = (left, right) => mergeWith(add, left || {}, right || {}) export let evaluateNode = (cache, situationGate, parsedRules, node) => node.evaluate ? node.evaluate(cache, situationGate, parsedRules, node) : node -export let rewriteNode = (node, nodeValue, explanation, collectMissing) => ({ +export let rewriteNode = (node, nodeValue, explanation, missingVariables) => ({ ...node, nodeValue, - collectMissing, - explanation + explanation, + missingVariables }) export let evaluateArray = (reducer, start) => ( @@ -42,11 +51,12 @@ export let evaluateArray = (reducer, start) => ( values = pluck('nodeValue', explanation), nodeValue = any(equals(null), values) ? null - : reduce(reducer, start, values) - - let collectMissing = node => - node.nodeValue == null ? chain(collectNodeMissing, node.explanation) : [] - return rewriteNode(node, nodeValue, explanation, collectMissing) + : reduce(reducer, start, values), + missingVariables = node.nodeValue == null + ? mergeAllMissing(explanation) + : {} +// console.log("".padStart(cache.parseLevel), missingVariables) + return rewriteNode(node, nodeValue, explanation, missingVariables) } export let evaluateArrayWithFilter = (evaluationFilter, reducer, start) => ( @@ -64,10 +74,12 @@ export let evaluateArrayWithFilter = (evaluationFilter, reducer, start) => ( values = pluck('nodeValue', explanation), nodeValue = any(equals(null), values) ? null - : reduce(reducer, start, values) + : reduce(reducer, start, values), + missingVariables = node.nodeValue == null + ? mergeAllMissing(explanation) + : {} - let collectMissing = node => chain(collectNodeMissing, node.explanation) - return rewriteNode(node, nodeValue, explanation, collectMissing) + return rewriteNode(node, nodeValue, explanation, missingVariables) } export let parseObject = (recurse, objectShape, value) => { @@ -86,11 +98,12 @@ export let evaluateObject = (objectShape, effect) => ( node ) => { let evaluateOne = child => - evaluateNode(cache, situationGate, parsedRules, child), - collectMissing = node => chain(collectNodeMissing, values(node.explanation)) + evaluateNode(cache, situationGate, parsedRules, child) let transforms = map(k => [k, evaluateOne], keys(objectShape)), explanation = evolve(fromPairs(transforms))(node.explanation), - nodeValue = effect(explanation) - return rewriteNode(node, nodeValue, explanation, collectMissing) + nodeValue = effect(explanation), + missingVariables = mergeAllMissing(values(explanation)) +// console.log("".padStart(cache.parseLevel),map(node => length(flatten(collectNodeMissing(node))) ,explanation)) + return rewriteNode(node, nodeValue, explanation, missingVariables) } diff --git a/source/engine/generateQuestions.js b/source/engine/generateQuestions.js index 7af07df0f..1155a1a69 100644 --- a/source/engine/generateQuestions.js +++ b/source/engine/generateQuestions.js @@ -1,5 +1,7 @@ import { - chain, + flatten, + mergeAll, + pluck, groupBy, toPairs, sort, @@ -22,7 +24,6 @@ import SelectAtmp from 'Components/conversation/select/SelectTauxRisque' import formValueTypes from 'Components/conversation/formValueTypes' import { findRuleByDottedName, disambiguateRuleReference } from './rules' -import { collectNodeMissing } from './evaluation' /* COLLECTE DES VARIABLES MANQUANTES @@ -39,13 +40,10 @@ import { collectNodeMissing } from './evaluation' missingVariables: {variable: [objectives]} */ -export let collectMissingVariables = targets => { - let missing = chain(collectNodeMissing, targets) - return groupBy(identity, missing) -} +export let collectMissingVariables = targets => mergeAll(pluck('missingVariables', targets)) export let getNextSteps = (situationGate, analysis) => { - let impact = ([, objectives]) => length(objectives) + let impact = ([, count]) => count let missingVariables = collectMissingVariables(analysis.targets), pairs = toPairs(missingVariables), diff --git a/source/engine/mecanisms.js b/source/engine/mecanisms.js index a7988980e..6b1836992 100644 --- a/source/engine/mecanisms.js +++ b/source/engine/mecanisms.js @@ -1,4 +1,9 @@ import { + flatten, + reduce, + mergeWith, + mergeAll, + length, objOf, toPairs, dissoc, @@ -16,6 +21,7 @@ import { evolve, curry, filter, + all, pipe, head, isEmpty, @@ -40,7 +46,10 @@ import { evaluateArrayWithFilter, evaluateObject, parseObject, - collectNodeMissing + collectNodeMissing, + mergeAllMissing, + mergeMissing, + bonus } from './evaluation' import { findRuleByName, @@ -144,23 +153,18 @@ let devariate = (recurse, k, v) => { } let explanation = map(evaluateOne, node.explanation), - choice = find(node => node.condition.nodeValue, explanation), + candidates = filter(node => node.condition.nodeValue !== false, explanation), + satisfied = filter(node => node.condition.nodeValue, explanation), + choice = head(satisfied), nodeValue = choice ? choice.nodeValue : null - let collectMissing = node => { - let choice = find(node => node.condition.nodeValue, node.explanation), - leftMissing = choice - ? [] - : uniq( - chain(collectNodeMissing, pluck('condition', node.explanation)) - ), - rightMissing = choice - ? collectNodeMissing(choice) - : chain(collectNodeMissing, node.explanation) - return concat(leftMissing, rightMissing) - } + let leftMissing = choice + ? {} + : mergeAllMissing(pluck('condition', explanation)), + rightMissing = mergeAllMissing(candidates), + missingVariables = mergeMissing(bonus(leftMissing), rightMissing) - return rewriteNode(node, nodeValue, explanation, collectMissing) + return rewriteNode(node, nodeValue, explanation, missingVariables) } // TODO - find an appropriate representation @@ -221,11 +225,13 @@ export let mecanismOneOf = (recurse, k, v) => { values = pluck('nodeValue', explanation), nodeValue = any(equals(true), values) ? true - : any(equals(null), values) ? null : false + : any(equals(null), values) ? null : false, + // Unlike most other array merges of missing variables this is a "flat" merge + // because "one of these conditions" tend to be several tests of the same variable + // (e.g. contract type is one of x, y, z) + missingVariables = nodeValue == null ? reduce(mergeWith(max),{},map(collectNodeMissing,explanation)) : {} - let collectMissing = node => - node.nodeValue == null ? chain(collectNodeMissing, node.explanation) : [] - return rewriteNode(node, nodeValue, explanation, collectMissing) + return rewriteNode(node, nodeValue, explanation, missingVariables) } return { @@ -265,11 +271,10 @@ export let mecanismAllOf = (recurse, k, v) => { values = pluck('nodeValue', explanation), nodeValue = any(equals(false), values) ? false // court-circuit - : any(equals(null), values) ? null : true + : any(equals(null), values) ? null : true, + missingVariables = nodeValue == null ? mergeAllMissing(explanation) : {} - let collectMissing = node => - node.nodeValue == null ? chain(collectNodeMissing, node.explanation) : [] - return rewriteNode(node, nodeValue, explanation, collectMissing) + return rewriteNode(node, nodeValue, explanation, missingVariables) } return { @@ -302,27 +307,24 @@ export let mecanismNumericalSwitch = (recurse, k, v) => { consequenceNode = mecanismNumericalSwitch(recurse, condition, consequence) let evaluate = (cache, situationGate, parsedRules, node) => { - let collectMissing = node => { - let missingOnTheLeft = collectNodeMissing(node.explanation.condition), - investigate = node.explanation.condition.nodeValue !== false, - missingOnTheRight = investigate - ? collectNodeMissing(node.explanation.consequence) - : [] - return concat(missingOnTheLeft, missingOnTheRight) - } - let explanation = evolve( { condition: curry(evaluateNode)(cache, situationGate, parsedRules), consequence: curry(evaluateNode)(cache, situationGate, parsedRules) }, node.explanation - ) + ), + leftMissing = explanation.condition.missingVariables, + investigate = explanation.condition.nodeValue !== false, + rightMissing = investigate + ? explanation.consequence.missingVariables + : {}, + missingVariables = mergeMissing(bonus(leftMissing), rightMissing) return { ...node, - collectMissing, explanation, + missingVariables, nodeValue: explanation.consequence.nodeValue, condValue: explanation.condition.nodeValue } @@ -360,16 +362,13 @@ export let mecanismNumericalSwitch = (recurse, k, v) => { getFirst('condValue') == null ? null : // c'est un true, on renvoie la valeur de la conséquence - getFirst('nodeValue') + getFirst('nodeValue'), + choice = find(node => node.condValue, explanation), + missingVariables = choice + ? choice.missingVariables + : mergeAllMissing(explanation) - let collectMissing = node => { - let choice = find(node => node.condValue, node.explanation) - return choice - ? collectNodeMissing(choice) - : chain(collectNodeMissing, node.explanation) - } - - return rewriteNode(node, nodeValue, explanation, collectMissing) + return rewriteNode(node, nodeValue, explanation, missingVariables) } let explanation = map(parseCondition, terms) @@ -429,46 +428,38 @@ export let findInversion = (situationGate, rules, v, dottedName) => { } } -let doInversion = (situationGate, parsedRules, v, dottedName) => { +let doInversion = (oldCache, situationGate, parsedRules, v, dottedName) => { let inversion = findInversion(situationGate, parsedRules, v, dottedName) if (inversion.inversionChoiceNeeded) return { - inversionMissingVariables: [dottedName], + missingVariables: {[dottedName]:1}, nodeValue: null } let { fixedObjectiveValue, fixedObjectiveRule } = inversion let inversionCache = {} let fx = x => { - inversionCache = {} + inversionCache = {parseLevel: oldCache.parseLevel+1, op:"<"} return evaluateNode( inversionCache, // with an empty cache n => (dottedName === n ? x : situationGate(n)), parsedRules, fixedObjectiveRule - ).nodeValue + ) } // si fx renvoie null pour une valeur numérique standard, disons 1000, on peut // considérer que l'inversion est impossible du fait de variables manquantes // TODO fx peut être null pour certains x, et valide pour d'autres : on peut implémenter ici le court-circuit - if (fx(1000) == null) - return { - nodeValue: null, - inversionMissingVariables: collectNodeMissing( - evaluateNode( - {}, - n => (dottedName === n ? 1000 : situationGate(n)), - parsedRules, - fixedObjectiveRule - ) - ) - } + let attempt = fx(1000) + if (attempt.nodeValue == null) { + return attempt + } let tolerancePercentage = 0.00001, // cette fonction détermine la racine d'une fonction sans faire trop d'itérations nodeValue = uniroot( - x => fx(x) - fixedObjectiveValue, + x => fx(x).nodeValue - fixedObjectiveValue, 0, 1000000000, tolerancePercentage * fixedObjectiveValue, @@ -477,7 +468,7 @@ let doInversion = (situationGate, parsedRules, v, dottedName) => { return { nodeValue, - inversionMissingVariables: [], + missingVariables: {}, inversionCache } } @@ -486,14 +477,15 @@ export let mecanismInversion = dottedName => (recurse, k, v) => { let evaluate = (cache, situationGate, parsedRules, node) => { let inversion = // avoid the inversion loop ! - situationGate(dottedName) == undefined && - doInversion(situationGate, parsedRules, v, dottedName) - let collectMissing = () => inversion.inversionMissingVariables, - nodeValue = inversion.nodeValue + situationGate(dottedName) == undefined && + doInversion(cache, situationGate, parsedRules, v, dottedName), + nodeValue = inversion.nodeValue, + missingVariables = inversion.missingVariables - let evaluatedNode = rewriteNode(node, nodeValue, null, collectMissing) - // rewrite the simulation cache with the definitive inversion values + let evaluatedNode = rewriteNode(node, nodeValue, null, missingVariables) + // TODO - we need this so that ResultsGrid will work, but it's + // just not right toPairs(inversion.inversionCache).map(([k, v]) => (cache[k] = v)) return evaluatedNode } @@ -932,8 +924,7 @@ export let mecanismSelection = (recurse, k, v) => { let explanation = recurse(v['cherche']) let evaluate = (cache, situationGate, parsedRules, node) => { - let collectMissing = node => collectNodeMissing(node.explanation), - explanation = evaluateNode( + let explanation = evaluateNode( cache, situationGate, parsedRules, @@ -962,8 +953,10 @@ export let mecanismSelection = (recurse, k, v) => { ? sortedSubValues ? Number.parseFloat(last(sortedSubValues)[1]) / 100 : 0 - : null - return rewriteNode(node, nodeValue, explanation, collectMissing) + : null, + missingVariables = explanation.missingVariables + + return rewriteNode(node, nodeValue, explanation, missingVariables) } let SelectionView = buildSelectionView(dataTargetName) diff --git a/source/engine/traverse-common-functions.js b/source/engine/traverse-common-functions.js index ab77c9047..e89d25549 100644 --- a/source/engine/traverse-common-functions.js +++ b/source/engine/traverse-common-functions.js @@ -5,5 +5,3 @@ export let val = node => node && node.nodeValue export let undefOrTrue = val => val == undefined || val == true export let anyNull = any(pipe(val, equals(null))) - -export let applyOrEmpty = func => v => (v ? func(v) : []) diff --git a/source/engine/traverse.js b/source/engine/traverse.js index 6662804ed..a7bdd3d0c 100644 --- a/source/engine/traverse.js +++ b/source/engine/traverse.js @@ -19,6 +19,9 @@ import { divide, multiply, map, + merge, + length, + flatten, intersection, keys, is, @@ -55,14 +58,15 @@ import { import { evaluateNode, rewriteNode, - collectNodeMissing, - makeJsx + makeJsx, + mergeMissing, + mergeAllMissing, + bonus } from './evaluation' import { anyNull, val, - undefOrTrue, - applyOrEmpty + undefOrTrue } from './traverse-common-functions' let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart) @@ -131,53 +135,34 @@ let fillVariableNode = (rules, rule, filter) => parseResult => { // On va vérifier dans le cache courant, dict, si la variable n'a pas été déjà évaluée // En effet, l'évaluation dans le cas d'une variable qui a une formule, est coûteuse ! cacheName = dottedName + (filter ? '.' + filter : ''), - cached = cache[cacheName], - // make parsedRules a dict object, that also serves as a cache of evaluation ? - variable = cached - ? cached - : findRuleByDottedName(parsedRules, dottedName), + cached = cache[cacheName] + if (cached) { + return cached + } + + let variable = findRuleByDottedName(parsedRules, dottedName), variableIsCalculable = variable.formule != null, - parsedRule = - variableIsCalculable && - (cached - ? cached - : evaluateNode(cache, situation, parsedRules, variable)), - // evaluateVariable renvoit la valeur déduite de la situation courante renseignée par l'utilisateur situationValue = evaluateVariable(situation, dottedName, variable), + needsEvaluation = (variableIsCalculable && situationValue == null), + parsedRule = needsEvaluation + ? evaluateNode(cache, situation, parsedRules, variable) + : variable, + // evaluateVariable renvoit la valeur déduite de la situation courante renseignée par l'utilisateur + explanation = parsedRule, nodeValue = situationValue != null ? situationValue // cette variable a été directement renseignée : variableIsCalculable ? parsedRule.nodeValue // la valeur du calcul fait foi : null, // elle restera donc nulle - explanation = parsedRule, - missingVariables = variableIsCalculable ? [] : [dottedName] - - let collectMissing = node => { - let missingName = cacheName + ':missing', - cached = cache[missingName] - - if (cached) return cached - - let result = - nodeValue != null // notamment si situationValue != null - ? [] + missingVariables = nodeValue != null // notamment si situationValue != null + ? {} : variableIsCalculable - ? collectNodeMissing(parsedRule) - : node.missingVariables - cache[missingName] = result - return result - } + ? parsedRule.missingVariables + : {[dottedName]:1} - if (cached) { - return cached - } else { - cache[cacheName] = { - ...rewriteNode(node, nodeValue, explanation, collectMissing), - missingVariables - } - return cache[cacheName] - } + cache[cacheName] = rewriteNode(node, nodeValue, explanation, missingVariables) + return cache[cacheName] } let { fragments } = parseResult, @@ -212,9 +197,10 @@ let buildNegatedVariable = variable => { parsedRules, node.explanation ), - nodeValue = explanation.nodeValue == null ? null : !explanation.nodeValue - let collectMissing = node => collectNodeMissing(node.explanation) - return rewriteNode(node, nodeValue, explanation, collectMissing) + nodeValue = explanation.nodeValue == null ? null : !explanation.nodeValue, + missingVariables = explanation.missingVariables + + return rewriteNode(node, nodeValue, explanation, missingVariables) } let jsx = (nodeValue, explanation) => ( @@ -317,12 +303,12 @@ let treat = (rules, rule) => rawNode => { nodeValue = value1 == null || value2 == null ? null - : operatorFunction(value1, value2) + : operatorFunction(value1, value2), + missingVariables = mergeMissing( + explanation[0].missingVariables, + explanation[1].missingVariables) - let collectMissing = node => - chain(collectNodeMissing, node.explanation) - - return rewriteNode(node, nodeValue, explanation, collectMissing) + return rewriteNode(node, nodeValue, explanation, missingVariables) } let fillFiltered = parseResult => @@ -425,7 +411,7 @@ let treat = (rules, rule) => rawNode => { sélection: mecanismSelection, 'une possibilité': always({ 'une possibilité': 'oui', - collectMissing: () => [rule.dottedName] + missingVariables: {[rule.dottedName]:1} }), inversion: mecanismInversion(rule.dottedName), allègement: mecanismReduction @@ -461,9 +447,12 @@ export let treatRuleRoot = (rules, rule) => { Aujourd'hui, une règle peut avoir (comme propriétés à parser) `non applicable si` et `formule`, qui ont elles-mêmes des propriétés de type mécanisme (ex. barème) ou des expressions en ligne (ex. maVariable + 3). Ces mécanismes où variables sont descendues à leur tour grâce à `treat()`. - Lors de ce traitement, des fonctions 'evaluate', `collectMissingVariables` et `jsx` sont attachés aux objets de l'AST + Lors de ce traitement, des fonctions 'evaluate' et `jsx` sont attachés aux objets de l'AST */ - let evaluate = (cache, situationGate, parsedRules, r) => { + let evaluate = (cache, situationGate, parsedRules, node) => { +// console.log((cache.op || ">").padStart(cache.parseLevel),rule.dottedName) + cache.parseLevel++ + let evolveRule = curry(evaluateNode)(cache, situationGate, parsedRules), evaluated = evolve( { @@ -471,7 +460,7 @@ export let treatRuleRoot = (rules, rule) => { 'non applicable si': evolveRule, 'applicable si': evolveRule }, - r + node ), formuleValue = val(evaluated['formule']), isApplicable = do { @@ -487,32 +476,32 @@ export let treatRuleRoot = (rules, rule) => { }, nodeValue = computeRuleValue(formuleValue, isApplicable) - return { ...evaluated, nodeValue, isApplicable } - } - - let collectMissing = rule => { let { formule, - isApplicable, 'non applicable si': notApplicable, 'applicable si': applicable - } = rule + } = evaluated let condMissing = val(notApplicable) === true - ? [] + ? {} : val(applicable) === false - ? [] - : [ - ...applyOrEmpty(collectNodeMissing)(notApplicable), - ...applyOrEmpty(collectNodeMissing)(applicable) - ], + ? {} + : merge( + (notApplicable && notApplicable.missingVariables) || {}, + (applicable && applicable.missingVariables) || {} + ), collectInFormule = isApplicable !== false, - formMissing = applyOrEmpty(() => - applyOrEmpty(collectNodeMissing)(formule) - )(collectInFormule) + formMissing = (collectInFormule && formule.missingVariables) || {}, + // On veut abaisser le score des conséquences par rapport aux conditions, + // mais seulement dans le cas où une condition est effectivement présente + hasCondition = keys(condMissing).length > 0, + missingVariables = mergeMissing(bonus(condMissing,hasCondition), formMissing) - return concat(condMissing, formMissing) + cache.parseLevel-- +// if (keys(condMissing).length) console.log("".padStart(cache.parseLevel-1),{conditions:condMissing, formule:formMissing}) +// else console.log("".padStart(cache.parseLevel-1),{formule:formMissing}) + return { ...evaluated, nodeValue, isApplicable, missingVariables } } let parsedRoot = evolve({ @@ -524,15 +513,16 @@ export let treatRuleRoot = (rules, rule) => { 'applicable si': evolveCond('applicable si', rule, rules), formule: value => { let evaluate = (cache, situationGate, parsedRules, node) => { - let collectMissing = node => collectNodeMissing(node.explanation) let explanation = evaluateNode( cache, situationGate, parsedRules, node.explanation ), - nodeValue = explanation.nodeValue - return rewriteNode(node, nodeValue, explanation, collectMissing) + nodeValue = explanation.nodeValue, + missingVariables = explanation.missingVariables + + return rewriteNode(node, nodeValue, explanation, missingVariables) } let child = treat(rules, rule)(value) @@ -555,22 +545,22 @@ export let treatRuleRoot = (rules, rule) => { // Pas de propriété explanation et jsx ici car on est parti du (mauvais) principe que 'non applicable si' et 'formule' sont particuliers, alors qu'ils pourraient être rangé avec les autres mécanismes ...parsedRoot, evaluate, - collectMissing, parsed: true } } let evolveCond = (name, rule, rules) => value => { let evaluate = (cache, situationGate, parsedRules, node) => { - let collectMissing = node => collectNodeMissing(node.explanation) let explanation = evaluateNode( cache, situationGate, parsedRules, node.explanation ), - nodeValue = explanation.nodeValue - return rewriteNode(node, nodeValue, explanation, collectMissing) + nodeValue = explanation.nodeValue, + missingVariables = explanation.missingVariables + + return rewriteNode(node, nodeValue, explanation, missingVariables) } let child = treat(rules, rule)(value) @@ -622,7 +612,7 @@ export let parseAll = flatRules => { export let analyseMany = (parsedRules, targetNames) => situationGate => { // TODO: we should really make use of namespaces at this level, in particular // setRule in Rule.js needs to get smarter and pass dottedName - let cache = {} + let cache = {parseLevel: 0} let parsedTargets = targetNames.map(t => findRule(parsedRules, t)), targets = chain(pt => getTargets(pt, parsedRules), parsedTargets).map(t => diff --git a/test/generateQuestions.test.js b/test/generateQuestions.test.js index e170835a0..3b804f8f2 100644 --- a/test/generateQuestions.test.js +++ b/test/generateQuestions.test.js @@ -160,19 +160,20 @@ describe('collectMissingVariables', function() { formule: { barème: { assiette: 2008, - 'multiplicateur des tranches': 1000, variations: [ { si: 'dix', + 'multiplicateur des tranches': 'deux', tranches: [ { 'en-dessous de': 1, taux: 0.1 }, - { de: 1, à: 2, taux: 'deux' }, + { de: 1, à: 2, taux: 'trois' }, , { 'au-dessus de': 2, taux: 10 } ] }, { si: '3 > 4', + 'multiplicateur des tranches': 'quatre', tranches: [ { 'en-dessous de': 1, taux: 0.1 }, { de: 1, à: 2, taux: 1.8 }, @@ -185,14 +186,19 @@ describe('collectMissingVariables', function() { } }, { nom: 'dix', espace: 'top' }, - { nom: 'deux', espace: 'top' } + { nom: 'deux', espace: 'top' }, + { nom: 'trois', espace: 'top' }, + { nom: 'quatre', espace: 'top' } ], rules = parseAll(rawRules.map(enrichRule)), analysis = analyse(rules, 'startHere')(stateSelector), result = collectMissingVariables(analysis.targets) expect(result).to.have.property('top . dix') - // expect(result).to.have.property('top . deux') - this is a TODO + expect(result).to.have.property('top . deux') + expect(result).not.to.have.property('top . quatre') + // TODO + // expect(result).to.have.property('top . trois') }) it('should not report missing variables in irrelevant variations', function() { diff --git a/test/inversion.test.js b/test/inversion.test.js index 72f74c454..2e23131bb 100644 --- a/test/inversion.test.js +++ b/test/inversion.test.js @@ -1,3 +1,4 @@ +import { length } from 'ramda' import { expect } from 'chai' import { rules as realRules, enrichRule } from '../source/engine/rules' import { analyse, analyseMany, parseAll } from '../source/engine/traverse' @@ -105,8 +106,23 @@ describe('inversions', () => { - net - nom: cadre - nom: assiette - formule: 67 + brut - + formule: + somme: + - 1200 + - brut + - taxeOne + - nom: taxeOne + non applicable si: cadre + formule: taxe + taxe + - nom: taxe + formule: + multiplication: + assiette: 1200 + variations: + - si: cadre + taux: 80% + - si: ≠ cadre + taux: 70% `, rules = parseAll(yaml.safeLoad(rawRules).map(enrichRule)), stateSelector = name => ({ net: 2000 }[name]),