@usertour/helpers 0.0.15 → 0.0.18

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.
Files changed (54) hide show
  1. package/dist/__tests__/attribute.test.cjs +1036 -0
  2. package/dist/__tests__/attribute.test.d.cts +2 -0
  3. package/dist/__tests__/attribute.test.d.ts +2 -0
  4. package/dist/__tests__/attribute.test.js +802 -0
  5. package/dist/__tests__/condition.test.cjs +257 -0
  6. package/dist/__tests__/condition.test.d.cts +2 -0
  7. package/dist/__tests__/condition.test.d.ts +2 -0
  8. package/dist/__tests__/condition.test.js +204 -0
  9. package/dist/__tests__/time.test.cjs +73 -0
  10. package/dist/__tests__/time.test.d.cts +2 -0
  11. package/dist/__tests__/time.test.d.ts +2 -0
  12. package/dist/__tests__/time.test.js +48 -0
  13. package/dist/__tests__/url.test.cjs +259 -0
  14. package/dist/__tests__/url.test.d.cts +2 -0
  15. package/dist/__tests__/url.test.d.ts +2 -0
  16. package/dist/__tests__/url.test.js +197 -0
  17. package/dist/{chunk-B4DTY6GN.js → chunk-4LLDSAHJ.js} +0 -12
  18. package/dist/chunk-7ODE2AIC.js +0 -0
  19. package/dist/{chunk-IZFZYGPU.js → chunk-BC7KXBMF.js} +5 -56
  20. package/dist/chunk-CEK3SCQO.js +31 -0
  21. package/dist/chunk-PBZSPV5R.js +239 -0
  22. package/dist/chunk-YOFQHQ7D.js +92 -0
  23. package/dist/conditions/attribute.cjs +264 -0
  24. package/dist/conditions/attribute.d.cts +28 -0
  25. package/dist/conditions/attribute.d.ts +28 -0
  26. package/dist/conditions/attribute.js +9 -0
  27. package/dist/conditions/condition.cjs +129 -0
  28. package/dist/conditions/condition.d.cts +14 -0
  29. package/dist/conditions/condition.d.ts +14 -0
  30. package/dist/conditions/condition.js +13 -0
  31. package/dist/conditions/index.cjs +379 -0
  32. package/dist/conditions/index.d.cts +6 -0
  33. package/dist/conditions/index.d.ts +6 -0
  34. package/dist/conditions/index.js +24 -0
  35. package/dist/conditions/time.cjs +55 -0
  36. package/dist/conditions/time.d.cts +10 -0
  37. package/dist/conditions/time.d.ts +10 -0
  38. package/dist/conditions/time.js +7 -0
  39. package/dist/{conditions.cjs → conditions/url.cjs} +9 -71
  40. package/dist/conditions/url.d.cts +6 -0
  41. package/dist/conditions/url.d.ts +6 -0
  42. package/dist/conditions/url.js +9 -0
  43. package/dist/content.cjs +0 -13
  44. package/dist/content.d.cts +1 -2
  45. package/dist/content.d.ts +1 -2
  46. package/dist/content.js +1 -3
  47. package/dist/index.cjs +261 -54
  48. package/dist/index.d.cts +5 -2
  49. package/dist/index.d.ts +5 -2
  50. package/dist/index.js +19 -8
  51. package/package.json +15 -9
  52. package/dist/conditions.d.cts +0 -7
  53. package/dist/conditions.d.ts +0 -7
  54. package/dist/conditions.js +0 -11
