@rsuci/shared-form-components 1.0.140 → 1.0.141

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tests unitaires pour la pseudo-variable ${_INDEX_} dans le ConditionEngine
3
+ * Permet de référencer le numéro d'instance courant dans les groupes multiples
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=condition-engine-index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition-engine-index.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/condition-engine-index.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Tests unitaires pour la pseudo-variable ${_INDEX_} dans le ConditionEngine
3
+ * Permet de référencer le numéro d'instance courant dans les groupes multiples
4
+ */
5
+ import { ConditionEngine } from '../condition-engine';
6
+ function makeResponse(variableCode, valeur, numeroMembre) {
7
+ const key = numeroMembre ? `${variableCode}_${numeroMembre}` : variableCode;
8
+ return {
9
+ variableId: 1,
10
+ variableCode,
11
+ valeur,
12
+ numeroMembre,
13
+ dateModification: new Date(),
14
+ };
15
+ }
16
+ function buildResponses(entries) {
17
+ const responses = {};
18
+ for (const entry of entries) {
19
+ const key = entry.numeroMembre
20
+ ? `${entry.variableCode}_${entry.numeroMembre}`
21
+ : entry.variableCode;
22
+ responses[key] = entry;
23
+ }
24
+ return responses;
25
+ }
26
+ describe('ConditionEngine - ${_INDEX_} pseudo-variable', () => {
27
+ describe('showMe avec _INDEX_', () => {
28
+ it('devrait retourner true quand _INDEX_ == instance courante', () => {
29
+ const engine = new ConditionEngine({});
30
+ expect(engine.evaluate('showMe(${_INDEX_} == 1)', 1)).toBe(true);
31
+ });
32
+ it('devrait retourner false quand _INDEX_ != instance courante', () => {
33
+ const engine = new ConditionEngine({});
34
+ expect(engine.evaluate('showMe(${_INDEX_} == 1)', 2)).toBe(false);
35
+ });
36
+ });
37
+ describe('hideMe avec _INDEX_', () => {
38
+ it('devrait retourner true (visible) quand la condition hideMe est fausse', () => {
39
+ const engine = new ConditionEngine({});
40
+ expect(engine.evaluate('hideMe(${_INDEX_} > 2)', 1)).toBe(true);
41
+ });
42
+ it('devrait retourner false (masqué) quand la condition hideMe est vraie', () => {
43
+ const engine = new ConditionEngine({});
44
+ expect(engine.evaluate('hideMe(${_INDEX_} > 2)', 3)).toBe(false);
45
+ });
46
+ });
47
+ describe('mix avec auto-suffixage', () => {
48
+ it('devrait combiner _INDEX_ et variables itératives', () => {
49
+ const responses = buildResponses([
50
+ makeResponse('AGE', 25, 1),
51
+ makeResponse('AGE', 15, 2),
52
+ ]);
53
+ const engine = new ConditionEngine(responses);
54
+ engine.setMultipleGroupVariables(new Set(['AGE']));
55
+ // Instance 1 : _INDEX_ == 1 && AGE_1 (25) >= 18 → true
56
+ expect(engine.evaluate('showMe(${_INDEX_} == 1 && ${AGE} >= 18)', 1)).toBe(true);
57
+ // Instance 2 : _INDEX_ == 1 → false (court-circuit)
58
+ expect(engine.evaluate('showMe(${_INDEX_} == 1 && ${AGE} >= 18)', 2)).toBe(false);
59
+ });
60
+ });
61
+ describe('sans itération (currentIteration undefined)', () => {
62
+ it('devrait utiliser 1 par défaut', () => {
63
+ const engine = new ConditionEngine({});
64
+ expect(engine.evaluate('showMe(${_INDEX_} == 1)')).toBe(true);
65
+ });
66
+ it('devrait retourner false pour _INDEX_ != 1 sans itération', () => {
67
+ const engine = new ConditionEngine({});
68
+ expect(engine.evaluate('showMe(${_INDEX_} == 2)')).toBe(false);
69
+ });
70
+ });
71
+ describe('opérateurs de comparaison', () => {
72
+ it('devrait supporter >=', () => {
73
+ const engine = new ConditionEngine({});
74
+ expect(engine.evaluate('${_INDEX_} >= 2', 2)).toBe(true);
75
+ expect(engine.evaluate('${_INDEX_} >= 2', 1)).toBe(false);
76
+ });
77
+ it('devrait supporter <', () => {
78
+ const engine = new ConditionEngine({});
79
+ expect(engine.evaluate('${_INDEX_} < 4', 3)).toBe(true);
80
+ expect(engine.evaluate('${_INDEX_} < 4', 4)).toBe(false);
81
+ });
82
+ it('devrait supporter !=', () => {
83
+ const engine = new ConditionEngine({});
84
+ expect(engine.evaluate('${_INDEX_} != 1', 2)).toBe(true);
85
+ expect(engine.evaluate('${_INDEX_} != 1', 1)).toBe(false);
86
+ });
87
+ });
88
+ describe('isolation du cache', () => {
89
+ it('devrait retourner des résultats différents pour des instances différentes', () => {
90
+ const engine = new ConditionEngine({});
91
+ const condition = 'showMe(${_INDEX_} == 1)';
92
+ expect(engine.evaluate(condition, 1)).toBe(true);
93
+ expect(engine.evaluate(condition, 2)).toBe(false);
94
+ // Re-évaluer pour vérifier que le cache ne contamine pas
95
+ expect(engine.evaluate(condition, 1)).toBe(true);
96
+ });
97
+ });
98
+ describe('pas de suffixage de _INDEX_', () => {
99
+ it('ne devrait pas transformer _INDEX_ en _INDEX__2', () => {
100
+ const responses = buildResponses([
101
+ makeResponse('NOM', 'Jean', 1),
102
+ makeResponse('NOM', 'Marie', 2),
103
+ ]);
104
+ const engine = new ConditionEngine(responses);
105
+ engine.setMultipleGroupVariables(new Set(['NOM']));
106
+ // Si _INDEX_ était suffixé, il deviendrait _INDEX__2 et l'évaluation échouerait
107
+ expect(engine.evaluate('showMe(${_INDEX_} == 2)', 2)).toBe(true);
108
+ });
109
+ });
110
+ describe('condition croisée avec index explicite', () => {
111
+ it('devrait évaluer la valeur du membre 1 depuis une autre instance', () => {
112
+ const responses = buildResponses([
113
+ makeResponse('C0_02', 'oui', 1),
114
+ makeResponse('C0_02', 'non', 2),
115
+ ]);
116
+ const engine = new ConditionEngine(responses);
117
+ engine.setMultipleGroupVariables(new Set(['C0_02']));
118
+ // Depuis l'instance 2, référencer explicitement le membre 1
119
+ expect(engine.evaluate("showMe(${C0_02_1} == 'oui')", 2)).toBe(true);
120
+ // Depuis l'instance 1, la valeur du membre 2 est 'non'
121
+ expect(engine.evaluate("showMe(${C0_02_2} == 'oui')", 1)).toBe(false);
122
+ });
123
+ });
124
+ describe('_INDEX_ exclu de getReferencedVariables', () => {
125
+ it('ne devrait pas inclure _INDEX_ dans les variables référencées', () => {
126
+ const engine = new ConditionEngine({});
127
+ const refs = engine.getReferencedVariables('showMe(${_INDEX_} == 1 && ${AGE} >= 18)');
128
+ expect(refs).not.toContain('_INDEX_');
129
+ expect(refs).toContain('AGE');
130
+ });
131
+ });
132
+ });
@@ -154,7 +154,7 @@ describe('GroupeInstanceManager', () => {
154
154
  it('devrait creer des instances basees sur les reponses existantes avec numeroMembre', () => {
155
155
  const groupe = createMultipleGroupe();
156
156
  const responses = {
157
- ...createControlResponse(3),
157
+ ...createControlResponse(2),
158
158
  'S1_01_1': createResponse('S1_01', 'Alice', 1),
159
159
  'S1_02_1': createResponse('S1_02', '25', 1),
160
160
  'S1_01_2': createResponse('S1_01', 'Bob', 2),
@@ -1 +1 @@
1
- {"version":3,"file":"condition-engine.d.ts","sourceRoot":"","sources":["../../src/lib/condition-engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"condition-engine.d.ts","sourceRoot":"","sources":["../../src/lib/condition-engine.ts"],"names":[],"mappings":"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"}
@@ -110,7 +110,7 @@ export class ConditionEngine {
110
110
  */
111
111
  getReferencedVariables(condition) {
112
112
  const matches = condition.match(/\$\{([A-Z_][A-Z0-9_]*(?:_\d+)?)\}/g);
113
- return matches ? matches.map(match => match.slice(2, -1)) : [];
113
+ return matches ? matches.map(match => match.slice(2, -1)).filter(v => v !== '_INDEX_') : [];
114
114
  }
115
115
  /**
116
116
  * Génère un hash du contexte pour les variables référencées dans une condition
@@ -588,7 +588,9 @@ export class ConditionEngine {
588
588
  * ORDRE CORRIGÉ: suffixer d'abord, puis traiter les fonctions de date
589
589
  */
590
590
  replaceVariableReferencesWithIterations(condition, currentIteration) {
591
- // ÉTAPE 1 (NOUVEAU): Suffixer les références ${VAR} ${VAR_index} AVANT les fonctions de date
591
+ // ÉTAPE 0: Remplacer ${_INDEX_} par le numéro d'instance courant
592
+ condition = condition.replace(/\$\{_INDEX_\}/g, String(currentIteration ?? 1));
593
+ // ÉTAPE 1: Suffixer les références ${VAR} → ${VAR_index} AVANT les fonctions de date
592
594
  if (currentIteration !== undefined) {
593
595
  condition = this.suffixVariableReferences(condition, currentIteration);
594
596
  }
@@ -632,6 +634,8 @@ export class ConditionEngine {
632
634
  suffixVariableReferences(condition, iteration) {
633
635
  // Regex pour capturer ${VAR} mais pas ${VAR_N} (déjà suffixé)
634
636
  return condition.replace(/\$\{([A-Z_][A-Z0-9_]*)(?!_\d)\}/g, (match, varCode) => {
637
+ if (varCode === '_INDEX_')
638
+ return match;
635
639
  // Si au moins une version itérative existe (ex: VAR_1), TOUJOURS suffixer
636
640
  if (this.isIterativeVariable(varCode)) {
637
641
  return `\${${varCode}_${iteration}}`;
@@ -644,6 +648,8 @@ export class ConditionEngine {
644
648
  * Vérifie si une variable est de type itératif (groupe multiple)
645
649
  */
646
650
  isIterativeVariable(varCode) {
651
+ if (varCode === '_INDEX_')
652
+ return false;
647
653
  // Utiliser la connaissance structurelle des groupes multiples
648
654
  // plutôt que de deviner depuis le contexte (fragile si _1 n'existe pas)
649
655
  if (this.multipleGroupVariables.size > 0) {
@@ -154,7 +154,7 @@ export class RosterConditionEngine {
154
154
  if (!condition)
155
155
  return [];
156
156
  const matches = condition.match(/\$\{([A-Z_][A-Z0-9_]*)\}/g) || [];
157
- return matches.map(m => m.slice(2, -1));
157
+ return matches.map(m => m.slice(2, -1)).filter(v => v !== '_INDEX_');
158
158
  }
159
159
  /**
160
160
  * Extrait la cible d'un jump s'il existe dans la condition (premier match uniquement)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.140",
3
+ "version": "1.0.141",
4
4
  "description": "Composants partagés de rendu de formulaires RSU v2 - Package local pour frontend Admin et Public",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",