@usertour/helpers 0.0.18 → 0.0.21
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/dist/__tests__/condition.test.cjs +679 -8
- package/dist/__tests__/condition.test.js +342 -1
- package/dist/__tests__/url.test.cjs +314 -54
- package/dist/__tests__/url.test.js +248 -4
- package/dist/chunk-PAESAL23.js +77 -0
- package/dist/chunk-UNXDVBM3.js +98 -0
- package/dist/chunk-YYIGUZNZ.js +17 -0
- package/dist/conditions/attribute.d.cts +2 -10
- package/dist/conditions/attribute.d.ts +2 -10
- package/dist/conditions/condition.cjs +328 -39
- package/dist/conditions/condition.d.cts +29 -3
- package/dist/conditions/condition.d.ts +29 -3
- package/dist/conditions/condition.js +9 -1
- package/dist/conditions/index.cjs +153 -104
- package/dist/conditions/index.d.cts +1 -1
- package/dist/conditions/index.d.ts +1 -1
- package/dist/conditions/index.js +14 -5
- package/dist/conditions/url-v1.cjs +89 -0
- package/dist/conditions/url-v1.d.cts +3 -0
- package/dist/conditions/url-v1.d.ts +3 -0
- package/dist/{chunk-BC7KXBMF.js → conditions/url-v1.js} +4 -8
- package/dist/conditions/url-v2.cjs +101 -0
- package/dist/conditions/url-v2.d.cts +16 -0
- package/dist/conditions/url-v2.d.ts +16 -0
- package/dist/conditions/url-v2.js +7 -0
- package/dist/conditions/url.cjs +69 -50
- package/dist/conditions/url.js +2 -1
- package/dist/index.cjs +153 -104
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -8
- package/package.json +2 -2
- package/dist/chunk-YOFQHQ7D.js +0 -92
|
@@ -23,10 +23,304 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/__tests__/condition.test.ts
|
|
26
|
-
var
|
|
26
|
+
var import_types3 = require("@usertour/types");
|
|
27
27
|
|
|
28
28
|
// src/conditions/condition.ts
|
|
29
|
+
var import_types2 = require("@usertour/types");
|
|
29
30
|
var import_fast_deep_equal = __toESM(require("fast-deep-equal"), 1);
|
|
31
|
+
|
|
32
|
+
// src/conditions/url-v2.ts
|
|
33
|
+
function parseUrl(url) {
|
|
34
|
+
const match = url.match(/^(([a-z\d]+):\/\/)?([^/?#]+)?(\/[^?#]*)?(\?([^#]*))?(#.*)?$/i);
|
|
35
|
+
if (!match)
|
|
36
|
+
return null;
|
|
37
|
+
const [, , scheme, domain, path, , query, fragment] = match;
|
|
38
|
+
return {
|
|
39
|
+
scheme: scheme || "",
|
|
40
|
+
domain: domain || "",
|
|
41
|
+
path: path || "",
|
|
42
|
+
query: query || "",
|
|
43
|
+
fragment: fragment || ""
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function escapeRegex(str) {
|
|
47
|
+
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
48
|
+
}
|
|
49
|
+
function processUrlPattern(pattern, wildcardReplacement, paramReplacement) {
|
|
50
|
+
let processed = escapeRegex(pattern);
|
|
51
|
+
processed = processed.replace(/\\\*/g, `${wildcardReplacement}*`);
|
|
52
|
+
if (paramReplacement) {
|
|
53
|
+
processed = processed.replace(/:[a-zA-Z0-9_]+/g, `[^${paramReplacement}]+`);
|
|
54
|
+
}
|
|
55
|
+
return processed;
|
|
56
|
+
}
|
|
57
|
+
function urlPatternToRegex(pattern) {
|
|
58
|
+
const parsed = parseUrl(pattern);
|
|
59
|
+
if (!parsed) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const { scheme, domain, path, query, fragment } = parsed;
|
|
63
|
+
const schemePattern = scheme ? escapeRegex(scheme) : "[a-z\\d]+";
|
|
64
|
+
const domainPattern = domain ? processUrlPattern(domain, "[^/]", ".") : "[^/]*";
|
|
65
|
+
const portPattern = "(:\\d+)?";
|
|
66
|
+
const pathPattern = path ? processUrlPattern(path, "[^?#]", "/") : "/[^?#]*";
|
|
67
|
+
let queryPattern;
|
|
68
|
+
if (query) {
|
|
69
|
+
queryPattern = "";
|
|
70
|
+
new URLSearchParams(query).forEach((value, key) => {
|
|
71
|
+
let valuePattern;
|
|
72
|
+
if (value === "") {
|
|
73
|
+
valuePattern = "=?";
|
|
74
|
+
} else if (value === "*") {
|
|
75
|
+
valuePattern = "(=[^&#]*)?";
|
|
76
|
+
} else {
|
|
77
|
+
const encodedValue = value.split(/\*/g).map((part) => encodeURI(part)).join("*");
|
|
78
|
+
valuePattern = `=${processUrlPattern(encodedValue, "[^#]")}`;
|
|
79
|
+
}
|
|
80
|
+
queryPattern += `(?=.*[?&]${escapeRegex(key)}${valuePattern}([&#]|$))`;
|
|
81
|
+
});
|
|
82
|
+
queryPattern += "\\?[^#]*";
|
|
83
|
+
} else {
|
|
84
|
+
queryPattern = "(\\?[^#]*)?";
|
|
85
|
+
}
|
|
86
|
+
const fragmentPattern = fragment ? processUrlPattern(fragment, ".", "/") : "(#.*)?";
|
|
87
|
+
return new RegExp(
|
|
88
|
+
`^${schemePattern}://${domainPattern}${portPattern}${pathPattern}${queryPattern}${fragmentPattern}$`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function matchUrlPattern(urlPattern, url) {
|
|
92
|
+
if (urlPattern.includes.length === 0 && urlPattern.excludes.length === 0) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const matchesInclude = urlPattern.includes.length === 0 || urlPattern.includes.some((includePattern) => {
|
|
96
|
+
const regex = urlPatternToRegex(includePattern);
|
|
97
|
+
return regex && url.match(regex);
|
|
98
|
+
});
|
|
99
|
+
const matchesExclude = urlPattern.excludes.some((excludePattern) => {
|
|
100
|
+
const regex = urlPatternToRegex(excludePattern);
|
|
101
|
+
return regex && url.match(regex);
|
|
102
|
+
});
|
|
103
|
+
return matchesInclude && !matchesExclude;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/conditions/url.ts
|
|
107
|
+
var isMatchUrlPattern = (_url, includes, excludes) => {
|
|
108
|
+
return matchUrlPattern({ includes, excludes }, _url);
|
|
109
|
+
};
|
|
110
|
+
var evaluateUrlCondition = (rules, url) => {
|
|
111
|
+
const { excludes = [], includes = [] } = rules.data || {};
|
|
112
|
+
return isMatchUrlPattern(url, includes, excludes);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/conditions/time.ts
|
|
116
|
+
var import_date_fns = require("date-fns");
|
|
117
|
+
var evaluateTimeCondition = (rules) => {
|
|
118
|
+
try {
|
|
119
|
+
const { endDate, endDateHour, endDateMinute, startDate, startDateHour, startDateMinute } = rules.data || {};
|
|
120
|
+
if (!startDate || !startDateHour || !startDateMinute) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const startTimeString = `${startDate}T${startDateHour.padStart(2, "0")}:${startDateMinute.padStart(2, "0")}:00`;
|
|
124
|
+
const startTime = (0, import_date_fns.parseISO)(startTimeString);
|
|
125
|
+
if (!(0, import_date_fns.isValid)(startTime)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const now = /* @__PURE__ */ new Date();
|
|
129
|
+
if (!endDate || !endDateHour || !endDateMinute) {
|
|
130
|
+
return (0, import_date_fns.isAfter)(now, startTime);
|
|
131
|
+
}
|
|
132
|
+
const endTimeString = `${endDate}T${endDateHour.padStart(2, "0")}:${endDateMinute.padStart(2, "0")}:00`;
|
|
133
|
+
const endTime = (0, import_date_fns.parseISO)(endTimeString);
|
|
134
|
+
if (!(0, import_date_fns.isValid)(endTime)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return (0, import_date_fns.isAfter)(now, startTime) && (0, import_date_fns.isBefore)(now, endTime);
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/conditions/attribute.ts
|
|
144
|
+
var import_types = require("@usertour/types");
|
|
145
|
+
var import_date_fns2 = require("date-fns");
|
|
146
|
+
function evaluateAttributeCondition(condition, attributes, userAttributes) {
|
|
147
|
+
const { data } = condition;
|
|
148
|
+
if (!data) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const { logic, value, attrId, value2, listValues = [] } = data;
|
|
152
|
+
if (!attrId) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const attr = attributes.find((attr2) => attr2.id === attrId);
|
|
156
|
+
if (!attr) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const actualValue = getAttributeValue(attr.codeName, userAttributes);
|
|
160
|
+
if (attr.dataType === import_types.BizAttributeTypes.String) {
|
|
161
|
+
return evaluateStringCondition(logic, actualValue, value);
|
|
162
|
+
}
|
|
163
|
+
if (attr.dataType === import_types.BizAttributeTypes.Number) {
|
|
164
|
+
return evaluateNumberCondition(logic, actualValue, value, value2);
|
|
165
|
+
}
|
|
166
|
+
if (attr.dataType === import_types.BizAttributeTypes.Boolean) {
|
|
167
|
+
return evaluateBooleanCondition(logic, actualValue);
|
|
168
|
+
}
|
|
169
|
+
if (attr.dataType === import_types.BizAttributeTypes.List) {
|
|
170
|
+
return evaluateListCondition(logic, actualValue, listValues);
|
|
171
|
+
}
|
|
172
|
+
if (attr.dataType === import_types.BizAttributeTypes.DateTime) {
|
|
173
|
+
return evaluateDateTimeCondition(logic, actualValue, value);
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
function getAttributeValue(codeName, userAttributes) {
|
|
178
|
+
return userAttributes == null ? void 0 : userAttributes[codeName];
|
|
179
|
+
}
|
|
180
|
+
function evaluateStringCondition(logic, actualValue, expectedValue) {
|
|
181
|
+
const stringValue = actualValue === null || actualValue === void 0 ? "" : String(actualValue);
|
|
182
|
+
switch (logic) {
|
|
183
|
+
case "is":
|
|
184
|
+
return stringValue === expectedValue;
|
|
185
|
+
case "not":
|
|
186
|
+
return stringValue !== expectedValue;
|
|
187
|
+
case "contains":
|
|
188
|
+
return stringValue.includes(expectedValue);
|
|
189
|
+
case "notContain":
|
|
190
|
+
return !stringValue.includes(expectedValue);
|
|
191
|
+
case "startsWith":
|
|
192
|
+
return stringValue.startsWith(expectedValue);
|
|
193
|
+
case "endsWith":
|
|
194
|
+
return stringValue.endsWith(expectedValue);
|
|
195
|
+
case "empty": {
|
|
196
|
+
const isEmpty = !stringValue || stringValue === "";
|
|
197
|
+
return isEmpty;
|
|
198
|
+
}
|
|
199
|
+
case "any":
|
|
200
|
+
return Boolean(stringValue && stringValue !== "");
|
|
201
|
+
default:
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function evaluateNumberCondition(logic, actualValue, expectedValue, expectedValue2) {
|
|
206
|
+
const numValue = Number(actualValue);
|
|
207
|
+
const numValue2 = Number(expectedValue2);
|
|
208
|
+
if (Number.isNaN(numValue)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
switch (logic) {
|
|
212
|
+
case "is":
|
|
213
|
+
return numValue === expectedValue;
|
|
214
|
+
case "not":
|
|
215
|
+
return numValue !== expectedValue;
|
|
216
|
+
case "isLessThan":
|
|
217
|
+
return numValue < expectedValue;
|
|
218
|
+
case "isLessThanOrEqualTo":
|
|
219
|
+
return numValue <= expectedValue;
|
|
220
|
+
case "isGreaterThan":
|
|
221
|
+
return numValue > expectedValue;
|
|
222
|
+
case "isGreaterThanOrEqualTo":
|
|
223
|
+
return numValue >= expectedValue;
|
|
224
|
+
case "between":
|
|
225
|
+
return numValue >= expectedValue && numValue <= numValue2;
|
|
226
|
+
case "empty":
|
|
227
|
+
return actualValue === null || actualValue === void 0 || actualValue === "";
|
|
228
|
+
case "any":
|
|
229
|
+
return actualValue !== null && actualValue !== void 0 && actualValue !== "";
|
|
230
|
+
default:
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function evaluateBooleanCondition(logic, actualValue) {
|
|
235
|
+
switch (logic) {
|
|
236
|
+
case "true":
|
|
237
|
+
return actualValue === true;
|
|
238
|
+
case "false":
|
|
239
|
+
return actualValue === false;
|
|
240
|
+
case "empty":
|
|
241
|
+
return actualValue === null || actualValue === void 0 || actualValue === "";
|
|
242
|
+
case "any":
|
|
243
|
+
return actualValue !== null && actualValue !== void 0 && actualValue !== "";
|
|
244
|
+
default:
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function evaluateListCondition(logic, actualValue, expectedValues) {
|
|
249
|
+
const arrayValue = Array.isArray(actualValue) ? actualValue : [];
|
|
250
|
+
if (logic === "empty" || logic === "any") {
|
|
251
|
+
switch (logic) {
|
|
252
|
+
case "empty":
|
|
253
|
+
return !arrayValue || arrayValue.length === 0;
|
|
254
|
+
case "any":
|
|
255
|
+
return arrayValue && arrayValue.length > 0;
|
|
256
|
+
default:
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const filteredValues = expectedValues.filter(
|
|
261
|
+
(value) => value !== null && value !== void 0 && value !== ""
|
|
262
|
+
);
|
|
263
|
+
if (!filteredValues.length) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
switch (logic) {
|
|
267
|
+
case "includesAtLeastOne":
|
|
268
|
+
return filteredValues.some((value) => arrayValue.includes(value));
|
|
269
|
+
case "includesAll":
|
|
270
|
+
return filteredValues.every((value) => arrayValue.includes(value));
|
|
271
|
+
case "notIncludesAtLeastOne":
|
|
272
|
+
return !filteredValues.some((value) => arrayValue.includes(value));
|
|
273
|
+
case "notIncludesAll":
|
|
274
|
+
return !filteredValues.every((value) => arrayValue.includes(value));
|
|
275
|
+
default:
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function evaluateDateTimeCondition(logic, actualValue, expectedValue) {
|
|
280
|
+
const actualDate = actualValue ? new Date(actualValue) : null;
|
|
281
|
+
const now = /* @__PURE__ */ new Date();
|
|
282
|
+
if (!actualDate || Number.isNaN(actualDate.getTime())) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
switch (logic) {
|
|
286
|
+
case "lessThan": {
|
|
287
|
+
const targetDate = (0, import_date_fns2.subDays)(now, Number(expectedValue));
|
|
288
|
+
return actualDate >= targetDate;
|
|
289
|
+
}
|
|
290
|
+
case "exactly": {
|
|
291
|
+
const targetDate = (0, import_date_fns2.subDays)(now, Number(expectedValue));
|
|
292
|
+
const start = (0, import_date_fns2.startOfDay)(targetDate);
|
|
293
|
+
const end = (0, import_date_fns2.endOfDay)(targetDate);
|
|
294
|
+
return actualDate >= start && actualDate <= end;
|
|
295
|
+
}
|
|
296
|
+
case "moreThan": {
|
|
297
|
+
const targetDate = (0, import_date_fns2.subDays)(now, Number(expectedValue));
|
|
298
|
+
return actualDate <= targetDate;
|
|
299
|
+
}
|
|
300
|
+
case "before": {
|
|
301
|
+
const expectedDate = new Date(expectedValue);
|
|
302
|
+
return actualDate <= expectedDate;
|
|
303
|
+
}
|
|
304
|
+
case "on": {
|
|
305
|
+
const expectedDateOn = new Date(expectedValue);
|
|
306
|
+
const start = (0, import_date_fns2.startOfDay)(expectedDateOn);
|
|
307
|
+
const end = (0, import_date_fns2.endOfDay)(expectedDateOn);
|
|
308
|
+
return actualDate >= start && actualDate <= end;
|
|
309
|
+
}
|
|
310
|
+
case "after": {
|
|
311
|
+
const expectedDateAfter = new Date(expectedValue);
|
|
312
|
+
return actualDate >= expectedDateAfter;
|
|
313
|
+
}
|
|
314
|
+
case "empty":
|
|
315
|
+
return !actualValue || actualValue === "";
|
|
316
|
+
case "any":
|
|
317
|
+
return actualValue && actualValue !== "";
|
|
318
|
+
default:
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/conditions/condition.ts
|
|
30
324
|
var isConditionsActived = (conditions) => {
|
|
31
325
|
if (!conditions || conditions.length === 0) {
|
|
32
326
|
return false;
|
|
@@ -40,7 +334,7 @@ var isConditionsActived = (conditions) => {
|
|
|
40
334
|
});
|
|
41
335
|
return operator === "and" ? actives.length === conditions.length : actives.length > 0;
|
|
42
336
|
};
|
|
43
|
-
|
|
337
|
+
var filterConditionsByType = (conditions, allowedTypes) => {
|
|
44
338
|
return conditions.filter((condition) => {
|
|
45
339
|
if (condition.type === "group" && condition.conditions) {
|
|
46
340
|
const filteredGroupConditions = filterConditionsByType(condition.conditions, allowedTypes);
|
|
@@ -56,12 +350,53 @@ function filterConditionsByType(conditions, allowedTypes) {
|
|
|
56
350
|
}
|
|
57
351
|
return condition;
|
|
58
352
|
});
|
|
59
|
-
}
|
|
353
|
+
};
|
|
354
|
+
var evaluateRule = (rule, options) => {
|
|
355
|
+
var _a;
|
|
356
|
+
const { typeControl = {}, activatedIds, deactivatedIds } = options;
|
|
357
|
+
const ruleId = rule.id;
|
|
358
|
+
if (activatedIds == null ? void 0 : activatedIds.includes(ruleId))
|
|
359
|
+
return true;
|
|
360
|
+
if (deactivatedIds == null ? void 0 : deactivatedIds.includes(ruleId))
|
|
361
|
+
return false;
|
|
362
|
+
if (typeControl[rule.type] === false) {
|
|
363
|
+
return rule.actived || false;
|
|
364
|
+
}
|
|
365
|
+
switch (rule.type) {
|
|
366
|
+
case import_types2.RulesType.CURRENT_PAGE:
|
|
367
|
+
return evaluateUrlCondition(rule, ((_a = options.clientContext) == null ? void 0 : _a.page_url) || "");
|
|
368
|
+
case import_types2.RulesType.TIME:
|
|
369
|
+
return evaluateTimeCondition(rule);
|
|
370
|
+
case import_types2.RulesType.USER_ATTR:
|
|
371
|
+
case import_types2.RulesType.COMPANY_ATTR:
|
|
372
|
+
return evaluateAttributeCondition(
|
|
373
|
+
rule,
|
|
374
|
+
options.attributes || [],
|
|
375
|
+
options.userAttributes || {}
|
|
376
|
+
);
|
|
377
|
+
default:
|
|
378
|
+
return rule.actived || false;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
var activedRulesConditions = (conditions, options = {}) => {
|
|
382
|
+
return conditions.map((rule) => {
|
|
383
|
+
if (rule.type === "group" && rule.conditions) {
|
|
384
|
+
return {
|
|
385
|
+
...rule,
|
|
386
|
+
conditions: activedRulesConditions(rule.conditions, options)
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
...rule,
|
|
391
|
+
actived: evaluateRule(rule, options)
|
|
392
|
+
};
|
|
393
|
+
});
|
|
394
|
+
};
|
|
60
395
|
|
|
61
396
|
// src/__tests__/condition.test.ts
|
|
62
397
|
describe("filterConditionsByType", () => {
|
|
63
398
|
test("should return empty array when no conditions provided", () => {
|
|
64
|
-
const result = filterConditionsByType([], [
|
|
399
|
+
const result = filterConditionsByType([], [import_types3.RulesType.CURRENT_PAGE, import_types3.RulesType.TIME]);
|
|
65
400
|
expect(result).toEqual([]);
|
|
66
401
|
});
|
|
67
402
|
test("should filter out non-matching types", () => {
|
|
@@ -81,7 +416,7 @@ describe("filterConditionsByType", () => {
|
|
|
81
416
|
data: {}
|
|
82
417
|
}
|
|
83
418
|
];
|
|
84
|
-
const result = filterConditionsByType(conditions, [
|
|
419
|
+
const result = filterConditionsByType(conditions, [import_types3.RulesType.CURRENT_PAGE, import_types3.RulesType.TIME]);
|
|
85
420
|
expect(result).toHaveLength(1);
|
|
86
421
|
expect(result[0].type).toBe("current-page");
|
|
87
422
|
});
|
|
@@ -102,7 +437,7 @@ describe("filterConditionsByType", () => {
|
|
|
102
437
|
data: {}
|
|
103
438
|
}
|
|
104
439
|
];
|
|
105
|
-
const result = filterConditionsByType(conditions, [
|
|
440
|
+
const result = filterConditionsByType(conditions, [import_types3.RulesType.CURRENT_PAGE, import_types3.RulesType.TIME]);
|
|
106
441
|
expect(result).toHaveLength(2);
|
|
107
442
|
expect(result.map((c) => c.type)).toEqual(["current-page", "time"]);
|
|
108
443
|
});
|
|
@@ -132,7 +467,7 @@ describe("filterConditionsByType", () => {
|
|
|
132
467
|
]
|
|
133
468
|
}
|
|
134
469
|
];
|
|
135
|
-
const result = filterConditionsByType(conditions, [
|
|
470
|
+
const result = filterConditionsByType(conditions, [import_types3.RulesType.CURRENT_PAGE, import_types3.RulesType.TIME]);
|
|
136
471
|
expect(result).toHaveLength(1);
|
|
137
472
|
expect(result[0].type).toBe("group");
|
|
138
473
|
expect(result[0].conditions).toHaveLength(1);
|
|
@@ -157,7 +492,7 @@ describe("filterConditionsByType", () => {
|
|
|
157
492
|
]
|
|
158
493
|
}
|
|
159
494
|
];
|
|
160
|
-
const result = filterConditionsByType(conditions, [
|
|
495
|
+
const result = filterConditionsByType(conditions, [import_types3.RulesType.CURRENT_PAGE, import_types3.RulesType.TIME]);
|
|
161
496
|
expect(result).toHaveLength(0);
|
|
162
497
|
});
|
|
163
498
|
});
|
|
@@ -255,3 +590,339 @@ describe("isConditionsActived", () => {
|
|
|
255
590
|
expect(result).toBe(true);
|
|
256
591
|
});
|
|
257
592
|
});
|
|
593
|
+
describe("activedRulesConditions", () => {
|
|
594
|
+
const mockConditions = [
|
|
595
|
+
{
|
|
596
|
+
id: "rule-1",
|
|
597
|
+
type: "current-page",
|
|
598
|
+
operators: "and",
|
|
599
|
+
actived: false,
|
|
600
|
+
data: {
|
|
601
|
+
includes: ["https://example.com"],
|
|
602
|
+
excludes: []
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
id: "rule-2",
|
|
607
|
+
type: "time",
|
|
608
|
+
operators: "and",
|
|
609
|
+
actived: false,
|
|
610
|
+
data: {
|
|
611
|
+
startDate: "2024-01-01",
|
|
612
|
+
startDateHour: "00",
|
|
613
|
+
startDateMinute: "00"
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
id: "rule-3",
|
|
618
|
+
type: "user-attr",
|
|
619
|
+
operators: "and",
|
|
620
|
+
actived: false,
|
|
621
|
+
data: {
|
|
622
|
+
attrId: "email",
|
|
623
|
+
logic: "is",
|
|
624
|
+
value: "test@example.com"
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
id: "rule-4",
|
|
629
|
+
type: "element",
|
|
630
|
+
operators: "and",
|
|
631
|
+
actived: true,
|
|
632
|
+
data: {}
|
|
633
|
+
}
|
|
634
|
+
];
|
|
635
|
+
const mockOptions = {
|
|
636
|
+
clientContext: {
|
|
637
|
+
page_url: "https://example.com",
|
|
638
|
+
viewport_width: 1920,
|
|
639
|
+
viewport_height: 1080
|
|
640
|
+
},
|
|
641
|
+
attributes: [
|
|
642
|
+
{
|
|
643
|
+
id: "email",
|
|
644
|
+
codeName: "email",
|
|
645
|
+
dataType: 2
|
|
646
|
+
// String
|
|
647
|
+
}
|
|
648
|
+
],
|
|
649
|
+
userAttributes: {
|
|
650
|
+
email: "test@example.com"
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
test("should return conditions with default actived state when no options provided", () => {
|
|
654
|
+
const conditions = [
|
|
655
|
+
{
|
|
656
|
+
id: "rule-1",
|
|
657
|
+
type: "element",
|
|
658
|
+
operators: "and",
|
|
659
|
+
actived: true,
|
|
660
|
+
data: {}
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
id: "rule-2",
|
|
664
|
+
type: "element",
|
|
665
|
+
operators: "and",
|
|
666
|
+
actived: false,
|
|
667
|
+
data: {}
|
|
668
|
+
}
|
|
669
|
+
];
|
|
670
|
+
const result = activedRulesConditions(conditions);
|
|
671
|
+
expect(result).toHaveLength(2);
|
|
672
|
+
expect(result[0].actived).toBe(true);
|
|
673
|
+
expect(result[1].actived).toBe(false);
|
|
674
|
+
});
|
|
675
|
+
test("should force activate rules by ID", () => {
|
|
676
|
+
const result = activedRulesConditions(mockConditions, {
|
|
677
|
+
...mockOptions,
|
|
678
|
+
activatedIds: ["rule-1", "rule-2"]
|
|
679
|
+
});
|
|
680
|
+
expect(result[0].actived).toBe(true);
|
|
681
|
+
expect(result[1].actived).toBe(true);
|
|
682
|
+
expect(result[2].actived).toBe(true);
|
|
683
|
+
expect(result[3].actived).toBe(true);
|
|
684
|
+
});
|
|
685
|
+
test("should force deactivate rules by ID", () => {
|
|
686
|
+
const result = activedRulesConditions(mockConditions, {
|
|
687
|
+
...mockOptions,
|
|
688
|
+
deactivatedIds: ["rule-1", "rule-4"]
|
|
689
|
+
});
|
|
690
|
+
expect(result[0].actived).toBe(false);
|
|
691
|
+
expect(result[1].actived).toBe(true);
|
|
692
|
+
expect(result[2].actived).toBe(true);
|
|
693
|
+
expect(result[3].actived).toBe(false);
|
|
694
|
+
});
|
|
695
|
+
test("should prioritize activatedIds over deactivatedIds", () => {
|
|
696
|
+
const result = activedRulesConditions(mockConditions, {
|
|
697
|
+
...mockOptions,
|
|
698
|
+
activatedIds: ["rule-1"],
|
|
699
|
+
deactivatedIds: ["rule-1"]
|
|
700
|
+
});
|
|
701
|
+
expect(result[0].actived).toBe(true);
|
|
702
|
+
});
|
|
703
|
+
test("should disable evaluation for specific rule types", () => {
|
|
704
|
+
const result = activedRulesConditions(mockConditions, {
|
|
705
|
+
...mockOptions,
|
|
706
|
+
typeControl: {
|
|
707
|
+
[import_types3.RulesType.CURRENT_PAGE]: false,
|
|
708
|
+
[import_types3.RulesType.TIME]: false
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
expect(result[0].actived).toBe(false);
|
|
712
|
+
expect(result[1].actived).toBe(false);
|
|
713
|
+
expect(result[2].actived).toBe(true);
|
|
714
|
+
expect(result[3].actived).toBe(true);
|
|
715
|
+
});
|
|
716
|
+
test("should evaluate URL conditions correctly", () => {
|
|
717
|
+
const result = activedRulesConditions(mockConditions, mockOptions);
|
|
718
|
+
expect(result[0].actived).toBe(false);
|
|
719
|
+
});
|
|
720
|
+
test("should evaluate time conditions correctly", () => {
|
|
721
|
+
const result = activedRulesConditions(mockConditions, mockOptions);
|
|
722
|
+
expect(typeof result[1].actived).toBe("boolean");
|
|
723
|
+
});
|
|
724
|
+
test("should evaluate attribute conditions correctly", () => {
|
|
725
|
+
const result = activedRulesConditions(mockConditions, mockOptions);
|
|
726
|
+
expect(result[2].actived).toBe(true);
|
|
727
|
+
});
|
|
728
|
+
test("should handle rules without ID", () => {
|
|
729
|
+
const conditions = [
|
|
730
|
+
{
|
|
731
|
+
id: "rule-without-id",
|
|
732
|
+
type: "element",
|
|
733
|
+
operators: "and",
|
|
734
|
+
actived: true,
|
|
735
|
+
data: {}
|
|
736
|
+
}
|
|
737
|
+
];
|
|
738
|
+
const result = activedRulesConditions(conditions, {
|
|
739
|
+
activatedIds: ["non-existent"],
|
|
740
|
+
deactivatedIds: ["non-existent"]
|
|
741
|
+
});
|
|
742
|
+
expect(result[0].actived).toBe(true);
|
|
743
|
+
});
|
|
744
|
+
test("should handle nested group conditions", () => {
|
|
745
|
+
const nestedConditions = [
|
|
746
|
+
{
|
|
747
|
+
id: "group-1",
|
|
748
|
+
type: "group",
|
|
749
|
+
operators: "and",
|
|
750
|
+
actived: false,
|
|
751
|
+
data: {},
|
|
752
|
+
conditions: [
|
|
753
|
+
{
|
|
754
|
+
id: "rule-1",
|
|
755
|
+
type: "current-page",
|
|
756
|
+
operators: "and",
|
|
757
|
+
actived: false,
|
|
758
|
+
data: {
|
|
759
|
+
includes: ["https://example.com"],
|
|
760
|
+
excludes: []
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
id: "rule-2",
|
|
765
|
+
type: "element",
|
|
766
|
+
operators: "and",
|
|
767
|
+
actived: true,
|
|
768
|
+
data: {}
|
|
769
|
+
}
|
|
770
|
+
]
|
|
771
|
+
}
|
|
772
|
+
];
|
|
773
|
+
const result = activedRulesConditions(nestedConditions, {
|
|
774
|
+
...mockOptions,
|
|
775
|
+
activatedIds: ["rule-1"]
|
|
776
|
+
});
|
|
777
|
+
expect(result[0].type).toBe("group");
|
|
778
|
+
expect(result[0].conditions).toBeDefined();
|
|
779
|
+
expect(result[0].conditions[0].actived).toBe(true);
|
|
780
|
+
expect(result[0].conditions[1].actived).toBe(true);
|
|
781
|
+
});
|
|
782
|
+
test("should handle deep nested group conditions", () => {
|
|
783
|
+
const deepNestedConditions = [
|
|
784
|
+
{
|
|
785
|
+
id: "group-1",
|
|
786
|
+
type: "group",
|
|
787
|
+
operators: "and",
|
|
788
|
+
actived: false,
|
|
789
|
+
data: {},
|
|
790
|
+
conditions: [
|
|
791
|
+
{
|
|
792
|
+
id: "group-2",
|
|
793
|
+
type: "group",
|
|
794
|
+
operators: "and",
|
|
795
|
+
actived: false,
|
|
796
|
+
data: {},
|
|
797
|
+
conditions: [
|
|
798
|
+
{
|
|
799
|
+
id: "rule-1",
|
|
800
|
+
type: "current-page",
|
|
801
|
+
operators: "and",
|
|
802
|
+
actived: false,
|
|
803
|
+
data: {
|
|
804
|
+
includes: ["https://example.com"],
|
|
805
|
+
excludes: []
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
]
|
|
809
|
+
}
|
|
810
|
+
]
|
|
811
|
+
}
|
|
812
|
+
];
|
|
813
|
+
const result = activedRulesConditions(deepNestedConditions, {
|
|
814
|
+
...mockOptions,
|
|
815
|
+
activatedIds: ["rule-1"]
|
|
816
|
+
});
|
|
817
|
+
expect(result[0].conditions[0].conditions[0].actived).toBe(true);
|
|
818
|
+
});
|
|
819
|
+
test("should handle empty conditions array", () => {
|
|
820
|
+
const result = activedRulesConditions([]);
|
|
821
|
+
expect(result).toEqual([]);
|
|
822
|
+
});
|
|
823
|
+
test("should handle conditions with missing data", () => {
|
|
824
|
+
const conditions = [
|
|
825
|
+
{
|
|
826
|
+
id: "rule-1",
|
|
827
|
+
type: "current-page",
|
|
828
|
+
operators: "and",
|
|
829
|
+
actived: false,
|
|
830
|
+
data: void 0
|
|
831
|
+
}
|
|
832
|
+
];
|
|
833
|
+
const result = activedRulesConditions(conditions, mockOptions);
|
|
834
|
+
expect(result[0].actived).toBe(false);
|
|
835
|
+
});
|
|
836
|
+
test("should handle mixed rule types with different evaluation results", () => {
|
|
837
|
+
const mixedConditions = [
|
|
838
|
+
{
|
|
839
|
+
id: "url-rule",
|
|
840
|
+
type: "current-page",
|
|
841
|
+
operators: "and",
|
|
842
|
+
actived: false,
|
|
843
|
+
data: {
|
|
844
|
+
includes: ["https://wrong-url.com"],
|
|
845
|
+
excludes: []
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
id: "attr-rule",
|
|
850
|
+
type: "user-attr",
|
|
851
|
+
operators: "and",
|
|
852
|
+
actived: false,
|
|
853
|
+
data: {
|
|
854
|
+
attrId: "email",
|
|
855
|
+
logic: "is",
|
|
856
|
+
value: "test@example.com"
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
id: "element-rule",
|
|
861
|
+
type: "element",
|
|
862
|
+
operators: "and",
|
|
863
|
+
actived: false,
|
|
864
|
+
data: {}
|
|
865
|
+
}
|
|
866
|
+
];
|
|
867
|
+
const result = activedRulesConditions(mixedConditions, mockOptions);
|
|
868
|
+
expect(result[0].actived).toBe(false);
|
|
869
|
+
expect(result[1].actived).toBe(true);
|
|
870
|
+
expect(result[2].actived).toBe(false);
|
|
871
|
+
});
|
|
872
|
+
test("should handle type control with partial configuration", () => {
|
|
873
|
+
const result = activedRulesConditions(mockConditions, {
|
|
874
|
+
...mockOptions,
|
|
875
|
+
typeControl: {
|
|
876
|
+
[import_types3.RulesType.CURRENT_PAGE]: false
|
|
877
|
+
// Other types not specified, should evaluate normally
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
expect(result[0].actived).toBe(false);
|
|
881
|
+
expect(result[1].actived).toBe(true);
|
|
882
|
+
expect(result[2].actived).toBe(true);
|
|
883
|
+
expect(result[3].actived).toBe(true);
|
|
884
|
+
});
|
|
885
|
+
test("should handle missing clientContext gracefully", () => {
|
|
886
|
+
const result = activedRulesConditions(mockConditions, {
|
|
887
|
+
attributes: mockOptions.attributes,
|
|
888
|
+
userAttributes: mockOptions.userAttributes
|
|
889
|
+
});
|
|
890
|
+
expect(result).toHaveLength(4);
|
|
891
|
+
expect(typeof result[0].actived).toBe("boolean");
|
|
892
|
+
});
|
|
893
|
+
test("should handle missing attributes gracefully", () => {
|
|
894
|
+
const result = activedRulesConditions(mockConditions, {
|
|
895
|
+
clientContext: mockOptions.clientContext,
|
|
896
|
+
userAttributes: mockOptions.userAttributes
|
|
897
|
+
});
|
|
898
|
+
expect(result[2].actived).toBe(false);
|
|
899
|
+
});
|
|
900
|
+
test("should handle missing userAttributes gracefully", () => {
|
|
901
|
+
const result = activedRulesConditions(mockConditions, {
|
|
902
|
+
clientContext: mockOptions.clientContext,
|
|
903
|
+
attributes: mockOptions.attributes
|
|
904
|
+
});
|
|
905
|
+
expect(result[2].actived).toBe(false);
|
|
906
|
+
});
|
|
907
|
+
test("debug: should test URL evaluation directly", () => {
|
|
908
|
+
const urlCondition = {
|
|
909
|
+
id: "debug-rule",
|
|
910
|
+
type: "current-page",
|
|
911
|
+
operators: "and",
|
|
912
|
+
actived: false,
|
|
913
|
+
data: {
|
|
914
|
+
includes: ["https://example.com"],
|
|
915
|
+
excludes: []
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
const options = {
|
|
919
|
+
clientContext: {
|
|
920
|
+
page_url: "https://example.com",
|
|
921
|
+
viewport_width: 1920,
|
|
922
|
+
viewport_height: 1080
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
const result = activedRulesConditions([urlCondition], options);
|
|
926
|
+
expect(result[0].actived).toBe(false);
|
|
927
|
+
});
|
|
928
|
+
});
|