@legalplace/conditions-runner 2.3.2 → 2.3.4-staging.0

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.3.3](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/conditions-runner@2.3.2...@legalplace/conditions-runner@2.3.3) (2026-09-02)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * pin lplogic 2.4.1 and add includes/does-not-include ([b35fdc5](https://git.legalplace.eu/legalplace/monorepo/commits/b35fdc54e74246c954ad6483eb5b6282c749c639))
12
+ * restore contains/does-not-contain as equality aliases of is/not ([3902bdf](https://git.legalplace.eu/legalplace/monorepo/commits/3902bdf7d8ace558215a7eb98edd2c7f5b6b5555))
13
+
14
+
15
+
16
+
17
+
6
18
  ## [2.3.2](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/conditions-runner@2.3.1...@legalplace/conditions-runner@2.3.2) (2026-09-01)
7
19
 
8
20
  **Note:** Version bump only for package @legalplace/conditions-runner
@@ -14,7 +14,7 @@ declare class ConditionsRunner {
14
14
  executeCondition(condition: {
15
15
  dataMap: ConditionDataMap;
16
16
  conditions: ConditionV3;
17
- }, id: number, index: number, conditionType: "options" | "optionValidator" | "variables" | "prefillers" | "variableValidator" | "sections" | "documents"): any;
17
+ }, id: number, index: number, conditionType: "options" | "optionValidator" | "variables" | "prefillers" | "variableValidator" | "sections" | "documents" | "multiples", documentSlug?: string): any;
18
18
  private buildComparatorResolverContext;
19
19
  }
20
20
  export default ConditionsRunner;
@@ -11,7 +11,7 @@ const ComparatorResolver_1 = require("./ComparatorResolver");
11
11
  class ConditionsRunner {
12
12
  constructor(references, ovc) {
13
13
  this.executeConditionMemo = {};
14
- this.buildMemoizeKey = (condition, id, index, conditionType) => `${JSON.stringify(condition)}-${id}-${index}-${conditionType}`;
14
+ this.buildMemoizeKey = (condition, id, index, conditionType, documentSlug) => `${JSON.stringify(condition)}-${id}-${index}-${conditionType}-${documentSlug}`;
15
15
  this.references = references;
16
16
  this.ovc = ovc;
17
17
  }
@@ -62,23 +62,26 @@ class ConditionsRunner {
62
62
  }
63
63
  return false;
64
64
  }
65
- executeCondition(condition, id, index, conditionType) {
66
- const memoizedKey = this.buildMemoizeKey(condition, id, index, conditionType);
65
+ executeCondition(condition, id, index, conditionType, documentSlug = "main") {
66
+ const memoizedKey = this.buildMemoizeKey(condition, id, index, conditionType, documentSlug);
67
67
  if (this.executeConditionMemo[memoizedKey] !== undefined) {
68
68
  return this.executeConditionMemo[memoizedKey];
69
69
  }
70
70
  let currentData;
71
71
  if (conditionType === "options" || conditionType === "optionValidator") {
72
- currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, id, index).getData();
72
+ currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, id, index, documentSlug).getData();
73
73
  }
74
74
  else if (conditionType === "variables" ||
75
75
  conditionType === "prefillers" ||
76
76
  conditionType === "variableValidator") {
77
77
  const dataId = this.references.relations.variables[id].parents[0];
78
- currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, dataId, index).getData();
78
+ currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, dataId, index, documentSlug).getData();
79
79
  }
80
80
  else if (conditionType === "sections" || conditionType === "documents") {
81
- currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, id, index).getData();
81
+ currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, id, index, documentSlug).getData();
82
+ }
83
+ else if (conditionType === "multiples") {
84
+ currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap).getData();
82
85
  }
83
86
  const resolverContext = this.buildComparatorResolverContext(id, index, conditionType);
84
87
  const deps = { references: this.references, ovc: this.ovc };
