@capillarytech/creatives-library 8.0.225 → 8.0.227
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/constants/unified.js +1 -0
- package/package.json +1 -1
- package/utils/commonUtils.js +2 -2
- package/utils/tagValidations.js +131 -82
- package/utils/tests/commonUtil.test.js +145 -0
- package/utils/tests/tagValidations.test.js +333 -50
- package/v2Components/FormBuilder/index.js +6 -14
- package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +25 -1
- package/v2Components/TestAndPreviewSlidebox/index.js +162 -20
- package/v2Components/TestAndPreviewSlidebox/messages.js +8 -0
package/constants/unified.js
CHANGED
|
@@ -149,6 +149,7 @@ export const BADGES_ISSUE = 'BADGES_ISSUE';
|
|
|
149
149
|
export const CUSTOMER_BARCODE_TAG = 'customer_barcode';
|
|
150
150
|
export const COPY_OF = 'Copy of';
|
|
151
151
|
export const ENTRY_TRIGGER_TAG_REGEX = /\bentryTrigger\.\w+(?:\.\w+)?(?:\(\w+\))?/g;
|
|
152
|
+
export const SKIP_TAGS_REGEX_GROUPS = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)", "Link_to_[a-zA-Z]", "SURVEY.*.TOKEN", "^[A-Za-z].*\\([a-zA-Z\\d]*\\)", "referral_unique_(code|url).*userid"];
|
|
152
153
|
|
|
153
154
|
export const GET_TRANSLATION_MAPPED = {
|
|
154
155
|
'en': 'en-US',
|
package/package.json
CHANGED
package/utils/commonUtils.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
IOS,
|
|
17
17
|
} from "../v2Containers/CreativesContainer/constants";
|
|
18
18
|
import { GLOBAL_CONVERT_OPTIONS } from "../v2Components/FormBuilder/constants";
|
|
19
|
-
import { checkSupport, extractNames } from "./tagValidations";
|
|
19
|
+
import { checkSupport, extractNames, skipTags as defaultSkipTags } from "./tagValidations";
|
|
20
20
|
import { SMS_TRAI_VAR } from '../v2Containers/SmsTrai/Edit/constants';
|
|
21
21
|
export const apiMessageFormatHandler = (id, fallback) => (
|
|
22
22
|
<FormattedMessage id={id} defaultMessage={fallback} />
|
|
@@ -146,7 +146,7 @@ export const validateLiquidTemplateContent = async (
|
|
|
146
146
|
isLiquidFlow,
|
|
147
147
|
forwardedTags = {},
|
|
148
148
|
tabType,
|
|
149
|
-
skipTags =
|
|
149
|
+
skipTags = defaultSkipTags,
|
|
150
150
|
} = options;
|
|
151
151
|
const emptyBodyError = formatMessage(messages?.emailBodyEmptyError);
|
|
152
152
|
const somethingWrongMsg = formatMessage(messages?.somethingWentWrong);
|
package/utils/tagValidations.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import lodashForEach from 'lodash/forEach';
|
|
10
10
|
import lodashCloneDeep from 'lodash/cloneDeep';
|
|
11
|
-
import { ENTRY_TRIGGER_TAG_REGEX } from '../constants/unified';
|
|
11
|
+
import { ENTRY_TRIGGER_TAG_REGEX, SKIP_TAGS_REGEX_GROUPS } from '../constants/unified';
|
|
12
12
|
|
|
13
13
|
const DEFAULT = 'default';
|
|
14
14
|
const SUBTAGS = 'subtags';
|
|
@@ -19,7 +19,6 @@ const SUBTAGS = 'subtags';
|
|
|
19
19
|
* @param {Object} tagObject - The tagLookupMap.
|
|
20
20
|
*/
|
|
21
21
|
export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [], isLiquidFlow = false, forwardedTags = {}) => {
|
|
22
|
-
|
|
23
22
|
const supportedList = [];
|
|
24
23
|
// Verifies the presence of the tag in the 'Add Labels' section.
|
|
25
24
|
// Incase of journey event context the tags won't be available in the tagObject(tagLookupMap).
|
|
@@ -40,7 +39,7 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
|
|
|
40
39
|
let updatedChildName = childName;
|
|
41
40
|
let updatedWithoutDotChildName = childName;
|
|
42
41
|
if (childName?.includes(".")) {
|
|
43
|
-
updatedChildName =
|
|
42
|
+
updatedChildName = `.${childName?.split(".")?.[1]}`;
|
|
44
43
|
updatedWithoutDotChildName = childName?.split(".")?.[1];
|
|
45
44
|
}
|
|
46
45
|
if (tagObject?.[parentTag]) {
|
|
@@ -71,7 +70,6 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
|
|
|
71
70
|
if (item?.children?.length) {
|
|
72
71
|
processChildren(item?.name, item?.children);
|
|
73
72
|
}
|
|
74
|
-
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
|
|
@@ -80,19 +78,19 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
|
|
|
80
78
|
|
|
81
79
|
const handleForwardedTags = (forwardedTags) => {
|
|
82
80
|
const result = [];
|
|
83
|
-
Object.keys(forwardedTags).forEach(key => {
|
|
81
|
+
Object.keys(forwardedTags).forEach((key) => {
|
|
84
82
|
result.push(key); // Add the main key to the result array
|
|
85
|
-
|
|
83
|
+
|
|
86
84
|
// Check if there are subtags for the current key
|
|
87
85
|
if (forwardedTags[key].subtags) {
|
|
88
86
|
// If subtags exist, add all subtag keys to the result array
|
|
89
|
-
Object.keys(forwardedTags[key].subtags).forEach(subkey => {
|
|
90
|
-
|
|
87
|
+
Object.keys(forwardedTags[key].subtags).forEach((subkey) => {
|
|
88
|
+
result.push(subkey);
|
|
91
89
|
});
|
|
92
90
|
}
|
|
93
91
|
});
|
|
94
92
|
return result;
|
|
95
|
-
}
|
|
93
|
+
};
|
|
96
94
|
|
|
97
95
|
/**
|
|
98
96
|
* Extracts the names from the given data.
|
|
@@ -155,7 +153,7 @@ export const validateTags = ({
|
|
|
155
153
|
unsupportedTags: [],
|
|
156
154
|
isBraceError: false,
|
|
157
155
|
};
|
|
158
|
-
if(tags && tags.length) {
|
|
156
|
+
if (tags && tags.length) {
|
|
159
157
|
lodashForEach(tags, ({
|
|
160
158
|
definition: {
|
|
161
159
|
supportedModules,
|
|
@@ -216,12 +214,12 @@ export const validateTags = ({
|
|
|
216
214
|
// validations (eg button) are handled on valid property coming from the response.
|
|
217
215
|
response.isBraceError ? response.valid = false : response.valid = true;
|
|
218
216
|
return response;
|
|
219
|
-
}
|
|
217
|
+
};
|
|
220
218
|
|
|
221
219
|
/**
|
|
222
220
|
* Checks if the given tag is supported based on the injected tags.
|
|
223
221
|
* @param {string} checkingTag - The tag to check.
|
|
224
|
-
* @param {
|
|
222
|
+
* @param {Object} injectedTags - The injected tags.
|
|
225
223
|
* @returns {boolean} - True if the tag is supported, false otherwise.
|
|
226
224
|
*/
|
|
227
225
|
export const checkIfSupportedTag = (checkingTag, injectedTags) => {
|
|
@@ -233,33 +231,27 @@ export const checkIfSupportedTag = (checkingTag, injectedTags) => {
|
|
|
233
231
|
result = true;
|
|
234
232
|
}
|
|
235
233
|
});
|
|
236
|
-
|
|
234
|
+
|
|
237
235
|
return result;
|
|
238
|
-
}
|
|
236
|
+
};
|
|
239
237
|
|
|
240
238
|
const indexOfEnd = (targetString, string) => {
|
|
241
|
-
|
|
239
|
+
const io = targetString.indexOf(string);
|
|
242
240
|
return io == -1 ? -1 : io + string.length;
|
|
243
|
-
}
|
|
241
|
+
};
|
|
244
242
|
|
|
245
243
|
export const skipTags = (tag) => {
|
|
246
244
|
// If the tag contains the word "entryTrigger.", then it's an event context tag and should not be skipped.
|
|
247
245
|
if (tag?.match(ENTRY_TRIGGER_TAG_REGEX)) {
|
|
248
246
|
return false;
|
|
249
247
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const groupRegex = new RegExp(group
|
|
254
|
-
|
|
255
|
-
if (match !== null ) {
|
|
256
|
-
skipped = true;
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
return true;
|
|
248
|
+
// Use some() to check if any pattern matches (stops on first match)
|
|
249
|
+
return SKIP_TAGS_REGEX_GROUPS.some((group) => {
|
|
250
|
+
// Create a new RegExp for each test to avoid state issues with global flag
|
|
251
|
+
const groupRegex = new RegExp(group);
|
|
252
|
+
return groupRegex.test(tag);
|
|
260
253
|
});
|
|
261
|
-
|
|
262
|
-
}
|
|
254
|
+
};
|
|
263
255
|
|
|
264
256
|
export const transformInjectedTags = (tags) => {
|
|
265
257
|
lodashForEach(tags, (tag) => {
|
|
@@ -274,35 +266,100 @@ export const transformInjectedTags = (tags) => {
|
|
|
274
266
|
if (subKey !== '') {
|
|
275
267
|
temp['tag-header'] = true;
|
|
276
268
|
if (subKey !== SUBTAGS) {
|
|
277
|
-
temp.subtags =lodashCloneDeep(temp[subKey]);
|
|
269
|
+
temp.subtags = lodashCloneDeep(temp[subKey]);
|
|
278
270
|
delete temp[subKey];
|
|
279
271
|
}
|
|
280
272
|
temp.subtags = transformInjectedTags(temp.subtags);
|
|
281
273
|
}
|
|
282
274
|
});
|
|
283
275
|
return tags;
|
|
284
|
-
}
|
|
276
|
+
};
|
|
285
277
|
|
|
286
278
|
//checks if the opening curly brackets have corresponding closing brackets
|
|
287
279
|
export const validateIfTagClosed = (value) => {
|
|
288
280
|
if (value.includes("{{{{") || value.includes("}}}}")) {
|
|
289
281
|
return false;
|
|
290
282
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
283
|
+
const regex1 = /{{.*?}}/g;
|
|
284
|
+
const regex2 = /{{/g;
|
|
285
|
+
const regex3 = /}}/g;
|
|
294
286
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
287
|
+
const l1 = value.match(regex1)?.length;
|
|
288
|
+
const l2 = value.match(regex2)?.length;
|
|
289
|
+
const l3 = value.match(regex3)?.length;
|
|
298
290
|
|
|
299
291
|
return (l1 == l2 && l2 == l3 && l1 == l3);
|
|
300
|
-
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Validates tag format: ensures tags are in format {{tag_name}} and checks for invalid patterns
|
|
296
|
+
* Validates against:
|
|
297
|
+
* - Single braces like {tag} (must be {{tag}})
|
|
298
|
+
* - Invalid patterns like {{first or first}}, {{first and first}}
|
|
299
|
+
* - Empty tag names
|
|
300
|
+
* - Unclosed single braces within tag names
|
|
301
|
+
* @param {string} textContent - The text content to validate
|
|
302
|
+
* @returns {boolean} - True if all tags have valid format, false otherwise
|
|
303
|
+
*/
|
|
304
|
+
export const validateTagFormat = (textContent) => {
|
|
305
|
+
// Find all potential tag patterns {{tag_name}}
|
|
306
|
+
const tagPattern = /{{[^}]*}}/g;
|
|
307
|
+
const matches = textContent.match(tagPattern) || [];
|
|
308
|
+
|
|
309
|
+
// Remove all valid {{tag}} patterns from content to check for invalid braces
|
|
310
|
+
let contentWithoutValidTags = textContent;
|
|
311
|
+
matches.forEach((match) => {
|
|
312
|
+
contentWithoutValidTags = contentWithoutValidTags.replace(match, '');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Check if there are any remaining braces (single braces or unclosed braces)
|
|
316
|
+
// These would be invalid patterns like {tag}, {first, first}, etc.
|
|
317
|
+
if (contentWithoutValidTags.includes('{') || contentWithoutValidTags.includes('}')) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check each tag for valid format
|
|
322
|
+
const allTagsValid = matches.every((match) => {
|
|
323
|
+
// Valid tag format: {{tag_name}} - must start with {{ and end with }}
|
|
324
|
+
if (!match.startsWith('{{') || !match.endsWith('}}')) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Extract tag name (content between {{ and }})
|
|
329
|
+
const tagName = match.slice(2, -2).trim();
|
|
330
|
+
|
|
331
|
+
// Tag name should not be empty
|
|
332
|
+
if (!tagName) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Check for invalid patterns in tag name
|
|
337
|
+
// Invalid patterns: "first or first", "first and first", etc.
|
|
338
|
+
const invalidPatterns = [
|
|
339
|
+
/\s+or\s+/i, // " or " as separate word (e.g., "first or first")
|
|
340
|
+
/\s+and\s+/i, // " and " as separate word
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
const hasInvalidPattern = invalidPatterns.some((pattern) => pattern.test(tagName));
|
|
344
|
+
if (hasInvalidPattern) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check for unclosed single braces in tag name (e.g., {{first{name}})
|
|
349
|
+
const singleOpenBraces = (tagName.match(/{/g) || []).length;
|
|
350
|
+
const singleCloseBraces = (tagName.match(/}/g) || []).length;
|
|
351
|
+
if (singleOpenBraces !== singleCloseBraces) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return true;
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return allTagsValid;
|
|
301
359
|
};
|
|
302
360
|
|
|
303
361
|
//replaces encoded string with their respective characters
|
|
304
362
|
export const preprocessHtml = (content) => {
|
|
305
|
-
|
|
306
363
|
const replacements = {
|
|
307
364
|
"'": "'",
|
|
308
365
|
""": "'",
|
|
@@ -310,7 +367,7 @@ export const preprocessHtml = (content) => {
|
|
|
310
367
|
"&": "&",
|
|
311
368
|
"<": "<",
|
|
312
369
|
">": ">",
|
|
313
|
-
"\n": "",
|
|
370
|
+
"\n": "", // Handling newlines by replacing them with an empty string
|
|
314
371
|
};
|
|
315
372
|
|
|
316
373
|
|
|
@@ -324,28 +381,22 @@ export const preprocessHtml = (content) => {
|
|
|
324
381
|
});
|
|
325
382
|
|
|
326
383
|
// Step 2: Perform the standard replacements on the entire content
|
|
327
|
-
return contentWithStyleFixes?.replace(/'|"|&|<|>|"|\n/g, match => replacements[match]);
|
|
384
|
+
return contentWithStyleFixes?.replace(/'|"|&|<|>|"|\n/g, (match) => replacements[match]);
|
|
328
385
|
};
|
|
329
386
|
|
|
330
387
|
//this is used to get the subtags from custom or extended tags
|
|
331
|
-
export const getTagMapValue = (object = {}) =>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return { ...acc, ...(current.subtags ?? {}) };
|
|
344
|
-
}
|
|
345
|
-
// If no subtags → keep the tag itself
|
|
346
|
-
return { ...acc, [key]: current };
|
|
347
|
-
}, {});
|
|
348
|
-
};
|
|
388
|
+
export const getTagMapValue = (object = {}) => Object.values(
|
|
389
|
+
object
|
|
390
|
+
).reduce((acc, current) => ({ ...acc, ...current?.subtags ?? {} }), {});
|
|
391
|
+
|
|
392
|
+
export const getLoyaltyTagsMapValue = (object = {}) => Object.entries(object).reduce((acc, [key, current]) => {
|
|
393
|
+
if (current?.subtags && Object.keys(current.subtags).length > 0) {
|
|
394
|
+
// If subtags exist → merge them
|
|
395
|
+
return { ...acc, ...(current.subtags ?? {}) };
|
|
396
|
+
}
|
|
397
|
+
// If no subtags → keep the tag itself
|
|
398
|
+
return { ...acc, [key]: current };
|
|
399
|
+
}, {});
|
|
349
400
|
|
|
350
401
|
|
|
351
402
|
/**
|
|
@@ -354,27 +405,25 @@ export const getLoyaltyTagsMapValue = (object = {}) => {
|
|
|
354
405
|
* @param {Object} object - The input object containing top-level keys with optional subtags.
|
|
355
406
|
* @returns {Object} - A flat map containing all top-level keys and their subtags.
|
|
356
407
|
*/
|
|
357
|
-
export const getForwardedMapValues = (object = {}) => {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}, {});
|
|
380
|
-
};
|
|
408
|
+
export const getForwardedMapValues = (object = {}) => Object?.entries(object)?.reduce((acc, [key, current]) => {
|
|
409
|
+
// Check if current has 'subtags' and it's an object
|
|
410
|
+
if (current && current?.subtags && typeof current?.subtags === 'object') {
|
|
411
|
+
// Add the top-level key with its 'name' and 'desc'
|
|
412
|
+
acc[key] = {
|
|
413
|
+
name: current?.name,
|
|
414
|
+
desc: current?.desc,
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// Merge the subtags into the accumulator
|
|
418
|
+
acc = { ...acc, ...current?.subtags };
|
|
419
|
+
} else if (current && typeof current === 'object') {
|
|
420
|
+
// If no 'subtags', add the top-level key with its 'name' and 'desc'
|
|
421
|
+
acc[key] = {
|
|
422
|
+
name: current?.name,
|
|
423
|
+
desc: current?.desc,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// If the current entry is not an object or lacks 'name'/'desc', skip it
|
|
428
|
+
return acc;
|
|
429
|
+
}, {});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
addBaseToTemplate,
|
|
8
8
|
validateCarouselCards,
|
|
9
9
|
} from "../commonUtils";
|
|
10
|
+
import { skipTags } from "../tagValidations";
|
|
10
11
|
import { SMS_TRAI_VAR } from '../../v2Containers/SmsTrai/Edit/constants';
|
|
11
12
|
import { ANDROID, IOS } from '../../v2Containers/CreativesContainer/constants';
|
|
12
13
|
|
|
@@ -122,6 +123,150 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
122
123
|
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
123
124
|
});
|
|
124
125
|
|
|
126
|
+
it("should skip referral_unique_code tags and not trigger unsupported tag error", async () => {
|
|
127
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
128
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_code_C6SOE_userid" }] }, isError: false })
|
|
129
|
+
);
|
|
130
|
+
await validateLiquidTemplateContent("foo", {
|
|
131
|
+
getLiquidTags,
|
|
132
|
+
formatMessage,
|
|
133
|
+
messages,
|
|
134
|
+
onError,
|
|
135
|
+
onSuccess,
|
|
136
|
+
tagLookupMap,
|
|
137
|
+
eventContextTags,
|
|
138
|
+
skipTags
|
|
139
|
+
});
|
|
140
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
141
|
+
expect(onError).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should skip referral_unique_url tags and not trigger unsupported tag error", async () => {
|
|
145
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
146
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_url_C6SOE_userid" }] }, isError: false })
|
|
147
|
+
);
|
|
148
|
+
await validateLiquidTemplateContent("foo", {
|
|
149
|
+
getLiquidTags,
|
|
150
|
+
formatMessage,
|
|
151
|
+
messages,
|
|
152
|
+
onError,
|
|
153
|
+
onSuccess,
|
|
154
|
+
tagLookupMap,
|
|
155
|
+
eventContextTags,
|
|
156
|
+
skipTags
|
|
157
|
+
});
|
|
158
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
159
|
+
expect(onError).not.toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should skip referral_unique_code tags with different codes", async () => {
|
|
163
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
164
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_code_ABC123_userid" }] }, isError: false })
|
|
165
|
+
);
|
|
166
|
+
await validateLiquidTemplateContent("foo", {
|
|
167
|
+
getLiquidTags,
|
|
168
|
+
formatMessage,
|
|
169
|
+
messages,
|
|
170
|
+
onError,
|
|
171
|
+
onSuccess,
|
|
172
|
+
tagLookupMap,
|
|
173
|
+
eventContextTags,
|
|
174
|
+
skipTags
|
|
175
|
+
});
|
|
176
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
177
|
+
expect(onError).not.toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should skip referral_unique_url tags with different codes", async () => {
|
|
181
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
182
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_url_ABC123_userid" }] }, isError: false })
|
|
183
|
+
);
|
|
184
|
+
await validateLiquidTemplateContent("foo", {
|
|
185
|
+
getLiquidTags,
|
|
186
|
+
formatMessage,
|
|
187
|
+
messages,
|
|
188
|
+
onError,
|
|
189
|
+
onSuccess,
|
|
190
|
+
tagLookupMap,
|
|
191
|
+
eventContextTags,
|
|
192
|
+
skipTags
|
|
193
|
+
});
|
|
194
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
195
|
+
expect(onError).not.toHaveBeenCalled();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should skip referral_unique_code tags with alphanumeric codes", async () => {
|
|
199
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
200
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_code_XYZ789_userid" }] }, isError: false })
|
|
201
|
+
);
|
|
202
|
+
await validateLiquidTemplateContent("foo", {
|
|
203
|
+
getLiquidTags,
|
|
204
|
+
formatMessage,
|
|
205
|
+
messages,
|
|
206
|
+
onError,
|
|
207
|
+
onSuccess,
|
|
208
|
+
tagLookupMap,
|
|
209
|
+
eventContextTags,
|
|
210
|
+
skipTags
|
|
211
|
+
});
|
|
212
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
213
|
+
expect(onError).not.toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should skip referral_unique_url tags with alphanumeric codes", async () => {
|
|
217
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
218
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_url_XYZ789_userid" }] }, isError: false })
|
|
219
|
+
);
|
|
220
|
+
await validateLiquidTemplateContent("foo", {
|
|
221
|
+
getLiquidTags,
|
|
222
|
+
formatMessage,
|
|
223
|
+
messages,
|
|
224
|
+
onError,
|
|
225
|
+
onSuccess,
|
|
226
|
+
tagLookupMap,
|
|
227
|
+
eventContextTags,
|
|
228
|
+
skipTags
|
|
229
|
+
});
|
|
230
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
231
|
+
expect(onError).not.toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should skip both referral_unique_code and referral_unique_url tags in the same content", async () => {
|
|
235
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
236
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_code_C6SOE_userid" }, { name: "referral_unique_url_C6SOE_userid" }] }, isError: false })
|
|
237
|
+
);
|
|
238
|
+
await validateLiquidTemplateContent("foo", {
|
|
239
|
+
getLiquidTags,
|
|
240
|
+
formatMessage,
|
|
241
|
+
messages,
|
|
242
|
+
onError,
|
|
243
|
+
onSuccess,
|
|
244
|
+
tagLookupMap,
|
|
245
|
+
eventContextTags,
|
|
246
|
+
skipTags
|
|
247
|
+
});
|
|
248
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
249
|
+
expect(onError).not.toHaveBeenCalled();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should skip referral_unique_code_632L7_userid tag (real-world example)", async () => {
|
|
253
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
254
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "referral_unique_code_632L7_userid" }] }, isError: false })
|
|
255
|
+
);
|
|
256
|
+
await validateLiquidTemplateContent("foo", {
|
|
257
|
+
getLiquidTags,
|
|
258
|
+
formatMessage,
|
|
259
|
+
messages,
|
|
260
|
+
onError,
|
|
261
|
+
onSuccess,
|
|
262
|
+
tagLookupMap,
|
|
263
|
+
eventContextTags,
|
|
264
|
+
skipTags
|
|
265
|
+
});
|
|
266
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
267
|
+
expect(onError).not.toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
|
|
125
270
|
it("calls onError with emailBodyEmptyError when validString is falsy", async () => {
|
|
126
271
|
const getLiquidTags = jest.fn((content, cb) => cb({ askAiraResponse: { errors: [], data: [] }, isError: false }));
|
|
127
272
|
const formatMessage = jest.fn((msg) => msg.id);
|