@@ -0,0 +1,1036 @@
1
+ "use strict";
2
+
3
+ // src/__tests__/attribute.test.ts
4
+ var import_types2 = require("@usertour/types");
5
+
6
+ // src/conditions/attribute.ts
7
+ var import_types = require("@usertour/types");
8
+ var import_date_fns = require("date-fns");
9
+ function evaluateFilterConditions(conditions, attributes, userAttributes) {
10
+ if (!conditions || !conditions.length) {
11
+ return true;
12
+ }
13
+ const result = evaluateAttributeConditionsGroup(conditions, attributes, userAttributes);
14
+ return evaluateFilterResult(result);
15
+ }
16
+ function evaluateAttributeConditionsGroup(conditions, attributes, userAttributes) {
17
+ if (!conditions || !conditions.length) {
18
+ return false;
19
+ }
20
+ const AND = [];
21
+ const OR = [];
22
+ for (const condition of conditions) {
23
+ const { operators } = condition;
24
+ const item = condition.type !== "group" ? evaluateAttributeCondition(condition, attributes, userAttributes) : evaluateAttributeConditionsGroup(condition.conditions || [], attributes, userAttributes);
25
+ if (!item) {
26
+ continue;
27
+ }
28
+ if (operators === "and") {
29
+ AND.push(item);
30
+ } else {
31
+ OR.push(item);
32
+ }
33
+ }
34
+ const filter = {};
35
+ if (AND.length > 0) {
36
+ filter.AND = AND;
37
+ }
38
+ if (OR.length > 0) {
39
+ filter.OR = OR;
40
+ }
41
+ if (AND.length === 0 && OR.length === 0) {
42
+ return false;
43
+ }
44
+ return filter;
45
+ }
46
+ function evaluateAttributeCondition(condition, attributes, userAttributes) {
47
+ const { data } = condition;
48
+ if (!data) {
49
+ return false;
50
+ }
51
+ const { logic, value, attrId, value2, listValues = [] } = data;
52
+ if (!attrId) {
53
+ return false;
54
+ }
55
+ const attr = attributes.find((attr2) => attr2.id === attrId);
56
+ if (!attr) {
57
+ return false;
58
+ }
59
+ const actualValue = getAttributeValue(attr.codeName, userAttributes);
60
+ if (attr.dataType === import_types.BizAttributeTypes.String) {
61
+ return evaluateStringCondition(logic, actualValue, value);
62
+ }
63
+ if (attr.dataType === import_types.BizAttributeTypes.Number) {
64
+ return evaluateNumberCondition(logic, actualValue, value, value2);
65
+ }
66
+ if (attr.dataType === import_types.BizAttributeTypes.Boolean) {
67
+ return evaluateBooleanCondition(logic, actualValue);
68
+ }
69
+ if (attr.dataType === import_types.BizAttributeTypes.List) {
70
+ return evaluateListCondition(logic, actualValue, listValues);
71
+ }
72
+ if (attr.dataType === import_types.BizAttributeTypes.DateTime) {
73
+ return evaluateDateTimeCondition(logic, actualValue, value);
74
+ }
75
+ return false;
76
+ }
77
+ function getAttributeValue(codeName, userAttributes) {
78
+ return userAttributes == null ? void 0 : userAttributes[codeName];
79
+ }
80
+ function evaluateStringCondition(logic, actualValue, expectedValue) {
81
+ const stringValue = actualValue === null || actualValue === void 0 ? "" : String(actualValue);
82
+ switch (logic) {
83
+ case "is":
84
+ return stringValue === expectedValue;
85
+ case "not":
86
+ return stringValue !== expectedValue;
87
+ case "contains":
88
+ return stringValue.includes(expectedValue);
89
+ case "notContain":
90
+ return !stringValue.includes(expectedValue);
91
+ case "startsWith":
92
+ return stringValue.startsWith(expectedValue);
93
+ case "endsWith":
94
+ return stringValue.endsWith(expectedValue);
95
+ case "empty": {
96
+ const isEmpty = !stringValue || stringValue === "";
97
+ return isEmpty;
98
+ }
99
+ case "any":
100
+ return Boolean(stringValue && stringValue !== "");
101
+ default:
102
+ return false;
103
+ }
104
+ }
105
+ function evaluateNumberCondition(logic, actualValue, expectedValue, expectedValue2) {
106
+ const numValue = Number(actualValue);
107
+ const numValue2 = Number(expectedValue2);
108
+ if (Number.isNaN(numValue)) {
109
+ return false;
110
+ }
111
+ switch (logic) {
112
+ case "is":
113
+ return numValue === expectedValue;
114
+ case "not":
115
+ return numValue !== expectedValue;
116
+ case "isLessThan":
117
+ return numValue < expectedValue;
118
+ case "isLessThanOrEqualTo":
119
+ return numValue <= expectedValue;
120
+ case "isGreaterThan":
121
+ return numValue > expectedValue;
122
+ case "isGreaterThanOrEqualTo":
123
+ return numValue >= expectedValue;
124
+ case "between":
125
+ return numValue >= expectedValue && numValue <= numValue2;
126
+ case "empty":
127
+ return actualValue === null || actualValue === void 0 || actualValue === "";
128
+ case "any":
129
+ return actualValue !== null && actualValue !== void 0 && actualValue !== "";
130
+ default:
131
+ return false;
132
+ }
133
+ }
134
+ function evaluateBooleanCondition(logic, actualValue) {
135
+ switch (logic) {
136
+ case "true":
137
+ return actualValue === true;
138
+ case "false":
139
+ return actualValue === false;
140
+ case "empty":
141
+ return actualValue === null || actualValue === void 0 || actualValue === "";
142
+ case "any":
143
+ return actualValue !== null && actualValue !== void 0 && actualValue !== "";
144
+ default:
145
+ return false;
146
+ }
147
+ }
148
+ function evaluateListCondition(logic, actualValue, expectedValues) {
149
+ const arrayValue = Array.isArray(actualValue) ? actualValue : [];
150
+ if (logic === "empty" || logic === "any") {
151
+ switch (logic) {
152
+ case "empty":
153
+ return !arrayValue || arrayValue.length === 0;
154
+ case "any":
155
+ return arrayValue && arrayValue.length > 0;
156
+ default:
157
+ return false;
158
+ }
159
+ }
160
+ const filteredValues = expectedValues.filter(
161
+ (value) => value !== null && value !== void 0 && value !== ""
162
+ );
163
+ if (!filteredValues.length) {
164
+ return false;
165
+ }
166
+ switch (logic) {
167
+ case "includesAtLeastOne":
168
+ return filteredValues.some((value) => arrayValue.includes(value));
169
+ case "includesAll":
170
+ return filteredValues.every((value) => arrayValue.includes(value));
171
+ case "notIncludesAtLeastOne":
172
+ return !filteredValues.some((value) => arrayValue.includes(value));
173
+ case "notIncludesAll":
174
+ return !filteredValues.every((value) => arrayValue.includes(value));
175
+ default:
176
+ return false;
177
+ }
178
+ }
179
+ function evaluateDateTimeCondition(logic, actualValue, expectedValue) {
180
+ const actualDate = actualValue ? new Date(actualValue) : null;
181
+ const now = /* @__PURE__ */ new Date();
182
+ if (!actualDate || Number.isNaN(actualDate.getTime())) {
183
+ return false;
184
+ }
185
+ switch (logic) {
186
+ case "lessThan": {
187
+ const targetDate = (0, import_date_fns.subDays)(now, Number(expectedValue));
188
+ return actualDate >= targetDate;
189
+ }
190
+ case "exactly": {
191
+ const targetDate = (0, import_date_fns.subDays)(now, Number(expectedValue));
192
+ const start = (0, import_date_fns.startOfDay)(targetDate);
193
+ const end = (0, import_date_fns.endOfDay)(targetDate);
194
+ return actualDate >= start && actualDate <= end;
195
+ }
196
+ case "moreThan": {
197
+ const targetDate = (0, import_date_fns.subDays)(now, Number(expectedValue));
198
+ return actualDate <= targetDate;
199
+ }
200
+ case "before": {
201
+ const expectedDate = new Date(expectedValue);
202
+ return actualDate <= expectedDate;
203
+ }
204
+ case "on": {
205
+ const expectedDateOn = new Date(expectedValue);
206
+ const start = (0, import_date_fns.startOfDay)(expectedDateOn);
207
+ const end = (0, import_date_fns.endOfDay)(expectedDateOn);
208
+ return actualDate >= start && actualDate <= end;
209
+ }
210
+ case "after": {
211
+ const expectedDateAfter = new Date(expectedValue);
212
+ return actualDate >= expectedDateAfter;
213
+ }
214
+ case "empty":
215
+ return !actualValue || actualValue === "";
216
+ case "any":
217
+ return actualValue && actualValue !== "";
218
+ default:
219
+ return false;
220
+ }
221
+ }
222
+ function evaluateFilterResult(filter) {
223
+ if (!filter) {
224
+ return false;
225
+ }
226
+ if (filter.AND) {
227
+ return filter.AND.every((item) => evaluateFilterResult(item));
228
+ }
229
+ if (filter.OR) {
230
+ return filter.OR.some((item) => evaluateFilterResult(item));
231
+ }
232
+ if (typeof filter === "boolean") {
233
+ return filter;
234
+ }
235
+ if (typeof filter === "object") {
236
+ return true;
237
+ }
238
+ return false;
239
+ }
240
+
241
+ // src/__tests__/attribute.test.ts
242
+ var testAttributes = [
243
+ {
244
+ id: "email-attr",
245
+ codeName: "email",
246
+ dataType: import_types2.BizAttributeTypes.String
247
+ },
248
+ {
249
+ id: "age-attr",
250
+ codeName: "age",
251
+ dataType: import_types2.BizAttributeTypes.Number
252
+ },
253
+ {
254
+ id: "isPremium-attr",
255
+ codeName: "isPremium",
256
+ dataType: import_types2.BizAttributeTypes.Boolean
257
+ },
258
+ {
259
+ id: "roles-attr",
260
+ codeName: "roles",
261
+ dataType: import_types2.BizAttributeTypes.List
262
+ },
263
+ {
264
+ id: "signUpDate-attr",
265
+ codeName: "signUpDate",
266
+ dataType: import_types2.BizAttributeTypes.DateTime
267
+ },
268
+ {
269
+ id: "score-attr",
270
+ codeName: "score",
271
+ dataType: import_types2.BizAttributeTypes.Number
272
+ }
273
+ ];
274
+ var testUserAttributes = {
275
+ email: "user@example.com",
276
+ age: 25,
277
+ isPremium: true,
278
+ roles: ["admin", "user"],
279
+ signUpDate: "2024-01-15T10:30:00Z",
280
+ score: 85.5
281
+ };
282
+ describe("Attribute Filter - Complete Test Suite", () => {
283
+ describe("String conditions (8 cases)", () => {
284
+ test('should evaluate "is" condition correctly', () => {
285
+ const condition = [
286
+ {
287
+ id: "condition-1",
288
+ type: "condition",
289
+ operators: "and",
290
+ data: {
291
+ logic: "is",
292
+ value: "user@example.com",
293
+ attrId: "email-attr"
294
+ }
295
+ }
296
+ ];
297
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
298
+ });
299
+ test('should evaluate "not" condition correctly', () => {
300
+ const condition = [
301
+ {
302
+ id: "condition-2",
303
+ type: "condition",
304
+ operators: "and",
305
+ data: {
306
+ logic: "not",
307
+ value: "other@example.com",
308
+ attrId: "email-attr"
309
+ }
310
+ }
311
+ ];
312
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
313
+ });
314
+ test('should evaluate "contains" condition correctly', () => {
315
+ const condition = [
316
+ {
317
+ id: "condition-3",
318
+ type: "condition",
319
+ operators: "and",
320
+ data: {
321
+ logic: "contains",
322
+ value: "example.com",
323
+ attrId: "email-attr"
324
+ }
325
+ }
326
+ ];
327
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
328
+ });
329
+ test('should evaluate "notContain" condition correctly', () => {
330
+ const condition = [
331
+ {
332
+ id: "condition-4",
333
+ type: "condition",
334
+ operators: "and",
335
+ data: {
336
+ logic: "notContain",
337
+ value: "gmail.com",
338
+ attrId: "email-attr"
339
+ }
340
+ }
341
+ ];
342
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
343
+ });
344
+ test('should evaluate "startsWith" condition correctly', () => {
345
+ const condition = [
346
+ {
347
+ id: "condition-5",
348
+ type: "condition",
349
+ operators: "and",
350
+ data: {
351
+ logic: "startsWith",
352
+ value: "user",
353
+ attrId: "email-attr"
354
+ }
355
+ }
356
+ ];
357
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
358
+ });
359
+ test('should evaluate "endsWith" condition correctly', () => {
360
+ const condition = [
361
+ {
362
+ id: "condition-6",
363
+ type: "condition",
364
+ operators: "and",
365
+ data: {
366
+ logic: "endsWith",
367
+ value: "example.com",
368
+ attrId: "email-attr"
369
+ }
370
+ }
371
+ ];
372
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
373
+ });
374
+ test('should evaluate "empty" condition correctly', () => {
375
+ const condition = [
376
+ {
377
+ id: "condition-7",
378
+ type: "condition",
379
+ operators: "and",
380
+ data: {
381
+ logic: "empty",
382
+ attrId: "email-attr"
383
+ }
384
+ }
385
+ ];
386
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
387
+ });
388
+ test('should evaluate "any" condition correctly', () => {
389
+ const condition = [
390
+ {
391
+ id: "condition-8",
392
+ type: "condition",
393
+ operators: "and",
394
+ data: {
395
+ logic: "any",
396
+ attrId: "email-attr"
397
+ }
398
+ }
399
+ ];
400
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
401
+ });
402
+ });
403
+ describe("Number conditions (9 cases)", () => {
404
+ test('should evaluate "is" condition correctly', () => {
405
+ const condition = [
406
+ {
407
+ id: "condition-9",
408
+ type: "condition",
409
+ operators: "and",
410
+ data: {
411
+ logic: "is",
412
+ value: 25,
413
+ attrId: "age-attr"
414
+ }
415
+ }
416
+ ];
417
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
418
+ });
419
+ test('should evaluate "not" condition correctly', () => {
420
+ const condition = [
421
+ {
422
+ id: "condition-10",
423
+ type: "condition",
424
+ operators: "and",
425
+ data: {
426
+ logic: "not",
427
+ value: 30,
428
+ attrId: "age-attr"
429
+ }
430
+ }
431
+ ];
432
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
433
+ });
434
+ test('should evaluate "isLessThan" condition correctly', () => {
435
+ const condition = [
436
+ {
437
+ id: "condition-11",
438
+ type: "condition",
439
+ operators: "and",
440
+ data: {
441
+ logic: "isLessThan",
442
+ value: 30,
443
+ attrId: "age-attr"
444
+ }
445
+ }
446
+ ];
447
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
448
+ });
449
+ test('should evaluate "isLessThanOrEqualTo" condition correctly', () => {
450
+ const condition = [
451
+ {
452
+ id: "condition-12",
453
+ type: "condition",
454
+ operators: "and",
455
+ data: {
456
+ logic: "isLessThanOrEqualTo",
457
+ value: 25,
458
+ attrId: "age-attr"
459
+ }
460
+ }
461
+ ];
462
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
463
+ });
464
+ test('should evaluate "isGreaterThan" condition correctly', () => {
465
+ const condition = [
466
+ {
467
+ id: "condition-13",
468
+ type: "condition",
469
+ operators: "and",
470
+ data: {
471
+ logic: "isGreaterThan",
472
+ value: 18,
473
+ attrId: "age-attr"
474
+ }
475
+ }
476
+ ];
477
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
478
+ });
479
+ test('should evaluate "isGreaterThanOrEqualTo" condition correctly', () => {
480
+ const condition = [
481
+ {
482
+ id: "condition-14",
483
+ type: "condition",
484
+ operators: "and",
485
+ data: {
486
+ logic: "isGreaterThanOrEqualTo",
487
+ value: 25,
488
+ attrId: "age-attr"
489
+ }
490
+ }
491
+ ];
492
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
493
+ });
494
+ test('should evaluate "between" condition correctly', () => {
495
+ const condition = [
496
+ {
497
+ id: "condition-15",
498
+ type: "condition",
499
+ operators: "and",
500
+ data: {
501
+ logic: "between",
502
+ value: 20,
503
+ value2: 30,
504
+ attrId: "age-attr"
505
+ }
506
+ }
507
+ ];
508
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
509
+ });
510
+ test('should evaluate "empty" condition correctly', () => {
511
+ const condition = [
512
+ {
513
+ id: "condition-16",
514
+ type: "condition",
515
+ operators: "and",
516
+ data: {
517
+ logic: "empty",
518
+ attrId: "age-attr"
519
+ }
520
+ }
521
+ ];
522
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
523
+ });
524
+ test('should evaluate "any" condition correctly', () => {
525
+ const condition = [
526
+ {
527
+ id: "condition-17",
528
+ type: "condition",
529
+ operators: "and",
530
+ data: {
531
+ logic: "any",
532
+ attrId: "age-attr"
533
+ }
534
+ }
535
+ ];
536
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
537
+ });
538
+ });
539
+ describe("Boolean conditions (4 cases)", () => {
540
+ test('should evaluate "true" condition correctly', () => {
541
+ const condition = [
542
+ {
543
+ id: "condition-18",
544
+ type: "condition",
545
+ operators: "and",
546
+ data: {
547
+ logic: "true",
548
+ attrId: "isPremium-attr"
549
+ }
550
+ }
551
+ ];
552
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
553
+ });
554
+ test('should evaluate "false" condition correctly', () => {
555
+ const condition = [
556
+ {
557
+ id: "condition-19",
558
+ type: "condition",
559
+ operators: "and",
560
+ data: {
561
+ logic: "false",
562
+ attrId: "isPremium-attr"
563
+ }
564
+ }
565
+ ];
566
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
567
+ });
568
+ test('should evaluate "empty" condition correctly', () => {
569
+ const condition = [
570
+ {
571
+ id: "condition-20",
572
+ type: "condition",
573
+ operators: "and",
574
+ data: {
575
+ logic: "empty",
576
+ attrId: "isPremium-attr"
577
+ }
578
+ }
579
+ ];
580
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
581
+ });
582
+ test('should evaluate "any" condition correctly', () => {
583
+ const condition = [
584
+ {
585
+ id: "condition-21",
586
+ type: "condition",
587
+ operators: "and",
588
+ data: {
589
+ logic: "any",
590
+ attrId: "isPremium-attr"
591
+ }
592
+ }
593
+ ];
594
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
595
+ });
596
+ });
597
+ describe("List conditions (6 cases)", () => {
598
+ test('should evaluate "includesAtLeastOne" condition correctly', () => {
599
+ const condition = [
600
+ {
601
+ id: "condition-22",
602
+ type: "condition",
603
+ operators: "and",
604
+ data: {
605
+ logic: "includesAtLeastOne",
606
+ listValues: ["admin"],
607
+ attrId: "roles-attr"
608
+ }
609
+ }
610
+ ];
611
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
612
+ });
613
+ test('should evaluate "includesAll" condition correctly', () => {
614
+ const condition = [
615
+ {
616
+ id: "condition-23",
617
+ type: "condition",
618
+ operators: "and",
619
+ data: {
620
+ logic: "includesAll",
621
+ listValues: ["admin", "user"],
622
+ attrId: "roles-attr"
623
+ }
624
+ }
625
+ ];
626
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
627
+ });
628
+ test('should evaluate "notIncludesAtLeastOne" condition correctly', () => {
629
+ const condition = [
630
+ {
631
+ id: "condition-24",
632
+ type: "condition",
633
+ operators: "and",
634
+ data: {
635
+ logic: "notIncludesAtLeastOne",
636
+ listValues: ["guest"],
637
+ attrId: "roles-attr"
638
+ }
639
+ }
640
+ ];
641
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
642
+ });
643
+ test('should evaluate "notIncludesAll" condition correctly', () => {
644
+ const condition = [
645
+ {
646
+ id: "condition-25",
647
+ type: "condition",
648
+ operators: "and",
649
+ data: {
650
+ logic: "notIncludesAll",
651
+ listValues: ["admin", "guest"],
652
+ attrId: "roles-attr"
653
+ }
654
+ }
655
+ ];
656
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
657
+ });
658
+ test('should evaluate "empty" condition correctly', () => {
659
+ const condition = [
660
+ {
661
+ id: "condition-26",
662
+ type: "condition",
663
+ operators: "and",
664
+ data: {
665
+ logic: "empty",
666
+ attrId: "roles-attr"
667
+ }
668
+ }
669
+ ];
670
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
671
+ });
672
+ test('should evaluate "any" condition correctly', () => {
673
+ const condition = [
674
+ {
675
+ id: "condition-27",
676
+ type: "condition",
677
+ operators: "and",
678
+ data: {
679
+ logic: "any",
680
+ attrId: "roles-attr"
681
+ }
682
+ }
683
+ ];
684
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
685
+ });
686
+ });
687
+ describe("DateTime conditions (8 cases)", () => {
688
+ test('should evaluate "lessThan" condition correctly', () => {
689
+ const condition = [
690
+ {
691
+ id: "condition-28",
692
+ type: "condition",
693
+ operators: "and",
694
+ data: {
695
+ logic: "lessThan",
696
+ value: 600,
697
+ // 600 days ago (signUpDate is 575 days ago, which is < 600, so should be false)
698
+ attrId: "signUpDate-attr"
699
+ }
700
+ }
701
+ ];
702
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
703
+ });
704
+ test('should evaluate "exactly" condition correctly', () => {
705
+ const condition = [
706
+ {
707
+ id: "condition-29",
708
+ type: "condition",
709
+ operators: "and",
710
+ data: {
711
+ logic: "exactly",
712
+ value: 30,
713
+ // 30 days ago
714
+ attrId: "signUpDate-attr"
715
+ }
716
+ }
717
+ ];
718
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
719
+ });
720
+ test('should evaluate "moreThan" condition correctly', () => {
721
+ const condition = [
722
+ {
723
+ id: "condition-30",
724
+ type: "condition",
725
+ operators: "and",
726
+ data: {
727
+ logic: "moreThan",
728
+ value: 500,
729
+ // 500 days ago (signUpDate is 575 days ago, which is > 500, so should be true)
730
+ attrId: "signUpDate-attr"
731
+ }
732
+ }
733
+ ];
734
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
735
+ });
736
+ test('should evaluate "before" condition correctly', () => {
737
+ const condition = [
738
+ {
739
+ id: "condition-31",
740
+ type: "condition",
741
+ operators: "and",
742
+ data: {
743
+ logic: "before",
744
+ value: "2024-02-01T00:00:00Z",
745
+ attrId: "signUpDate-attr"
746
+ }
747
+ }
748
+ ];
749
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
750
+ });
751
+ test('should evaluate "on" condition correctly', () => {
752
+ const condition = [
753
+ {
754
+ id: "condition-32",
755
+ type: "condition",
756
+ operators: "and",
757
+ data: {
758
+ logic: "on",
759
+ value: "2024-01-15T00:00:00Z",
760
+ attrId: "signUpDate-attr"
761
+ }
762
+ }
763
+ ];
764
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
765
+ });
766
+ test('should evaluate "after" condition correctly', () => {
767
+ const condition = [
768
+ {
769
+ id: "condition-33",
770
+ type: "condition",
771
+ operators: "and",
772
+ data: {
773
+ logic: "after",
774
+ value: "2024-01-01T00:00:00Z",
775
+ attrId: "signUpDate-attr"
776
+ }
777
+ }
778
+ ];
779
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
780
+ });
781
+ test('should evaluate "empty" condition correctly', () => {
782
+ const condition = [
783
+ {
784
+ id: "condition-34",
785
+ type: "condition",
786
+ operators: "and",
787
+ data: {
788
+ logic: "empty",
789
+ attrId: "signUpDate-attr"
790
+ }
791
+ }
792
+ ];
793
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
794
+ });
795
+ test('should evaluate "any" condition correctly', () => {
796
+ const condition = [
797
+ {
798
+ id: "condition-35",
799
+ type: "condition",
800
+ operators: "and",
801
+ data: {
802
+ logic: "any",
803
+ attrId: "signUpDate-attr"
804
+ }
805
+ }
806
+ ];
807
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
808
+ });
809
+ });
810
+ describe("Complex conditions with AND/OR logic", () => {
811
+ test("should evaluate AND condition correctly", () => {
812
+ const condition = [
813
+ {
814
+ id: "condition-36",
815
+ type: "group",
816
+ operators: "and",
817
+ data: {},
818
+ conditions: [
819
+ {
820
+ id: "condition-36-1",
821
+ type: "condition",
822
+ operators: "and",
823
+ data: {
824
+ logic: "contains",
825
+ value: "example.com",
826
+ attrId: "email-attr"
827
+ }
828
+ },
829
+ {
830
+ id: "condition-36-2",
831
+ type: "condition",
832
+ operators: "and",
833
+ data: {
834
+ logic: "isGreaterThan",
835
+ value: 18,
836
+ attrId: "age-attr"
837
+ }
838
+ }
839
+ ]
840
+ }
841
+ ];
842
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
843
+ });
844
+ test("should evaluate OR condition correctly", () => {
845
+ const condition = [
846
+ {
847
+ id: "condition-37",
848
+ type: "group",
849
+ operators: "or",
850
+ data: {},
851
+ conditions: [
852
+ {
853
+ id: "condition-37-1",
854
+ type: "condition",
855
+ operators: "or",
856
+ data: {
857
+ logic: "is",
858
+ value: "wrong@email.com",
859
+ attrId: "email-attr"
860
+ }
861
+ },
862
+ {
863
+ id: "condition-37-2",
864
+ type: "condition",
865
+ operators: "or",
866
+ data: {
867
+ logic: "isGreaterThan",
868
+ value: 18,
869
+ attrId: "age-attr"
870
+ }
871
+ }
872
+ ]
873
+ }
874
+ ];
875
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
876
+ });
877
+ test("should evaluate mixed AND/OR condition correctly", () => {
878
+ const condition = [
879
+ {
880
+ id: "condition-38",
881
+ type: "group",
882
+ operators: "and",
883
+ data: {},
884
+ conditions: [
885
+ {
886
+ id: "condition-38-1",
887
+ type: "condition",
888
+ operators: "and",
889
+ data: {
890
+ logic: "contains",
891
+ value: "example.com",
892
+ attrId: "email-attr"
893
+ }
894
+ },
895
+ {
896
+ id: "condition-38-2",
897
+ type: "group",
898
+ operators: "or",
899
+ data: {},
900
+ conditions: [
901
+ {
902
+ id: "condition-38-2-1",
903
+ type: "condition",
904
+ operators: "or",
905
+ data: {
906
+ logic: "is",
907
+ value: "wrong@email.com",
908
+ attrId: "email-attr"
909
+ }
910
+ },
911
+ {
912
+ id: "condition-38-2-2",
913
+ type: "condition",
914
+ operators: "or",
915
+ data: {
916
+ logic: "isGreaterThan",
917
+ value: 18,
918
+ attrId: "age-attr"
919
+ }
920
+ }
921
+ ]
922
+ }
923
+ ]
924
+ }
925
+ ];
926
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(true);
927
+ });
928
+ });
929
+ describe("Edge cases and error handling", () => {
930
+ test("should handle empty conditions array", () => {
931
+ expect(evaluateFilterConditions([], testAttributes, testUserAttributes)).toBe(true);
932
+ });
933
+ test("should handle null/undefined conditions", () => {
934
+ expect(evaluateFilterConditions(null, testAttributes, testUserAttributes)).toBe(true);
935
+ });
936
+ test("should handle missing attribute", () => {
937
+ const condition = [
938
+ {
939
+ id: "condition-39",
940
+ type: "condition",
941
+ operators: "and",
942
+ data: {
943
+ logic: "is",
944
+ value: "test",
945
+ attrId: "non-existent-attr"
946
+ }
947
+ }
948
+ ];
949
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
950
+ });
951
+ test("should handle missing user attribute value", () => {
952
+ const userAttributesWithoutEmail = {
953
+ age: 25,
954
+ isPremium: true
955
+ };
956
+ const condition = [
957
+ {
958
+ id: "condition-40",
959
+ type: "condition",
960
+ operators: "and",
961
+ data: {
962
+ logic: "is",
963
+ value: "test@example.com",
964
+ attrId: "email-attr"
965
+ }
966
+ }
967
+ ];
968
+ expect(evaluateFilterConditions(condition, testAttributes, userAttributesWithoutEmail)).toBe(
969
+ false
970
+ );
971
+ });
972
+ test("should handle invalid data type", () => {
973
+ const invalidAttribute = [
974
+ {
975
+ id: "invalid-attr",
976
+ codeName: "invalid",
977
+ dataType: 999
978
+ // Invalid data type
979
+ }
980
+ ];
981
+ const condition = [
982
+ {
983
+ id: "condition-41",
984
+ type: "condition",
985
+ operators: "and",
986
+ data: {
987
+ logic: "is",
988
+ value: "test",
989
+ attrId: "invalid-attr"
990
+ }
991
+ }
992
+ ];
993
+ expect(evaluateFilterConditions(condition, invalidAttribute, testUserAttributes)).toBe(false);
994
+ });
995
+ test("should handle invalid logic", () => {
996
+ const condition = [
997
+ {
998
+ id: "condition-42",
999
+ type: "condition",
1000
+ operators: "and",
1001
+ data: {
1002
+ logic: "invalidLogic",
1003
+ value: "test",
1004
+ attrId: "email-attr"
1005
+ }
1006
+ }
1007
+ ];
1008
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
1009
+ });
1010
+ test("should handle missing data in condition", () => {
1011
+ const condition = [
1012
+ {
1013
+ id: "condition-43",
1014
+ type: "condition",
1015
+ operators: "and",
1016
+ data: void 0
1017
+ }
1018
+ ];
1019
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
1020
+ });
1021
+ test("should handle missing attrId in condition", () => {
1022
+ const condition = [
1023
+ {
1024
+ id: "condition-44",
1025
+ type: "condition",
1026
+ operators: "and",
1027
+ data: {
1028
+ logic: "is",
1029
+ value: "test"
1030
+ }
1031
+ }
1032
+ ];
1033
+ expect(evaluateFilterConditions(condition, testAttributes, testUserAttributes)).toBe(false);
1034
+ });
1035
+ });
1036
+ });