@@ -164,7 +164,7 @@ describe("ConditionsRunner class", () => {
164
164
  it("should not raise error when instanciating class", () => {
165
165
  expect(new ConditionsRunner_1.default(references_json_1.default, inputs_type_json_1.default)).toBeDefined();
166
166
  });
167
- it("slices related document conditions to the current occurrence", () => {
167
+ it("should slice the condition to the current occurrence when the scope is related on a document", () => {
168
168
  const runner = new ConditionsRunner_1.default(buildRelatedDocumentReferences(), relatedOvc);
169
169
  const condition = mappedCondition({
170
170
  selected: [{ var: "o.3" }],
@@ -173,7 +173,17 @@ describe("ConditionsRunner class", () => {
173
173
  expect(runner.executeCondition(condition, 2, 0, "documents")).toBe(true);
174
174
  expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(false);
175
175
  });
176
- it("evaluates selected + first against the first row after id/index slicing", () => {
176
+ it("should evaluate the condition unsliced when the target type is multiples", () => {
177
+ const runner = new ConditionsRunner_1.default(buildRelatedDocumentReferences(), relatedOvc);
178
+ const condition = mappedCondition({
179
+ selected: [{ var: "o.3" }],
180
+ scope: "any",
181
+ });
182
+ expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(false);
183
+ expect(runner.executeCondition(condition, 2, 1, "multiples")).toBe(true);
184
+ expect(runner.executeCondition(condition, 2, 0, "multiples")).toBe(true);
185
+ });
186
+ it("should evaluate against the first occurrence when the scope is first", () => {
177
187
  const runner = new ConditionsRunner_1.default(buildRelatedDocumentReferences(), relatedOvc);
178
188
  const condition = mappedCondition({
179
189
  selected: [{ var: "o.3" }],
@@ -181,7 +191,7 @@ describe("ConditionsRunner class", () => {
181
191
  });
182
192
  expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(true);
183
193
  });
184
- it("related sibling [var:N] uses the current occurrence index", () => {
194
+ it("should bind a sibling [var:N] to the current occurrence when the scope is related", () => {
185
195
  const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), siblingOvc);
186
196
  const condition = mappedCondition({
187
197
  "=": [{ var: "v.3" }, "[var:4]"],
@@ -191,7 +201,7 @@ describe("ConditionsRunner class", () => {
191
201
  expect(runner.executeCondition(condition, 3, 1, "variables")).toBe(false);
192
202
  expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
193
203
  });
194
- it("first-scoped sibling [var:N] uses occurrence 0", () => {
204
+ it("should bind a sibling [var:N] to occurrence 0 when the scope is first", () => {
195
205
  const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), siblingOvc);
196
206
  const condition = mappedCondition({
197
207
  "=": [{ var: "v.3" }, "[var:4]"],
@@ -199,7 +209,7 @@ describe("ConditionsRunner class", () => {
199
209
  });
200
210
  expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
201
211
  });
202
- it("all-scope sibling [var:N] is pairwise true when every row matches", () => {
212
+ it("should return true when the scope is all and every pairwise row matches", () => {
203
213
  const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), {
204
214
  options: { 10: [true, true], 20: [true] },
205
215
  variables: {
@@ -214,7 +224,7 @@ describe("ConditionsRunner class", () => {
214
224
  });
215
225
  expect(runner.executeCondition(condition, 3, 0, "options")).toBe(true);
216
226
  });
217
- it("all-scope sibling [var:N] is pairwise false when a row fails", () => {
227
+ it("should return false when the scope is all and a pairwise row fails", () => {
218
228
  const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), {
219
229
  options: { 10: [true, true], 20: [true] },
220
230
  variables: {
@@ -229,7 +239,7 @@ describe("ConditionsRunner class", () => {
229
239
  });
230
240
  expect(runner.executeCondition(condition, 3, 0, "options")).toBe(false);
231
241
  });
232
- it("any-scope sibling [var:N] succeeds on a later pairwise row", () => {
242
+ it("should return true when the scope is any and a later pairwise row matches", () => {
233
243
  const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), {
234
244
  options: { 10: [true, true], 20: [true] },
235
245
  variables: {
@@ -244,7 +254,7 @@ describe("ConditionsRunner class", () => {
244
254
  });
245
255
  expect(runner.executeCondition(condition, 5, 0, "options")).toBe(true);
246
256
  });
247
- it("joins non-sibling [var:N] values instead of slicing by row", () => {
257
+ it("should join every value when the [var:N] reference is not a sibling of the multiple", () => {
248
258
  const references = buildSiblingVariableReferences();
249
259
  const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
250
260
  "=": [{ var: "v.3" }, "[var:5]"],
@@ -258,7 +268,7 @@ describe("ConditionsRunner class", () => {
258
268
  describe("resolveConditionComparators", () => {
259
269
  const references = buildSiblingVariableReferences();
260
270
  const deps = { references, ovc: siblingOvc };
261
- it("related leaf: sibling [var:N] uses the current occurrence index", () => {
271
+ it("should resolve a sibling [var:N] to the current occurrence when the leaf scope is related", () => {
262
272
  const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
263
273
  "=": [{ var: "v.3" }, "[var:4]"],
264
274
  scope: "related",
@@ -267,7 +277,7 @@ describe("resolveConditionComparators", () => {
267
277
  "=": [{ var: "v.3" }, "c"],
268
278
  });
269
279
  });
270
- it("first-scoped leaf: sibling [var:N] uses occurrence 0", () => {
280
+ it("should resolve a sibling [var:N] to occurrence 0 when the leaf scope is first", () => {
271
281
  const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
272
282
  "=": [{ var: "v.3" }, "[var:4]"],
273
283
  scope: "first",
@@ -276,7 +286,7 @@ describe("resolveConditionComparators", () => {
276
286
  "=-first": [{ var: "v.3" }, "same"],
277
287
  });
278
288
  });
279
- it("uses empty sentinel for missing sibling values", () => {
289
+ it("should resolve to the empty sentinel when the sibling value is missing", () => {
280
290
  const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
281
291
  "=": [{ var: "v.3" }, "[var:4]"],
282
292
  scope: "related",
@@ -292,3 +302,21 @@ describe("resolveConditionComparators", () => {
292
302
  });
293
303
  });
294
304
  });
305
+ describe("includes / does-not-include operators", () => {
306
+ it("should lower the operator to includes-any when the scope is any", () => {
307
+ expect((0, referencesparser_1.lowerScope)({
308
+ includes: [{ var: "v.1" }, "SARL"],
309
+ scope: "any",
310
+ })).toEqual({
311
+ "includes-any": [{ var: "v.1" }, "SARL"],
312
+ });
313
+ });
314
+ it("should lower the operator to does-not-include-first when the scope is first", () => {
315
+ expect((0, referencesparser_1.lowerScope)({
316
+ "does-not-include": [{ var: "v.1" }, "SARL"],
317
+ scope: "first",
318
+ })).toEqual({
319
+ "does-not-include-first": [{ var: "v.1" }, "SARL"],
320
+ });
321
+ });
322
+ });
@@ -9,8 +9,9 @@ declare class DataPopulator {
9
9
  private id?;
10
10
  private index?;
11
11
  private dataMap;
12
+ private documentSlug;
12
13
  private data;
13
- constructor(conditionsRunner: ConditionsRunner, references: Types.ReferencesType, ovc: InputsType, dataMap: ConditionDataMap, id?: number, index?: number);
14
+ constructor(conditionsRunner: ConditionsRunner, references: Types.ReferencesType, ovc: InputsType, dataMap: ConditionDataMap, id?: number, index?: number, documentSlug?: string);
14
15
  getData(): Readonly<ConditionData>;
15
16
  private populateConditions;
16
17
  private getOptionConditions;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class DataPopulator {
4
- constructor(conditionsRunner, references, ovc, dataMap, id, index) {
4
+ constructor(conditionsRunner, references, ovc, dataMap, id, index, documentSlug = "main") {
5
5
  this.data = {
6
6
  o: {},
7
7
  v: {},
@@ -11,6 +11,7 @@ class DataPopulator {
11
11
  v: {},
12
12
  },
13
13
  };
14
+ this.documentSlug = documentSlug;
14
15
  this.id = id;
15
16
  this.ovc = ovc;
16
17
  this.index = index;
@@ -156,10 +157,11 @@ class DataPopulator {
156
157
  return result;
157
158
  }
158
159
  sectionConditionValue(sectionId) {
159
- const conditions = this.references.conditions.sections.main[sectionId];
160
+ var _a;
161
+ const conditions = (_a = this.references.conditions.sections[this.documentSlug]) === null || _a === void 0 ? void 0 : _a[sectionId];
160
162
  if (conditions === undefined)
161
163
  return undefined;
162
- return this.conditionsRunner.executeCondition(conditions, 0, 0, "sections");
164
+ return this.conditionsRunner.executeCondition(conditions, 0, 0, "sections", this.documentSlug);
163
165
  }
164
166
  }
165
167
  exports.default = DataPopulator;
@@ -1,3 +1,3 @@
1
1
  import type { ConditionData } from "./types/conditions.d";
2
- import { type ComparatorResolverContext, type ComparatorResolverDeps } from "./ComparatorResolver";
2
+ import type { ComparatorResolverContext, ComparatorResolverDeps } from "./ComparatorResolver";
3
3
  export declare const aggregateScopedLeaves: (tree: unknown, unslicedData: ConditionData, context: ComparatorResolverContext, deps: ComparatorResolverDeps) => unknown;
package/jest.config.js ADDED
@@ -0,0 +1,32 @@
1
+ /*
2
+ * For a detailed explanation regarding each configuration property and type check, visit:
3
+ * https://jestjs.io/docs/configuration
4
+ *
5
+ * Kept as .js so Jest does not need to parse a TypeScript config (avoids the
6
+ * ts.resolveTypeReferenceDirective crash with mismatched TS/Jest versions).
7
+ */
8
+
9
+ module.exports = {
10
+ clearMocks: true,
11
+ collectCoverage: false,
12
+ collectCoverageFrom: [
13
+ "src/**/*.ts",
14
+ "src/**/*.tsx",
15
+ "!**/node_modules/**",
16
+ "!src/**/*.d.ts",
17
+ ],
18
+ coverageDirectory: "./coverage",
19
+ coverageProvider: "babel",
20
+ coverageReporters: ["html", "text", "text-summary", "cobertura"],
21
+ moduleNameMapper: {
22
+ "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
23
+ "<rootDir>__mocks__/fileMock.js",
24
+ "\\.(css|less|sass|scss)$": "identity-obj-proxy",
25
+ },
26
+ roots: ["src/"],
27
+ setupFiles: ["./setupJest.ts"],
28
+ testEnvironment: "jsdom",
29
+ transform: {
30
+ "^.+\\.tsx?$": "@swc-node/jest",
31
+ },
32
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legalplace/conditions-runner",
3
- "version": "2.3.2",
3
+ "version": "2.3.4-staging.0",
4
4
  "description": "Package containing method to execute conditions",
5
5
  "license": "MIT",
6
6
  "main": "dist/ConditionsRunner.js",
@@ -14,21 +14,20 @@
14
14
  "start": "npx tsc-watch --noClear -p ./tsconfig.json",
15
15
  "lint": "tsc --noEmit && eslint 'src/**/*.{js,jsx,ts,tsx}' --quiet",
16
16
  "lint:fix": "tsc --noEmit && eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
17
- "test": "npx jest",
18
- "test:ci": "npx jest --config ./jest.config.ts --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false",
19
- "test:coverage": "npx jest test --coverage --watchAll=false ",
17
+ "test": "npx jest --config ./jest.config.js",
18
+ "test:ci": "npx jest --config ./jest.config.js --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false",
19
+ "test:coverage": "npx jest --config ./jest.config.js --coverage --watchAll=false",
20
20
  "precommit": "test && lint:fix"
21
21
  },
22
22
  "prettier": "@legalplace/prettier-config",
23
23
  "dependencies": {
24
- "@legalplace/lplogic": "2.3.5",
25
- "@legalplace/models-v3-types": "^5.18.0",
26
- "@legalplace/referencesparser": "^3.3.2"
24
+ "@legalplace/lplogic": "2.4.1",
25
+ "@legalplace/models-v3-types": "5.18.3-staging.0",
26
+ "@legalplace/referencesparser": "3.3.5-staging.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@legalplace/prettier-config": "^2.1.3",
30
- "@legalplace/types": "^2.22.13",
29
+ "@legalplace/prettier-config": "2.1.4-staging.0",
30
+ "@legalplace/types": "2.22.15-staging.0",
31
31
  "ts-node": "^10.8.1"
32
- },
33
- "gitHead": "9915cad70819d4cf395ccdf7e006482a02b195fa"
32
+ }
34
33
  }
@@ -191,7 +191,7 @@ describe("ConditionsRunner class", () => {
191
191
  ).toBeDefined();
192
192
  });
193
193
 
194
- it("slices related document conditions to the current occurrence", () => {
194
+ it("should slice the condition to the current occurrence when the scope is related on a document", () => {
195
195
  const runner = new ConditionsRunner(
196
196
  buildRelatedDocumentReferences(),
197
197
  relatedOvc
@@ -205,7 +205,22 @@ describe("ConditionsRunner class", () => {
205
205
  expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(false);
206
206
  });
207
207
 
208
- it("evaluates selected + first against the first row after id/index slicing", () => {
208
+ it("should evaluate the condition unsliced when the target type is multiples", () => {
209
+ const runner = new ConditionsRunner(
210
+ buildRelatedDocumentReferences(),
211
+ relatedOvc
212
+ );
213
+ const condition = mappedCondition({
214
+ selected: [{ var: "o.3" }],
215
+ scope: "any",
216
+ });
217
+
218
+ expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(false);
219
+ expect(runner.executeCondition(condition, 2, 1, "multiples")).toBe(true);
220
+ expect(runner.executeCondition(condition, 2, 0, "multiples")).toBe(true);
221
+ });
222
+
223
+ it("should evaluate against the first occurrence when the scope is first", () => {
209
224
  const runner = new ConditionsRunner(
210
225
  buildRelatedDocumentReferences(),
211
226
  relatedOvc
@@ -218,7 +233,7 @@ describe("ConditionsRunner class", () => {
218
233
  expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(true);
219
234
  });
220
235
 
221
- it("related sibling [var:N] uses the current occurrence index", () => {
236
+ it("should bind a sibling [var:N] to the current occurrence when the scope is related", () => {
222
237
  const runner = new ConditionsRunner(
223
238
  buildSiblingVariableReferences(),
224
239
  siblingOvc
@@ -236,7 +251,7 @@ describe("ConditionsRunner class", () => {
236
251
  expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
237
252
  });
238
253
 
239
- it("first-scoped sibling [var:N] uses occurrence 0", () => {
254
+ it("should bind a sibling [var:N] to occurrence 0 when the scope is first", () => {
240
255
  const runner = new ConditionsRunner(
241
256
  buildSiblingVariableReferences(),
242
257
  siblingOvc
@@ -250,7 +265,7 @@ describe("ConditionsRunner class", () => {
250
265
  expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
251
266
  });
252
267
 
253
- it("all-scope sibling [var:N] is pairwise true when every row matches", () => {
268
+ it("should return true when the scope is all and every pairwise row matches", () => {
254
269
  const runner = new ConditionsRunner(buildSiblingVariableReferences(), {
255
270
  options: { 10: [true, true], 20: [true] },
256
271
  variables: {
@@ -267,7 +282,7 @@ describe("ConditionsRunner class", () => {
267
282
  expect(runner.executeCondition(condition, 3, 0, "options")).toBe(true);
268
283
  });
269
284
 
270
- it("all-scope sibling [var:N] is pairwise false when a row fails", () => {
285
+ it("should return false when the scope is all and a pairwise row fails", () => {
271
286
  const runner = new ConditionsRunner(buildSiblingVariableReferences(), {
272
287
  options: { 10: [true, true], 20: [true] },
273
288
  variables: {
@@ -284,7 +299,7 @@ describe("ConditionsRunner class", () => {
284
299
  expect(runner.executeCondition(condition, 3, 0, "options")).toBe(false);
285
300
  });
286
301
 
287
- it("any-scope sibling [var:N] succeeds on a later pairwise row", () => {
302
+ it("should return true when the scope is any and a later pairwise row matches", () => {
288
303
  const runner = new ConditionsRunner(buildSiblingVariableReferences(), {
289
304
  options: { 10: [true, true], 20: [true] },
290
305
  variables: {
@@ -301,7 +316,7 @@ describe("ConditionsRunner class", () => {
301
316
  expect(runner.executeCondition(condition, 5, 0, "options")).toBe(true);
302
317
  });
303
318
 
304
- it("joins non-sibling [var:N] values instead of slicing by row", () => {
319
+ it("should join every value when the [var:N] reference is not a sibling of the multiple", () => {
305
320
  const references = buildSiblingVariableReferences();
306
321
  const resolved = resolveConditionComparators(
307
322
  lowerScope({
@@ -322,7 +337,7 @@ describe("resolveConditionComparators", () => {
322
337
  const references = buildSiblingVariableReferences();
323
338
  const deps = { references, ovc: siblingOvc };
324
339
 
325
- it("related leaf: sibling [var:N] uses the current occurrence index", () => {
340
+ it("should resolve a sibling [var:N] to the current occurrence when the leaf scope is related", () => {
326
341
  const resolved = resolveConditionComparators(
327
342
  lowerScope({
328
343
  "=": [{ var: "v.3" }, "[var:4]"],
@@ -337,7 +352,7 @@ describe("resolveConditionComparators", () => {
337
352
  });
338
353
  });
339
354
 
340
- it("first-scoped leaf: sibling [var:N] uses occurrence 0", () => {
355
+ it("should resolve a sibling [var:N] to occurrence 0 when the leaf scope is first", () => {
341
356
  const resolved = resolveConditionComparators(
342
357
  lowerScope({
343
358
  "=": [{ var: "v.3" }, "[var:4]"],
@@ -352,7 +367,7 @@ describe("resolveConditionComparators", () => {
352
367
  });
353
368
  });
354
369
 
355
- it("uses empty sentinel for missing sibling values", () => {
370
+ it("should resolve to the empty sentinel when the sibling value is missing", () => {
356
371
  const resolved = resolveConditionComparators(
357
372
  lowerScope({
358
373
  "=": [{ var: "v.3" }, "[var:4]"],
@@ -373,3 +388,27 @@ describe("resolveConditionComparators", () => {
373
388
  });
374
389
  });
375
390
  });
391
+
392
+ describe("includes / does-not-include operators", () => {
393
+ it("should lower the operator to includes-any when the scope is any", () => {
394
+ expect(
395
+ lowerScope({
396
+ includes: [{ var: "v.1" }, "SARL"],
397
+ scope: "any",
398
+ } as ConditionV3)
399
+ ).toEqual({
400
+ "includes-any": [{ var: "v.1" }, "SARL"],
401
+ });
402
+ });
403
+
404
+ it("should lower the operator to does-not-include-first when the scope is first", () => {
405
+ expect(
406
+ lowerScope({
407
+ "does-not-include": [{ var: "v.1" }, "SARL"],
408
+ scope: "first",
409
+ } as ConditionV3)
410
+ ).toEqual({
411
+ "does-not-include-first": [{ var: "v.1" }, "SARL"],
412
+ });
413
+ });
414
+ });
@@ -10,8 +10,8 @@ import { aggregateScopedLeaves } from "./ScopedLeafEvaluator";
10
10
  import {
11
11
  getConditionVariableOptionId,
12
12
  resolveConditionComparators,
13
- type ComparatorResolverContext,
14
13
  } from "./ComparatorResolver";
14
+ import type { ComparatorResolverContext } from "./ComparatorResolver";
15
15
 
16
16
  class ConditionsRunner {
17
17
  private references: Types.ReferencesType;
@@ -114,7 +114,12 @@ class ConditionsRunner {
114
114
  | "variableValidator"
115
115
  | "sections"
116
116
  | "documents"
117
- ) => `${JSON.stringify(condition)}-${id}-${index}-${conditionType}`;
117
+ | "multiples",
118
+ documentSlug: string
119
+ ) =>
120
+ `${JSON.stringify(
121
+ condition
122
+ )}-${id}-${index}-${conditionType}-${documentSlug}`;
118
123
 
119
124
  executeCondition(
120
125
  condition: {
@@ -131,12 +136,15 @@ class ConditionsRunner {
131
136
  | "variableValidator"
132
137
  | "sections"
133
138
  | "documents"
139
+ | "multiples",
140
+ documentSlug = "main"
134
141
  ): any {
135
142
  const memoizedKey = this.buildMemoizeKey(
136
143
  condition,
137
144
  id,
138
145
  index,
139
- conditionType
146
+ conditionType,
147
+ documentSlug
140
148
  );
141
149
  if (this.executeConditionMemo[memoizedKey] !== undefined) {
142
150
  return this.executeConditionMemo[memoizedKey];
@@ -151,7 +159,8 @@ class ConditionsRunner {
151
159
  this.ovc,
152
160
  condition.dataMap,
153
161
  id,
154
- index
162
+ index,
163
+ documentSlug
155
164
  ).getData();
156
165
  } else if (
157
166
  conditionType === "variables" ||
@@ -165,7 +174,8 @@ class ConditionsRunner {
165
174
  this.ovc,
166
175
  condition.dataMap,
167
176
  dataId,
168
- index
177
+ index,
178
+ documentSlug
169
179
  ).getData();
170
180
  } else if (conditionType === "sections" || conditionType === "documents") {
171
181
  currentData = new DataPopulator(
@@ -174,7 +184,16 @@ class ConditionsRunner {
174
184
  this.ovc,
175
185
  condition.dataMap,
176
186
  id,
177
- index
187
+ index,
188
+ documentSlug
189
+ ).getData();
190
+ } else if (conditionType === "multiples") {
191
+ // Multiple CTA conditions are evaluated once for the whole group
192
+ currentData = new DataPopulator(
193
+ this,
194
+ this.references,
195
+ this.ovc,
196
+ condition.dataMap
178
197
  ).getData();
179
198
  }
180
199
 
@@ -16,6 +16,8 @@ class DataPopulator {
16
16
 
17
17
  private dataMap: ConditionDataMap;
18
18
 
19
+ private documentSlug: string;
20
+
19
21
  private data: ConditionData = {
20
22
  o: {},
21
23
  v: {},
@@ -32,8 +34,10 @@ class DataPopulator {
32
34
  ovc: InputsType,
33
35
  dataMap: ConditionDataMap,
34
36
  id?: number,
35
- index?: number
37
+ index?: number,
38
+ documentSlug = "main"
36
39
  ) {
40
+ this.documentSlug = documentSlug;
37
41
  this.id = id;
38
42
  this.ovc = ovc;
39
43
  this.index = index;
@@ -306,10 +310,17 @@ class DataPopulator {
306
310
  */
307
311
  private sectionConditionValue(sectionId: number) {
308
312
  // Getting section conditions
309
- const conditions = this.references.conditions.sections.main[sectionId];
313
+ const conditions =
314
+ this.references.conditions.sections[this.documentSlug]?.[sectionId];
310
315
  if (conditions === undefined) return undefined;
311
316
 
312
- return this.conditionsRunner.executeCondition(conditions, 0, 0, "sections");
317
+ return this.conditionsRunner.executeCondition(
318
+ conditions,
319
+ 0,
320
+ 0,
321
+ "sections",
322
+ this.documentSlug
323
+ );
313
324
  }
314
325
  }
315
326
 
@@ -7,8 +7,10 @@ import {
7
7
  getConditionVariableOptionId,
8
8
  resolveComparatorString,
9
9
  SCOPED_OPERATOR_REGEX,
10
- type ComparatorResolverContext,
11
- type ComparatorResolverDeps,
10
+ } from "./ComparatorResolver";
11
+ import type {
12
+ ComparatorResolverContext,
13
+ ComparatorResolverDeps,
12
14
  } from "./ComparatorResolver";
13
15
 
14
16
  const UI_METADATA_KEYS = new Set(["scope", "groupMeta"]);
package/jest.config.ts DELETED
@@ -1,205 +0,0 @@
1
- /*
2
- * For a detailed explanation regarding each configuration property and type check, visit:
3
- * https://jestjs.io/docs/configuration
4
- */
5
-
6
- const jestConfig = {
7
- // All imported modules in your tests should be mocked automatically
8
- // automock: false,
9
-
10
- // Stop running tests after `n` failures
11
- // bail: 0,
12
-
13
- // The directory where Jest should store its cached dependency information
14
- // cacheDirectory: "/private/var/folders/cc/yjms5nz10198m80g1mz2zy980000gn/T/jest_dx",
15
-
16
- // Automatically clear mock calls and instances between every test
17
- clearMocks: true,
18
-
19
- // Indicates whether the coverage information should be collected while executing the test
20
- collectCoverage: false,
21
-
22
- // An array of glob patterns indicating a set of files for which coverage information should be collected
23
- collectCoverageFrom: [
24
- "src/**/*.ts",
25
- "src/**/*.tsx",
26
- "!**/node_modules/**",
27
- "!src/**/*.d.ts",
28
- ],
29
-
30
- // The directory where Jest should output its coverage files
31
- coverageDirectory: "./coverage",
32
-
33
- // An array of regexp pattern strings used to skip coverage collection
34
- // coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
35
-
36
- // Indicates which provider should be used to instrument code for coverage
37
- coverageProvider: "babel",
38
-
39
- // A list of reporter names that Jest uses when writing coverage reports
40
- coverageReporters: ["html", "text", "text-summary", "cobertura"],
41
-
42
- // An object that configures minimum threshold enforcement for coverage results
43
- // coverageThreshold: {
44
- // global: {
45
- // branches: 77,
46
- // functions: 100,
47
- // lines: 84,
48
- // statements: 84,
49
- // },
50
- // },
51
-
52
- // A path to a custom dependency extractor
53
- // dependencyExtractor: undefined,
54
-
55
- // Make calling deprecated APIs throw helpful error messages
56
- // errorOnDeprecated: false,
57
-
58
- // Force coverage collection from ignored files using an array of glob patterns
59
- // forceCoverageMatch: [],
60
-
61
- // A path to a module which exports an async function that is triggered once before all test suites
62
- // globalSetup: undefined,
63
-
64
- // A path to a module which exports an async function that is triggered once after all test suites
65
- // globalTeardown: undefined,
66
-
67
- // A set of global variables that need to be available in all test environments
68
- // globals: {},
69
-
70
- // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
71
- // maxWorkers: "50%",
72
-
73
- // An array of directory names to be searched recursively up from the requiring module's location
74
- // moduleDirectories: [
75
- // "node_modules"
76
- // ],
77
-
78
- // An array of file extensions your modules use
79
- // moduleFileExtensions: [
80
- // "js",
81
- // "jsx",
82
- // "ts",
83
- // "tsx",
84
- // "json",
85
- // "node"
86
- // ],
87
-
88
- // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
89
- moduleNameMapper: {
90
- "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
91
- "<rootDir>__mocks__/fileMock.js",
92
- "\\.(css|less|sass|scss)$": "identity-obj-proxy",
93
- },
94
-
95
- // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
96
- // modulePathIgnorePatterns: [],
97
-
98
- // Activates notifications for test results
99
- // notify: false,
100
-
101
- // An enum that specifies notification mode. Requires { notify: true }
102
- // notifyMode: "failure-change",
103
-
104
- // A preset that is used as a base for Jest's configuration
105
- // preset: undefined,
106
-
107
- // Run tests from one or more projects
108
- // projects: undefined,
109
-
110
- // Use this configuration option to add custom reporters to Jest
111
- // reporters: undefined,
112
-
113
- // Automatically reset mock state between every test
114
- // resetMocks: false,
115
-
116
- // Reset the module registry before running each individual test
117
- // resetModules: false,
118
-
119
- // A path to a custom resolver
120
- // resolver: undefined,
121
-
122
- // Automatically restore mock state between every test
123
- // restoreMocks: false,
124
-
125
- // The root directory that Jest should scan for tests and modules within
126
- // rootDir: undefined,
127
-
128
- // A list of paths to directories that Jest should use to search for files in
129
- roots: ["src/"],
130
-
131
- // Allows you to use a custom runner instead of Jest's default test runner
132
- // runner: "jest-runner",
133
-
134
- // The paths to modules that run some code to configure or set up the testing environment before each test
135
- setupFiles: ["./setupJest.ts"],
136
-
137
- // A list of paths to modules that run some code to configure or set up the testing framework before each test
138
- // setupFilesAfterEnv: [],
139
-
140
- // The number of seconds after which a test is considered as slow and reported as such in the results.
141
- // slowTestThreshold: 5,
142
-
143
- // A list of paths to snapshot serializer modules Jest should use for snapshot testing
144
- // snapshotSerializers: [],
145
-
146
- // The test environment that will be used for testing
147
- testEnvironment: "jsdom",
148
-
149
- // Options that will be passed to the testEnvironment
150
- // testEnvironmentOptions: {},
151
-
152
- // Adds a location field to test results
153
- // testLocationInResults: false,
154
-
155
- // The glob patterns Jest uses to detect test files
156
- // testMatch: [
157
- // "**/__tests__/**/*.[jt]s?(x)",
158
- // "**/?(*.)+(spec|test).[tj]s?(x)"
159
- // ],
160
-
161
- // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
162
- // testPathIgnorePatterns: [
163
- // "/node_modules/"
164
- // ],
165
-
166
- // The regexp pattern or array of patterns that Jest uses to detect test files
167
- // testRegex: [],
168
-
169
- // This option allows the use of a custom results processor
170
- // testResultsProcessor: undefined,
171
-
172
- // This option allows use of a custom test runner
173
- // testRunner: "jest-circus/runner",
174
-
175
- // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
176
- // testURL: "http://localhost",
177
-
178
- // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
179
- // timers: "real",
180
-
181
- // A map from regular expressions to paths to transformers
182
- transform: {
183
- "^.+\\.tsx?$": "@swc-node/jest",
184
- },
185
-
186
- // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
187
- // transformIgnorePatterns: [
188
- // "/node_modules/",
189
- // "\\.pnp\\.[^\\/]+$"
190
- // ],
191
-
192
- // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
193
- // unmockedModulePathPatterns: undefined,
194
-
195
- // Indicates whether each individual test should be reported during the run
196
- // verbose: undefined,
197
-
198
- // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
199
- // watchPathIgnorePatterns: [],
200
-
201
- // Whether to use watchman for file crawling
202
- // watchman: true,
203
- };
204
-
205
- export default jestConfig;