@capillarytech/creatives-library 8.0.283 → 8.0.285-alpha.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/constants/unified.js +0 -1
- package/package.json +1 -1
- package/utils/common.js +0 -7
- package/utils/commonUtils.js +2 -79
- package/utils/tagValidations.js +92 -71
- package/utils/tests/commonUtil.test.js +79 -32
- package/utils/tests/tagValidations.test.js +18 -37
- package/v2Components/CapWhatsappCarouselButton/index.js +32 -14
- package/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
- package/v2Components/FormBuilder/index.js +47 -126
- package/v2Containers/CreativesContainer/index.js +0 -1
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +6 -18
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +8 -106
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/InApp/index.js +2 -23
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InappAdvance/index.js +3 -24
- package/v2Containers/Line/Container/Text/index.js +0 -1
- package/v2Containers/MobilePushNew/index.js +4 -24
- package/v2Containers/Rcs/index.js +12 -37
- package/v2Containers/SmsTrai/Edit/index.js +4 -17
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +9 -0
- package/v2Containers/Viber/index.js +0 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -3
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +18 -9
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -24
- package/v2Containers/Whatsapp/index.js +9 -20
- package/v2Containers/Zalo/index.js +3 -11
package/constants/unified.js
CHANGED
|
@@ -43,7 +43,6 @@ export const HOSPITALITY_BASED_SCOPE = 'HOSPITALITY_BASED_SCOPE';
|
|
|
43
43
|
export const REGISTRATION_CUSTOM_FIELD = 'Registration custom fields';
|
|
44
44
|
export const GIFT_CARDS = 'GIFT_CARDS';
|
|
45
45
|
export const PROMO_ENGINE = 'PROMO_ENGINE';
|
|
46
|
-
export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
|
|
47
46
|
export const ENABLE_NEW_MPUSH = 'ENABLE_NEW_MPUSH';
|
|
48
47
|
export const SUPPORT_CK_EDITOR = 'SUPPORT_CK_EDITOR';
|
|
49
48
|
export const CUSTOM_TAG = 'CustomTagMessage';
|
package/package.json
CHANGED
package/utils/common.js
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
BADGES_ISSUE,
|
|
23
23
|
ENABLE_WECHAT,
|
|
24
24
|
ENABLE_WEBPUSH,
|
|
25
|
-
LIQUID_SUPPORT,
|
|
26
25
|
SUPPORT_CK_EDITOR,
|
|
27
26
|
ENABLE_NEW_MPUSH
|
|
28
27
|
} from '../constants/unified';
|
|
@@ -91,12 +90,6 @@ export const hasPromoFeature = Auth.hasFeatureAccess.bind(
|
|
|
91
90
|
null,
|
|
92
91
|
PROMO_ENGINE,
|
|
93
92
|
);
|
|
94
|
-
|
|
95
|
-
export const hasLiquidSupportFeature = Auth.hasFeatureAccess.bind(
|
|
96
|
-
null,
|
|
97
|
-
LIQUID_SUPPORT,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
93
|
export const hasSupportCKEditor = Auth.hasFeatureAccess.bind(
|
|
101
94
|
null,
|
|
102
95
|
SUPPORT_CK_EDITOR,
|
package/utils/commonUtils.js
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
IOS,
|
|
17
17
|
} from "../v2Containers/CreativesContainer/constants";
|
|
18
18
|
import { GLOBAL_CONVERT_OPTIONS } from "../v2Components/FormBuilder/constants";
|
|
19
|
-
import { checkSupport, extractNames, skipTags as defaultSkipTags, isInsideLiquidBlock } from "./tagValidations";
|
|
20
19
|
import { SMS_TRAI_VAR } from '../v2Containers/SmsTrai/Edit/constants';
|
|
21
20
|
export const apiMessageFormatHandler = (id, fallback) => (
|
|
22
21
|
<FormattedMessage id={id} defaultMessage={fallback} />
|
|
@@ -146,7 +145,6 @@ export const validateLiquidTemplateContent = async (
|
|
|
146
145
|
isLiquidFlow,
|
|
147
146
|
forwardedTags = {},
|
|
148
147
|
tabType,
|
|
149
|
-
skipTags = defaultSkipTags,
|
|
150
148
|
} = options;
|
|
151
149
|
const emptyBodyError = formatMessage(messages?.emailBodyEmptyError);
|
|
152
150
|
const somethingWrongMsg = formatMessage(messages?.somethingWentWrong);
|
|
@@ -204,81 +202,6 @@ export const validateLiquidTemplateContent = async (
|
|
|
204
202
|
});
|
|
205
203
|
return false;
|
|
206
204
|
}
|
|
207
|
-
// Extract and validate tags
|
|
208
|
-
const extractedLiquidTags = extractNames(result?.data || []);
|
|
209
|
-
// Get supported tags
|
|
210
|
-
const supportedLiquidTags = checkSupport(
|
|
211
|
-
result,
|
|
212
|
-
tagLookupMap,
|
|
213
|
-
eventContextTags,
|
|
214
|
-
isLiquidFlow,
|
|
215
|
-
forwardedTags
|
|
216
|
-
);
|
|
217
|
-
// Helper function to check if a tag appears only inside {% %} blocks
|
|
218
|
-
const isTagOnlyInsideLiquidBlocks = (tagName) => {
|
|
219
|
-
// Escape special regex characters in tag name, including dots
|
|
220
|
-
// Dots need to be escaped to match literally (item.name should match item.name, not item or name)
|
|
221
|
-
const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
222
|
-
|
|
223
|
-
// First, check if tag appears in {% %} syntax itself (like "order.items" in "{% for item in order.items %}")
|
|
224
|
-
// This is a tag used in Liquid logic, not output, so it should always be skipped
|
|
225
|
-
// Match the tag name as a whole (with dots escaped), optionally surrounded by word boundaries or non-word chars
|
|
226
|
-
// For tags with dots, we match them directly; for simple tags, we use word boundaries
|
|
227
|
-
const hasDot = tagName.includes('.');
|
|
228
|
-
const liquidSyntaxPattern = hasDot
|
|
229
|
-
? `{%[^%]*${escapedTagName}[^%]*%}`
|
|
230
|
-
: `{%[^%]*\\b${escapedTagName}\\b[^%]*%}`;
|
|
231
|
-
const liquidSyntaxRegex = new RegExp(liquidSyntaxPattern, 'g');
|
|
232
|
-
const liquidSyntaxMatches = Array.from(content.matchAll(liquidSyntaxRegex));
|
|
233
|
-
|
|
234
|
-
// Find all occurrences of {{tagName}} in the content (output tags)
|
|
235
|
-
// Match patterns like: {{tagName}}, {{ tagName }}, {{tagName }}, {{ tagName}}
|
|
236
|
-
// Use non-word-boundary approach for tags with dots (item.name should match item.name, not item or name separately)
|
|
237
|
-
const outputTagRegex = new RegExp(`\\{\\{\\s*${escapedTagName}\\s*\\}\\}`, 'g');
|
|
238
|
-
const outputTagMatches = Array.from(content.matchAll(outputTagRegex));
|
|
239
|
-
const outputTagPositions = outputTagMatches.map((match) => match.index);
|
|
240
|
-
|
|
241
|
-
// If tag appears in {% %} syntax, skip validation
|
|
242
|
-
if (liquidSyntaxMatches.length > 0) {
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// If no output tag matches found, don't skip validation
|
|
247
|
-
// The tag was extracted by the API, so it exists somewhere and should be validated
|
|
248
|
-
if (outputTagPositions.length === 0) {
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Check if all output tag occurrences are inside {% %} blocks
|
|
253
|
-
// We check the position of {{ to see if it's inside a block
|
|
254
|
-
// Only skip validation if ALL occurrences are inside blocks
|
|
255
|
-
return outputTagPositions.every((tagIndex) => {
|
|
256
|
-
if (tagIndex === undefined || tagIndex === null) {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
return isInsideLiquidBlock(content, tagIndex);
|
|
260
|
-
});
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
// Find unsupported tags, excluding those that are only inside {% %} blocks
|
|
264
|
-
const unsupportedLiquidTags = extractedLiquidTags?.filter(
|
|
265
|
-
(tag) => !supportedLiquidTags?.includes(tag)
|
|
266
|
-
&& !skipTags(tag)
|
|
267
|
-
&& !isTagOnlyInsideLiquidBlocks(tag)
|
|
268
|
-
);
|
|
269
|
-
// Handle unsupported tags
|
|
270
|
-
if (unsupportedLiquidTags?.length > 0) {
|
|
271
|
-
const errorMsg = formatMessage(messages.unsupportedTagsValidationError, {
|
|
272
|
-
unsupportedTags: unsupportedLiquidTags.join(", "),
|
|
273
|
-
});
|
|
274
|
-
onError({
|
|
275
|
-
standardErrors: [],
|
|
276
|
-
liquidErrors: [errorMsg],
|
|
277
|
-
tabType,
|
|
278
|
-
});
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
// All validations passed
|
|
282
205
|
onSuccess(content, tabType);
|
|
283
206
|
return true;
|
|
284
207
|
};
|
|
@@ -365,8 +288,8 @@ export const _validatePlatformSpecificContent = async (
|
|
|
365
288
|
/**
|
|
366
289
|
* Validate Mobile Push content for both Android and iOS tabs
|
|
367
290
|
* @param {object} formData - Form data containing Android and iOS content
|
|
368
|
-
* @param {object} options - Options for validation
|
|
369
|
-
* @returns {Promise} - Promise that resolves when validation
|
|
291
|
+
* @param {object} options - Options for validation (currentTab, onError, onSuccess, getLiquidTags, formatMessage, messages, tagLookupMap, eventContextTags, isLiquidFlow, forwardedTags, singleTab). skipTags and extractNames are no longer accepted.
|
|
292
|
+
* @returns {Promise<boolean>} - Promise that resolves to true when validation succeeds, false otherwise
|
|
370
293
|
*/
|
|
371
294
|
export const validateMobilePushContent = async (formData, options) => {
|
|
372
295
|
const {
|
package/utils/tagValidations.js
CHANGED
|
@@ -177,97 +177,118 @@ export const isInsideLiquidBlock = (content, tagIndex) => {
|
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
/**
|
|
180
|
-
*
|
|
181
|
-
* @param {Object} params
|
|
180
|
+
* Shared core for tag validation. Used by validateTags (tagValidations) and FormBuilder.validateTags.
|
|
181
|
+
* @param {Object} params
|
|
182
|
+
* @param {string} params.contentForBraceCheck - String to run validateIfTagClosed on.
|
|
183
|
+
* @param {string} params.contentForUnsubscribeScan - String to scan for {{...}} unsubscribe variants.
|
|
184
|
+
* @param {Array} [params.tags] - Tag definitions (for definition-based missing tags).
|
|
185
|
+
* @param {string} [params.currentModule] - Module context (e.g. 'default', 'outbound').
|
|
186
|
+
* @param {boolean} [params.isFullMode] - If true, skip unsubscribe checks.
|
|
187
|
+
* @param {Array} [params.initialMissingTags=null] - If set, use instead of computing from definitions.
|
|
188
|
+
* @param {function} [params.skipTagsFn=skipTags] - skipTags implementation.
|
|
189
|
+
* @param {boolean} [params.includeIsContentEmpty=false] - If true, response includes isContentEmpty: false.
|
|
190
|
+
* @returns {{ valid: boolean, missingTags: string[], unsupportedTags: string[], isBraceError: boolean }}
|
|
182
191
|
*/
|
|
183
|
-
export const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
tagModule,
|
|
189
|
-
eventContextTags,
|
|
192
|
+
export const validateTagsCore = ({
|
|
193
|
+
contentForBraceCheck,
|
|
194
|
+
contentForUnsubscribeScan,
|
|
195
|
+
tags,
|
|
196
|
+
currentModule,
|
|
190
197
|
isFullMode,
|
|
198
|
+
initialMissingTags = null,
|
|
199
|
+
skipTagsFn = skipTags,
|
|
200
|
+
includeIsContentEmpty = false,
|
|
191
201
|
}) => {
|
|
192
|
-
const tags = tagsParam;
|
|
193
|
-
const injectedTags = transformInjectedTags(injectedTagsParams);
|
|
194
|
-
let currentModule = location?.query?.module ? location?.query?.module : DEFAULT;
|
|
195
|
-
if (tagModule) {
|
|
196
|
-
currentModule = tagModule;
|
|
197
|
-
}
|
|
198
202
|
const response = {
|
|
199
203
|
valid: true,
|
|
200
204
|
missingTags: [],
|
|
201
205
|
unsupportedTags: [],
|
|
202
206
|
isBraceError: false,
|
|
207
|
+
...(includeIsContentEmpty && { isContentEmpty: false }),
|
|
203
208
|
};
|
|
204
|
-
|
|
209
|
+
|
|
205
210
|
if (tags && tags.length && !isFullMode) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
211
|
+
if (initialMissingTags == null) {
|
|
212
|
+
// Definition-based: same as original tagValidations (when caller does not pass initialMissingTags)
|
|
213
|
+
lodashForEach(tags, ({
|
|
214
|
+
definition: {
|
|
215
|
+
supportedModules,
|
|
216
|
+
value,
|
|
217
|
+
},
|
|
218
|
+
}) => {
|
|
219
|
+
if (value === 'unsubscribe') {
|
|
220
|
+
lodashForEach(supportedModules, (module) => {
|
|
221
|
+
if (module.mandatory && (currentModule === module.context)) {
|
|
222
|
+
if (contentForUnsubscribeScan.indexOf(`{{${value}}}`) === -1) {
|
|
223
|
+
response.missingTags.push(value);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
218
227
|
}
|
|
219
228
|
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const regex = /{{[
|
|
225
|
-
let match = regex.exec(
|
|
229
|
+
} else {
|
|
230
|
+
response.missingTags = [...initialMissingTags];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const regex = /{{([^}]+)}}/g;
|
|
234
|
+
let match = regex.exec(contentForUnsubscribeScan);
|
|
226
235
|
while (match !== null) {
|
|
227
|
-
const tagValue = match[
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
ifSupported = true;
|
|
236
|
+
const tagValue = match[1].trim();
|
|
237
|
+
const ifSkipped = skipTagsFn(tagValue);
|
|
238
|
+
if (ifSkipped && tagValue.indexOf('unsubscribe') !== -1) {
|
|
239
|
+
const missingTagIndex = response.missingTags.indexOf('unsubscribe');
|
|
240
|
+
if (missingTagIndex !== -1) {
|
|
241
|
+
response.missingTags.splice(missingTagIndex, 1);
|
|
234
242
|
}
|
|
235
|
-
});
|
|
236
|
-
const ifSkipped = skipTags(tagValue);
|
|
237
|
-
if (ifSkipped) {
|
|
238
|
-
ifSupported = true;
|
|
239
|
-
const isUnsubscribeSkipped = tagValue.indexOf("unsubscribe") !== -1;
|
|
240
|
-
if (isUnsubscribeSkipped) {
|
|
241
|
-
const missingTagIndex = response.missingTags.indexOf("unsubscribe");
|
|
242
|
-
if (missingTagIndex !== -1) {
|
|
243
|
-
response.missingTags.splice(missingTagIndex, 1);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
// Journey Event Context Tags support
|
|
248
|
-
eventContextTags?.forEach((tag) => {
|
|
249
|
-
if (tagValue === tag?.tagName) {
|
|
250
|
-
ifSupported = true;
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
ifSupported = ifSupported || checkIfSupportedTag(tagValue, injectedTags);
|
|
254
|
-
// Only add to unsupportedTags if not inside a {% ... %} block and does not contain a dot
|
|
255
|
-
if (!ifSupported && !isInsideLiquidBlock(content, tagIndex) && tagValue?.indexOf('.') === -1) {
|
|
256
|
-
response.unsupportedTags.push(tagValue);
|
|
257
|
-
response.valid = false;
|
|
258
|
-
}
|
|
259
|
-
if (response.unsupportedTags.length === 0 && response.missingTags.length === 0) {
|
|
260
|
-
response.valid = true;
|
|
261
243
|
}
|
|
244
|
+
match = regex.exec(contentForUnsubscribeScan);
|
|
262
245
|
}
|
|
246
|
+
|
|
247
|
+
if (response.missingTags.length > 0) {
|
|
248
|
+
response.valid = false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
response.isBraceError = !validateIfTagClosed(contentForBraceCheck);
|
|
253
|
+
if (response.isBraceError) {
|
|
254
|
+
response.valid = false;
|
|
255
|
+
} else if (response.missingTags.length > 0) {
|
|
256
|
+
response.valid = false;
|
|
263
257
|
}
|
|
264
|
-
response.isBraceError = !validateIfTagClosed(content);
|
|
265
|
-
// response should not be valid if there is unbalanced bracket error, as
|
|
266
|
-
// validations (eg button) are handled on valid property coming from the response.
|
|
267
|
-
response.isBraceError ? response.valid = false : response.valid = true;
|
|
268
258
|
return response;
|
|
269
259
|
};
|
|
270
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Validates the tags based on the provided parameters.
|
|
263
|
+
* @param {Object} params - The parameters for tag validation.
|
|
264
|
+
* @param {string} params.content - Content to validate.
|
|
265
|
+
* @param {Array} [params.tagsParam] - Tag definitions.
|
|
266
|
+
* @param {Object} [params.location] - Location with query.module.
|
|
267
|
+
* @param {string} [params.tagModule] - Override for current module context.
|
|
268
|
+
* @param {boolean} [params.isFullMode] - If true, skip unsubscribe checks.
|
|
269
|
+
* @returns {{ valid: boolean, missingTags: string[], unsupportedTags: string[], isBraceError: boolean }}
|
|
270
|
+
*/
|
|
271
|
+
export const validateTags = ({
|
|
272
|
+
content,
|
|
273
|
+
tagsParam,
|
|
274
|
+
location,
|
|
275
|
+
tagModule,
|
|
276
|
+
isFullMode,
|
|
277
|
+
}) => {
|
|
278
|
+
const tags = tagsParam;
|
|
279
|
+
let currentModule = location?.query?.module ? location?.query?.module : DEFAULT;
|
|
280
|
+
if (tagModule) {
|
|
281
|
+
currentModule = tagModule;
|
|
282
|
+
}
|
|
283
|
+
return validateTagsCore({
|
|
284
|
+
contentForBraceCheck: content,
|
|
285
|
+
contentForUnsubscribeScan: content,
|
|
286
|
+
tags,
|
|
287
|
+
currentModule,
|
|
288
|
+
isFullMode,
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
|
|
271
292
|
/**
|
|
272
293
|
* Checks if the given tag is supported based on the injected tags.
|
|
273
294
|
* @param {string} checkingTag - The tag to check.
|
|
@@ -27,6 +27,11 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
27
27
|
|
|
28
28
|
beforeEach(() => {
|
|
29
29
|
jest.clearAllMocks();
|
|
30
|
+
formatMessage.mockImplementation((msg, vars) =>
|
|
31
|
+
vars && vars.unsupportedTags != null
|
|
32
|
+
? `${msg?.id}:${vars.unsupportedTags}`
|
|
33
|
+
: (msg?.id ?? "unsupported")
|
|
34
|
+
);
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
it("calls onError for empty content", async () => {
|
|
@@ -43,7 +48,7 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
43
48
|
eventContextTags
|
|
44
49
|
});
|
|
45
50
|
expect(onError).toHaveBeenCalledWith({
|
|
46
|
-
standardErrors: [
|
|
51
|
+
standardErrors: ["empty"],
|
|
47
52
|
liquidErrors: [],
|
|
48
53
|
tabType: undefined
|
|
49
54
|
});
|
|
@@ -86,9 +91,9 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
86
91
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
87
92
|
});
|
|
88
93
|
|
|
89
|
-
it("calls
|
|
94
|
+
it("calls onSuccess when API returns no errors and extracted tags (extracted tags are not validated)", async () => {
|
|
90
95
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
91
|
-
cb({ askAiraResponse: { errors: [], data: [{ name: "
|
|
96
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "foo" }] }, isError: false })
|
|
92
97
|
);
|
|
93
98
|
await validateLiquidTemplateContent("foo", {
|
|
94
99
|
getLiquidTags,
|
|
@@ -99,15 +104,11 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
99
104
|
tagLookupMap,
|
|
100
105
|
eventContextTags
|
|
101
106
|
});
|
|
102
|
-
expect(
|
|
103
|
-
|
|
104
|
-
liquidErrors: [undefined],
|
|
105
|
-
tabType: undefined
|
|
106
|
-
});
|
|
107
|
-
expect(onSuccess).not.toHaveBeenCalled();
|
|
107
|
+
expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
|
|
108
|
+
expect(onError).not.toHaveBeenCalled();
|
|
108
109
|
});
|
|
109
110
|
|
|
110
|
-
it("calls onSuccess for valid content", async () => {
|
|
111
|
+
it("calls onSuccess for valid content when API returns multiple extracted tags but no errors", async () => {
|
|
111
112
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
112
113
|
cb({ askAiraResponse: { errors: [], data: [{ name: "foo" }, { name: "bar" }] }, isError: false })
|
|
113
114
|
);
|
|
@@ -297,7 +298,7 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
297
298
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
298
299
|
});
|
|
299
300
|
|
|
300
|
-
it("
|
|
301
|
+
it("calls onSuccess when API returns extracted tags but no errors (extracted tags are not validated)", async () => {
|
|
301
302
|
const content = '{% for item in order.items %} Hello {% endfor %}';
|
|
302
303
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
303
304
|
cb({ askAiraResponse: { errors: [], data: [{ name: "order.items" }] }, isError: false })
|
|
@@ -311,12 +312,11 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
311
312
|
tagLookupMap,
|
|
312
313
|
eventContextTags
|
|
313
314
|
});
|
|
314
|
-
// order.items appears in {% %} syntax, so it should be skipped and validation should pass
|
|
315
315
|
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
316
316
|
expect(onError).not.toHaveBeenCalled();
|
|
317
317
|
});
|
|
318
318
|
|
|
319
|
-
it("
|
|
319
|
+
it("calls onSuccess when API returns extracted tags in {% %} blocks but no errors", async () => {
|
|
320
320
|
const content = '{% for item in order.items %} {{ item.name }} - {{ item.quantity }} {% endfor %}';
|
|
321
321
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
322
322
|
cb({ askAiraResponse: { errors: [], data: [{ name: "item.name" }, { name: "item.quantity" }] }, isError: false })
|
|
@@ -330,12 +330,11 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
330
330
|
tagLookupMap,
|
|
331
331
|
eventContextTags
|
|
332
332
|
});
|
|
333
|
-
// item.name and item.quantity appear inside {% for %} block, so they should be skipped
|
|
334
333
|
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
335
334
|
expect(onError).not.toHaveBeenCalled();
|
|
336
335
|
});
|
|
337
336
|
|
|
338
|
-
it("
|
|
337
|
+
it("calls onSuccess when API returns extracted tags not in content but no errors", async () => {
|
|
339
338
|
const content = 'Some content without the tag';
|
|
340
339
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
341
340
|
cb({ askAiraResponse: { errors: [], data: [{ name: "extractedTag" }] }, isError: false })
|
|
@@ -349,16 +348,11 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
349
348
|
tagLookupMap,
|
|
350
349
|
eventContextTags
|
|
351
350
|
});
|
|
352
|
-
|
|
353
|
-
expect(onError).
|
|
354
|
-
standardErrors: [],
|
|
355
|
-
liquidErrors: [undefined],
|
|
356
|
-
tabType: undefined
|
|
357
|
-
});
|
|
358
|
-
expect(onSuccess).not.toHaveBeenCalled();
|
|
351
|
+
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
352
|
+
expect(onError).not.toHaveBeenCalled();
|
|
359
353
|
});
|
|
360
354
|
|
|
361
|
-
it("
|
|
355
|
+
it("calls onSuccess when API returns tags outside {% %} but no errors", async () => {
|
|
362
356
|
const content = '{{ outsideTag }} {% for item in order.items %} {{ item.name }} {% endfor %}';
|
|
363
357
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
364
358
|
cb({ askAiraResponse: { errors: [], data: [{ name: "outsideTag" }, { name: "item.name" }] }, isError: false })
|
|
@@ -372,17 +366,71 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
372
366
|
tagLookupMap,
|
|
373
367
|
eventContextTags
|
|
374
368
|
});
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
369
|
+
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
370
|
+
expect(onError).not.toHaveBeenCalled();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("calls onSuccess when API returns tag in {{ }} and no errors", async () => {
|
|
374
|
+
const content = 'Hello {{ unsupportedTag }} world';
|
|
375
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
376
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "unsupportedTag" }] }, isError: false })
|
|
377
|
+
);
|
|
378
|
+
await validateLiquidTemplateContent(content, {
|
|
379
|
+
getLiquidTags,
|
|
380
|
+
formatMessage,
|
|
381
|
+
messages,
|
|
382
|
+
onError,
|
|
383
|
+
onSuccess,
|
|
384
|
+
tagLookupMap,
|
|
385
|
+
eventContextTags
|
|
381
386
|
});
|
|
382
|
-
expect(onSuccess).
|
|
387
|
+
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
388
|
+
expect(onError).not.toHaveBeenCalled();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("calls onSuccess when API returns tag with spacing variants and no errors", async () => {
|
|
392
|
+
const content = '{{ tag}} and {{tag }} and {{ tag }}';
|
|
393
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
394
|
+
cb({
|
|
395
|
+
askAiraResponse: {
|
|
396
|
+
errors: [],
|
|
397
|
+
data: [{ name: "tag" }]
|
|
398
|
+
},
|
|
399
|
+
isError: false
|
|
400
|
+
})
|
|
401
|
+
);
|
|
402
|
+
await validateLiquidTemplateContent(content, {
|
|
403
|
+
getLiquidTags,
|
|
404
|
+
formatMessage,
|
|
405
|
+
messages,
|
|
406
|
+
onError,
|
|
407
|
+
onSuccess,
|
|
408
|
+
tagLookupMap,
|
|
409
|
+
eventContextTags
|
|
410
|
+
});
|
|
411
|
+
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
412
|
+
expect(onError).not.toHaveBeenCalled();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it("calls onSuccess when API returns tag inside {% %} blocks but no errors", async () => {
|
|
416
|
+
const content = '{% for x in some.unsupported %} {{ x }} {% endfor %}';
|
|
417
|
+
const getLiquidTags = jest.fn((content, cb) =>
|
|
418
|
+
cb({ askAiraResponse: { errors: [], data: [{ name: "some.unsupported" }] }, isError: false })
|
|
419
|
+
);
|
|
420
|
+
await validateLiquidTemplateContent(content, {
|
|
421
|
+
getLiquidTags,
|
|
422
|
+
formatMessage,
|
|
423
|
+
messages,
|
|
424
|
+
onError,
|
|
425
|
+
onSuccess,
|
|
426
|
+
tagLookupMap,
|
|
427
|
+
eventContextTags
|
|
428
|
+
});
|
|
429
|
+
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
430
|
+
expect(onError).not.toHaveBeenCalled();
|
|
383
431
|
});
|
|
384
432
|
|
|
385
|
-
it("
|
|
433
|
+
it("calls onSuccess when API returns tags with dots in {% %} syntax but no errors", async () => {
|
|
386
434
|
const content = '{% assign myVar = order.items %} Some text';
|
|
387
435
|
const getLiquidTags = jest.fn((content, cb) =>
|
|
388
436
|
cb({ askAiraResponse: { errors: [], data: [{ name: "order.items" }] }, isError: false })
|
|
@@ -396,7 +444,6 @@ describe("validateLiquidTemplateContent", () => {
|
|
|
396
444
|
tagLookupMap,
|
|
397
445
|
eventContextTags
|
|
398
446
|
});
|
|
399
|
-
// order.items appears in {% %} syntax, so it should be skipped
|
|
400
447
|
expect(onSuccess).toHaveBeenCalledWith(content, undefined);
|
|
401
448
|
expect(onError).not.toHaveBeenCalled();
|
|
402
449
|
});
|