@capillarytech/creatives-library 8.0.288 → 8.0.290-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/initialState.js +0 -2
- package/package.json +1 -1
- package/utils/common.js +5 -8
- package/utils/commonUtils.js +4 -85
- package/utils/tagValidations.js +84 -222
- package/utils/tests/commonUtil.test.js +461 -118
- package/utils/tests/tagValidations.test.js +280 -358
- package/v2Components/ErrorInfoNote/index.js +2 -5
- package/v2Components/FormBuilder/index.js +78 -161
- package/v2Components/FormBuilder/messages.js +0 -8
- package/v2Components/HtmlEditor/HTMLEditor.js +0 -5
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +0 -15
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +1 -2
- package/v2Containers/Cap/mockData.js +0 -14
- package/v2Containers/Cap/reducer.js +3 -55
- package/v2Containers/Cap/tests/reducer.test.js +0 -102
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
- package/v2Containers/CreativesContainer/index.js +19 -6
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +10 -62
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +12 -115
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/FTP/messages.js +0 -4
- package/v2Containers/InApp/index.js +1 -96
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InappAdvance/index.js +2 -103
- package/v2Containers/Line/Container/Text/index.js +0 -1
- package/v2Containers/MobilePush/Create/index.js +6 -16
- package/v2Containers/MobilePush/Edit/index.js +6 -16
- package/v2Containers/MobilePushNew/index.js +2 -33
- package/v2Containers/Rcs/index.js +12 -37
- package/v2Containers/Sms/Create/index.js +31 -3
- package/v2Containers/Sms/Create/messages.js +4 -0
- package/v2Containers/Sms/Edit/index.js +29 -3
- package/v2Containers/Sms/commonMethods.js +6 -6
- package/v2Containers/SmsTrai/Edit/index.js +6 -47
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/Templates/reducer.js +3 -1
- package/v2Containers/Templates/tests/reducer.test.js +12 -0
- package/v2Containers/Viber/index.js +0 -1
- package/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
- package/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
- 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/preview/tests/NotificationContainer.test.js +269 -0
- package/v2Containers/WebPush/Create/utils/validation.js +17 -2
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -24
- package/v2Containers/Whatsapp/index.js +9 -17
- package/v2Containers/Zalo/index.js +3 -11
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import '@testing-library/jest-dom';
|
|
2
2
|
import {
|
|
3
|
-
checkSupport,
|
|
4
|
-
extractNames,
|
|
5
3
|
getTagMapValue,
|
|
6
4
|
getLoyaltyTagsMapValue,
|
|
7
5
|
getForwardedMapValues,
|
|
@@ -9,10 +7,8 @@ import {
|
|
|
9
7
|
validateIfTagClosed,
|
|
10
8
|
validateTags,
|
|
11
9
|
skipTags,
|
|
12
|
-
isInsideLiquidBlock,
|
|
13
10
|
} from '../tagValidations';
|
|
14
11
|
import { containsBase64Images } from '../content';
|
|
15
|
-
import { eventContextTags } from '../../v2Containers/TagList/tests/mockdata';
|
|
16
12
|
|
|
17
13
|
describe("check if curly brackets are balanced", () => {
|
|
18
14
|
it("test for balanced curly brackets", () => {
|
|
@@ -64,53 +60,41 @@ describe("validateTags", () => {
|
|
|
64
60
|
it("should return valid response when all tags are present", () => {
|
|
65
61
|
const content = "Hello {{tag1}}, {{tag2}}, {{tag3}} {{entryTrigger.lifetimePurchases}}";
|
|
66
62
|
|
|
67
|
-
const injectedTagsParams = [];
|
|
68
63
|
const location = { query: { module: "DEFAULT" } };
|
|
69
64
|
const tagModule = null;
|
|
70
65
|
|
|
71
66
|
const result = validateTags({
|
|
72
67
|
content,
|
|
73
68
|
tagsParam,
|
|
74
|
-
injectedTagsParams,
|
|
75
69
|
location,
|
|
76
70
|
tagModule,
|
|
77
|
-
eventContextTags,
|
|
78
71
|
});
|
|
79
72
|
|
|
80
73
|
expect(result.valid).toEqual(true);
|
|
81
74
|
expect(result.missingTags).toEqual([]);
|
|
82
|
-
expect(result.unsupportedTags).toEqual([]);
|
|
83
75
|
expect(result.isBraceError).toEqual(false);
|
|
84
76
|
});
|
|
85
77
|
|
|
86
|
-
it("should return
|
|
78
|
+
it("should return valid response when content has balanced braces and all tags present", () => {
|
|
87
79
|
const content = "Hello {{tag1}}, {{tag2}}, {{tag3}}";
|
|
88
|
-
const updatedTagsParam = [...tagsParam, {
|
|
89
|
-
definition: {
|
|
90
|
-
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
91
|
-
value: "tag4",
|
|
92
|
-
},
|
|
93
|
-
},];
|
|
94
|
-
const injectedTagsParams = [];
|
|
95
80
|
const location = { query: { module: "DEFAULT" } };
|
|
96
81
|
const tagModule = null;
|
|
97
82
|
|
|
98
83
|
const result = validateTags({
|
|
99
84
|
content,
|
|
100
|
-
|
|
101
|
-
injectedTagsParams,
|
|
85
|
+
tagsParam,
|
|
102
86
|
location,
|
|
103
87
|
tagModule,
|
|
104
88
|
});
|
|
105
89
|
|
|
106
90
|
expect(result.valid).toEqual(true);
|
|
107
|
-
expect(result.
|
|
91
|
+
expect(result.missingTags).toEqual([]);
|
|
108
92
|
expect(result.isBraceError).toEqual(false);
|
|
109
93
|
});
|
|
110
94
|
|
|
111
|
-
it("should return
|
|
95
|
+
it("should return valid response when content has balanced braces with missingEventContextTags and partial tagsParam", () => {
|
|
112
96
|
const content = "Hello {{tag1}}, {{tag2}}, {{tag3}} {{missingEventContextTags}}";
|
|
113
|
-
const
|
|
97
|
+
const tagsParamLocal = [
|
|
114
98
|
{
|
|
115
99
|
definition: {
|
|
116
100
|
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
@@ -124,27 +108,24 @@ describe("validateTags", () => {
|
|
|
124
108
|
},
|
|
125
109
|
},
|
|
126
110
|
];
|
|
127
|
-
const injectedTagsParams = [];
|
|
128
111
|
const location = { query: { module: "DEFAULT" } };
|
|
129
112
|
const tagModule = null;
|
|
130
113
|
|
|
131
114
|
const result = validateTags({
|
|
132
115
|
content,
|
|
133
|
-
tagsParam,
|
|
134
|
-
injectedTagsParams,
|
|
116
|
+
tagsParam: tagsParamLocal,
|
|
135
117
|
location,
|
|
136
118
|
tagModule,
|
|
137
119
|
});
|
|
138
120
|
|
|
139
121
|
expect(result.valid).toEqual(true);
|
|
140
122
|
expect(result.missingTags).toEqual([]);
|
|
141
|
-
expect(result.unsupportedTags).toEqual(["tag3", "missingEventContextTags"]);
|
|
142
123
|
expect(result.isBraceError).toEqual(false);
|
|
143
124
|
});
|
|
144
125
|
|
|
145
126
|
it("should return invalid response when there is an unbalanced bracket error", () => {
|
|
146
127
|
const content = "Hello {{tag1}, {{tag2}}, {{tag3}}";
|
|
147
|
-
const
|
|
128
|
+
const tagsParamLocal = [
|
|
148
129
|
{
|
|
149
130
|
definition: {
|
|
150
131
|
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
@@ -164,26 +145,23 @@ describe("validateTags", () => {
|
|
|
164
145
|
},
|
|
165
146
|
},
|
|
166
147
|
];
|
|
167
|
-
const injectedTagsParams = [];
|
|
168
148
|
const location = { query: { module: "DEFAULT" } };
|
|
169
149
|
const tagModule = null;
|
|
170
150
|
|
|
171
151
|
const result = validateTags({
|
|
172
152
|
content,
|
|
173
|
-
tagsParam,
|
|
174
|
-
injectedTagsParams,
|
|
153
|
+
tagsParam: tagsParamLocal,
|
|
175
154
|
location,
|
|
176
155
|
tagModule,
|
|
177
156
|
});
|
|
178
157
|
|
|
179
158
|
expect(result.valid).toEqual(false);
|
|
180
|
-
expect(result.missingTags).toEqual([
|
|
181
|
-
expect(result.unsupportedTags).toEqual([]);
|
|
159
|
+
expect(result.missingTags).toEqual([]);
|
|
182
160
|
expect(result.isBraceError).toEqual(true);
|
|
183
161
|
});
|
|
184
162
|
|
|
185
|
-
it("should
|
|
186
|
-
const
|
|
163
|
+
it("should require unsubscribe when mandatory and missing, and accept skipped unsubscribe variant", () => {
|
|
164
|
+
const tagsParamUnsubscribe = [
|
|
187
165
|
{
|
|
188
166
|
definition: {
|
|
189
167
|
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
@@ -191,288 +169,310 @@ describe("validateTags", () => {
|
|
|
191
169
|
},
|
|
192
170
|
},
|
|
193
171
|
];
|
|
194
|
-
// Content does not include {{unsubscribe}}, so it would be missing
|
|
195
|
-
const contentMissing = "Hello {{tag1}}";
|
|
196
|
-
const injectedTagsParams = [];
|
|
197
172
|
const location = { query: { module: "DEFAULT" } };
|
|
198
173
|
const tagModule = null;
|
|
199
174
|
|
|
200
|
-
|
|
175
|
+
const contentMissing = "Hello world";
|
|
201
176
|
const resultMissing = validateTags({
|
|
202
177
|
content: contentMissing,
|
|
203
|
-
tagsParam,
|
|
204
|
-
injectedTagsParams,
|
|
178
|
+
tagsParam: tagsParamUnsubscribe,
|
|
205
179
|
location,
|
|
206
180
|
tagModule,
|
|
207
181
|
});
|
|
208
182
|
expect(resultMissing.missingTags).toContain("unsubscribe");
|
|
183
|
+
expect(resultMissing.valid).toBe(false);
|
|
209
184
|
|
|
210
|
-
// Now, content includes a tag that triggers skipTags logic for unsubscribe
|
|
211
|
-
// e.g., {{unsubscribe(#123456)}} matches the skipTags regex
|
|
212
185
|
const contentWithSkippedUnsubscribe = "Hello {{unsubscribe(#123456)}}";
|
|
213
186
|
const resultSkipped = validateTags({
|
|
214
187
|
content: contentWithSkippedUnsubscribe,
|
|
215
|
-
tagsParam,
|
|
216
|
-
injectedTagsParams,
|
|
188
|
+
tagsParam: tagsParamUnsubscribe,
|
|
217
189
|
location,
|
|
218
190
|
tagModule,
|
|
219
191
|
});
|
|
220
192
|
expect(resultSkipped.missingTags).not.toContain("unsubscribe");
|
|
221
193
|
expect(resultSkipped.valid).toBe(true);
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
194
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
195
|
+
const contentWithWhitespaceUnsubscribe = "Hello {{ unsubscribe }}";
|
|
196
|
+
const resultWhitespace = validateTags({
|
|
197
|
+
content: contentWithWhitespaceUnsubscribe,
|
|
198
|
+
tagsParam: tagsParamUnsubscribe,
|
|
199
|
+
location,
|
|
200
|
+
tagModule,
|
|
201
|
+
});
|
|
202
|
+
expect(resultWhitespace.missingTags).not.toContain("unsubscribe");
|
|
203
|
+
expect(resultWhitespace.valid).toBe(true);
|
|
204
|
+
expect(resultWhitespace.unsupportedTags ?? []).toEqual([]);
|
|
230
205
|
});
|
|
206
|
+
});
|
|
231
207
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
name: "Node 1.1",
|
|
239
|
-
children: [],
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
name: "Node 1.2",
|
|
243
|
-
children: [],
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
name: "Node 2",
|
|
249
|
-
children: [
|
|
250
|
-
{
|
|
251
|
-
name: "Node 2.1",
|
|
252
|
-
children: [],
|
|
253
|
-
},
|
|
254
|
-
],
|
|
208
|
+
describe('validateTags wrapper (v2 consumers)', () => {
|
|
209
|
+
const tagsWithUnsubscribe = [
|
|
210
|
+
{
|
|
211
|
+
definition: {
|
|
212
|
+
supportedModules: [{ context: 'DEFAULT', mandatory: true }],
|
|
213
|
+
value: 'unsubscribe',
|
|
255
214
|
},
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
"Node 2.1",
|
|
264
|
-
]);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it("should ignore nodes without names", () => {
|
|
268
|
-
const data = [
|
|
269
|
-
{
|
|
270
|
-
name: "Node 1",
|
|
271
|
-
children: [
|
|
272
|
-
{
|
|
273
|
-
name: "Node 1.1",
|
|
274
|
-
children: [],
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
children: [],
|
|
278
|
-
},
|
|
279
|
-
],
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
const tagsOutboundUnsubscribe = [
|
|
218
|
+
{
|
|
219
|
+
definition: {
|
|
220
|
+
supportedModules: [{ context: 'outbound', mandatory: true }],
|
|
221
|
+
value: 'unsubscribe',
|
|
280
222
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
{
|
|
289
|
-
name: "Node 2.2",
|
|
290
|
-
},
|
|
291
|
-
],
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
const tagsDefaultLowercase = [
|
|
226
|
+
{
|
|
227
|
+
definition: {
|
|
228
|
+
supportedModules: [{ context: 'default', mandatory: true }],
|
|
229
|
+
value: 'unsubscribe',
|
|
292
230
|
},
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
expect(result).toEqual([
|
|
296
|
-
"Node 1",
|
|
297
|
-
"Node 1.1",
|
|
298
|
-
"Node 2",
|
|
299
|
-
"Node 2.1",
|
|
300
|
-
"Node 2.2",
|
|
301
|
-
]);
|
|
302
|
-
});
|
|
303
|
-
});
|
|
231
|
+
},
|
|
232
|
+
];
|
|
304
233
|
|
|
234
|
+
describe('module selection (location.query.module vs tagModule vs DEFAULT)', () => {
|
|
235
|
+
it('uses location.query.module when set and tagModule not provided', () => {
|
|
236
|
+
const content = 'Hello world';
|
|
237
|
+
const result = validateTags({
|
|
238
|
+
content,
|
|
239
|
+
tagsParam: tagsWithUnsubscribe,
|
|
240
|
+
location: { query: { module: 'DEFAULT' } },
|
|
241
|
+
tagModule: null,
|
|
242
|
+
});
|
|
243
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
244
|
+
expect(result.valid).toBe(false);
|
|
245
|
+
});
|
|
305
246
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
247
|
+
it('uses tagModule override when provided (overrides location.query.module)', () => {
|
|
248
|
+
const content = 'Hello world';
|
|
249
|
+
const resultWithOutbound = validateTags({
|
|
250
|
+
content,
|
|
251
|
+
tagsParam: tagsOutboundUnsubscribe,
|
|
252
|
+
location: { query: { module: 'DEFAULT' } },
|
|
253
|
+
tagModule: 'outbound',
|
|
254
|
+
});
|
|
255
|
+
expect(resultWithOutbound.missingTags).toContain('unsubscribe');
|
|
256
|
+
expect(resultWithOutbound.valid).toBe(false);
|
|
257
|
+
|
|
258
|
+
const resultWithDefault = validateTags({
|
|
259
|
+
content,
|
|
260
|
+
tagsParam: tagsWithUnsubscribe,
|
|
261
|
+
location: { query: { module: 'outbound' } },
|
|
262
|
+
tagModule: 'DEFAULT',
|
|
263
|
+
});
|
|
264
|
+
expect(resultWithDefault.missingTags).toContain('unsubscribe');
|
|
265
|
+
expect(resultWithDefault.valid).toBe(false);
|
|
266
|
+
});
|
|
317
267
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
268
|
+
it('uses DEFAULT (lowercase) when location is undefined', () => {
|
|
269
|
+
const content = 'Hello world';
|
|
270
|
+
const result = validateTags({
|
|
271
|
+
content,
|
|
272
|
+
tagsParam: tagsDefaultLowercase,
|
|
273
|
+
location: undefined,
|
|
274
|
+
tagModule: null,
|
|
275
|
+
});
|
|
276
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
277
|
+
expect(result.valid).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('uses DEFAULT when location.query is undefined', () => {
|
|
281
|
+
const content = 'Hello world';
|
|
282
|
+
const result = validateTags({
|
|
283
|
+
content,
|
|
284
|
+
tagsParam: tagsDefaultLowercase,
|
|
285
|
+
location: {},
|
|
286
|
+
tagModule: null,
|
|
287
|
+
});
|
|
288
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
289
|
+
expect(result.valid).toBe(false);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('uses DEFAULT when location.query.module is falsy', () => {
|
|
293
|
+
const content = 'Hello world';
|
|
294
|
+
const result = validateTags({
|
|
295
|
+
content,
|
|
296
|
+
tagsParam: tagsDefaultLowercase,
|
|
297
|
+
location: { query: { module: '' } },
|
|
298
|
+
tagModule: null,
|
|
299
|
+
});
|
|
300
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
301
|
+
expect(result.valid).toBe(false);
|
|
302
|
+
});
|
|
325
303
|
});
|
|
326
304
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
305
|
+
describe('content passed to contentForBraceCheck and contentForUnsubscribeScan', () => {
|
|
306
|
+
it('uses same content for brace check (isBraceError when unbalanced)', () => {
|
|
307
|
+
const content = 'Hello {{tag1}, {{tag2}}';
|
|
308
|
+
const result = validateTags({
|
|
309
|
+
content,
|
|
310
|
+
tagsParam: [],
|
|
311
|
+
location: { query: { module: 'DEFAULT' } },
|
|
312
|
+
});
|
|
313
|
+
expect(result.isBraceError).toBe(true);
|
|
314
|
+
expect(result.valid).toBe(false);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('uses same content for unsubscribe scan (missing unsubscribe when required)', () => {
|
|
318
|
+
const content = 'Hello {{other}}';
|
|
319
|
+
const result = validateTags({
|
|
320
|
+
content,
|
|
321
|
+
tagsParam: tagsWithUnsubscribe,
|
|
322
|
+
location: { query: { module: 'DEFAULT' } },
|
|
323
|
+
});
|
|
324
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
325
|
+
expect(result.valid).toBe(false);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('content with {{ unsubscribe }} satisfies unsubscribe requirement', () => {
|
|
329
|
+
const content = 'Hello {{ unsubscribe }}';
|
|
330
|
+
const result = validateTags({
|
|
331
|
+
content,
|
|
332
|
+
tagsParam: tagsWithUnsubscribe,
|
|
333
|
+
location: { query: { module: 'DEFAULT' } },
|
|
334
|
+
});
|
|
335
|
+
expect(result.missingTags).not.toContain('unsubscribe');
|
|
336
|
+
expect(result.valid).toBe(true);
|
|
337
|
+
});
|
|
334
338
|
});
|
|
335
339
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
340
|
+
describe('isFullMode', () => {
|
|
341
|
+
it('when true skips unsubscribe check (no missingTags for mandatory unsubscribe)', () => {
|
|
342
|
+
const content = 'Hello world';
|
|
343
|
+
const result = validateTags({
|
|
344
|
+
content,
|
|
345
|
+
tagsParam: tagsWithUnsubscribe,
|
|
346
|
+
location: { query: { module: 'DEFAULT' } },
|
|
347
|
+
isFullMode: true,
|
|
348
|
+
});
|
|
349
|
+
expect(result.missingTags).toEqual([]);
|
|
350
|
+
expect(result.valid).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('when true still runs brace check', () => {
|
|
354
|
+
const content = 'Hello {{tag1}';
|
|
355
|
+
const result = validateTags({
|
|
356
|
+
content,
|
|
357
|
+
tagsParam: tagsWithUnsubscribe,
|
|
358
|
+
location: { query: { module: 'DEFAULT' } },
|
|
359
|
+
isFullMode: true,
|
|
360
|
+
});
|
|
361
|
+
expect(result.isBraceError).toBe(true);
|
|
362
|
+
expect(result.valid).toBe(false);
|
|
363
|
+
});
|
|
347
364
|
});
|
|
348
365
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
|
|
366
|
+
describe('tagsParam null or empty', () => {
|
|
367
|
+
it('when null: only brace check runs, no missing-tag logic', () => {
|
|
368
|
+
const content = 'Hello {{a}}';
|
|
369
|
+
const result = validateTags({
|
|
370
|
+
content,
|
|
371
|
+
tagsParam: null,
|
|
372
|
+
location: { query: { module: 'DEFAULT' } },
|
|
373
|
+
});
|
|
374
|
+
expect(result.missingTags).toEqual([]);
|
|
375
|
+
expect(result.isBraceError).toBe(false);
|
|
376
|
+
expect(result.valid).toBe(true);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('when empty array: only brace check runs', () => {
|
|
380
|
+
const content = 'Hello {{a}}';
|
|
381
|
+
const result = validateTags({
|
|
382
|
+
content,
|
|
383
|
+
tagsParam: [],
|
|
384
|
+
location: { query: { module: 'DEFAULT' } },
|
|
385
|
+
});
|
|
386
|
+
expect(result.missingTags).toEqual([]);
|
|
387
|
+
expect(result.valid).toBe(true);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('when null with unbalanced braces: returns isBraceError', () => {
|
|
391
|
+
const content = 'Hello {{a}';
|
|
392
|
+
const result = validateTags({
|
|
393
|
+
content,
|
|
394
|
+
tagsParam: null,
|
|
395
|
+
location: { query: { module: 'DEFAULT' } },
|
|
396
|
+
});
|
|
397
|
+
expect(result.isBraceError).toBe(true);
|
|
398
|
+
expect(result.valid).toBe(false);
|
|
399
|
+
expect(result.missingTags).toEqual([]);
|
|
400
|
+
});
|
|
363
401
|
});
|
|
364
402
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
},
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
it("should not add childName to supportedList which does not have dot in eventContextTags", () => {
|
|
440
|
-
// This case is unlikely to happen as we are not supporting tags without dot in eventContextTags
|
|
441
|
-
const response = { data: [{ name: "entryTrigger.lifetimePoints", children: [{name: "userId"}] }]};
|
|
442
|
-
const tagObject = {};
|
|
443
|
-
const eventContextTags = [{ tagName: "entryTrigger.lifetimePoints", children: [{name: "userId"}] }];
|
|
444
|
-
const isLiquidFlow = true;
|
|
445
|
-
const result = checkSupport(response, tagObject, eventContextTags, isLiquidFlow);
|
|
446
|
-
expect(result).toEqual( [ "entryTrigger.lifetimePoints" ]);
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it("should add only parent tag to supportedList if isLiquidFlow false", () => {
|
|
450
|
-
const response = { data: [{ name: "leaderboard", children: [{name: "person.userId"}]}]};
|
|
451
|
-
const tagObject = {};
|
|
452
|
-
const eventContextTags = [{ tagName: "leaderboard", subTags: ["userId"]}];
|
|
453
|
-
const isLiquidFlow = false;
|
|
454
|
-
const result = checkSupport(response, tagObject, eventContextTags, isLiquidFlow);
|
|
455
|
-
expect(result).toEqual( [ 'leaderboard' ]);
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
it("test for checking loyalty tags in that are coming in forwardedTags", () => {
|
|
459
|
-
const response = { data: [{ name: "leaderboard", children: [{name: "person.userId"}]}]};
|
|
460
|
-
const tagObject = {};
|
|
461
|
-
const isLiquidFlow = true;
|
|
462
|
-
// forwardedTags contains tag hierarchy with parent tags and their subtags
|
|
463
|
-
// needed for loyalty email liquid tag resolution
|
|
464
|
-
const forwardedTags = {
|
|
465
|
-
leaderboard: {
|
|
466
|
-
name: "Leaderboard",
|
|
467
|
-
subtags: {
|
|
468
|
-
"person.userId": {
|
|
469
|
-
name: "User ID",
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
const result = checkSupport(response, tagObject, [], isLiquidFlow, forwardedTags);
|
|
475
|
-
expect(result).toEqual( [ 'leaderboard', 'person.userId' ]);
|
|
403
|
+
describe('v2 consumer call patterns', () => {
|
|
404
|
+
it('Whatsapp-style: content, tagsParam, location, tagModule (getDefaultTags), isFullMode', () => {
|
|
405
|
+
const content = 'Hello {{ unsubscribe }}';
|
|
406
|
+
const result = validateTags({
|
|
407
|
+
content: content,
|
|
408
|
+
tagsParam: tagsWithUnsubscribe,
|
|
409
|
+
location: { query: { module: 'DEFAULT' } },
|
|
410
|
+
tagModule: 'DEFAULT',
|
|
411
|
+
isFullMode: false,
|
|
412
|
+
});
|
|
413
|
+
expect(result.valid).toBe(true);
|
|
414
|
+
expect(result.isBraceError).toBe(false);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('Zalo / Rcs / MobilePushNew / EmailWrapper: same pattern as Whatsapp', () => {
|
|
418
|
+
const result = validateTags({
|
|
419
|
+
content: 'Hi {{ unsubscribe }}',
|
|
420
|
+
tagsParam: tagsWithUnsubscribe,
|
|
421
|
+
location: { query: { module: 'DEFAULT' } },
|
|
422
|
+
tagModule: 'DEFAULT',
|
|
423
|
+
isFullMode: false,
|
|
424
|
+
});
|
|
425
|
+
expect(result.valid).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('Line / Viber: tagModule "outbound"', () => {
|
|
429
|
+
const contentMissing = 'Hello';
|
|
430
|
+
const resultMissing = validateTags({
|
|
431
|
+
content: contentMissing,
|
|
432
|
+
tagsParam: tagsOutboundUnsubscribe,
|
|
433
|
+
location: { query: { module: 'inbound' } },
|
|
434
|
+
tagModule: 'outbound',
|
|
435
|
+
isFullMode: false,
|
|
436
|
+
});
|
|
437
|
+
expect(resultMissing.missingTags).toContain('unsubscribe');
|
|
438
|
+
expect(resultMissing.valid).toBe(false);
|
|
439
|
+
|
|
440
|
+
const resultOk = validateTags({
|
|
441
|
+
content: 'Hello {{ unsubscribe }}',
|
|
442
|
+
tagsParam: tagsOutboundUnsubscribe,
|
|
443
|
+
location: {},
|
|
444
|
+
tagModule: 'outbound',
|
|
445
|
+
isFullMode: false,
|
|
446
|
+
});
|
|
447
|
+
expect(resultOk.valid).toBe(true);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('WebPush: validationConfig spread (content + tagsParam, location, tagModule, isFullMode)', () => {
|
|
451
|
+
const validationConfig = {
|
|
452
|
+
tagsParam: tagsWithUnsubscribe,
|
|
453
|
+
location: { query: { module: 'DEFAULT' } },
|
|
454
|
+
tagModule: 'DEFAULT',
|
|
455
|
+
};
|
|
456
|
+
const result = validateTags({
|
|
457
|
+
content: 'Hello {{ unsubscribe }}',
|
|
458
|
+
...validationConfig,
|
|
459
|
+
isFullMode: false,
|
|
460
|
+
});
|
|
461
|
+
expect(result.valid).toBe(true);
|
|
462
|
+
expect(result.isBraceError).toBe(false);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('WebPush with isFullMode: only brace check', () => {
|
|
466
|
+
const result = validateTags({
|
|
467
|
+
content: 'Hello world',
|
|
468
|
+
tagsParam: tagsWithUnsubscribe,
|
|
469
|
+
location: { query: { module: 'DEFAULT' } },
|
|
470
|
+
tagModule: 'DEFAULT',
|
|
471
|
+
isFullMode: true,
|
|
472
|
+
});
|
|
473
|
+
expect(result.valid).toBe(true);
|
|
474
|
+
expect(result.missingTags).toEqual([]);
|
|
475
|
+
});
|
|
476
476
|
});
|
|
477
477
|
});
|
|
478
478
|
|
|
@@ -1309,84 +1309,6 @@ describe('getForwardedMapValues', () => {
|
|
|
1309
1309
|
});
|
|
1310
1310
|
});
|
|
1311
1311
|
|
|
1312
|
-
describe('isInsideLiquidBlock', () => {
|
|
1313
|
-
it('returns true for index inside a single block', () => {
|
|
1314
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1315
|
-
// Index of 'a' in 'assign' inside the block
|
|
1316
|
-
const tagIndex = content.indexOf('assign');
|
|
1317
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1318
|
-
});
|
|
1319
|
-
|
|
1320
|
-
it('returns false for index outside any block', () => {
|
|
1321
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1322
|
-
// Index of 'H' in 'Hello'
|
|
1323
|
-
expect(isInsideLiquidBlock(content, 0)).toBe(false);
|
|
1324
|
-
// Index of 'W' in 'World'
|
|
1325
|
-
expect(isInsideLiquidBlock(content, content.indexOf('World'))).toBe(false);
|
|
1326
|
-
});
|
|
1327
|
-
|
|
1328
|
-
it('returns true for index at the start of a block', () => {
|
|
1329
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1330
|
-
// Index of '{' in '{%'
|
|
1331
|
-
const tagIndex = content.indexOf('{%');
|
|
1332
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1333
|
-
});
|
|
1334
|
-
|
|
1335
|
-
it('returns false for index at the end of a block (exclusive)', () => {
|
|
1336
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1337
|
-
// Index just after the closing '%}'
|
|
1338
|
-
const blockEnd = content.indexOf('%}') + 2;
|
|
1339
|
-
expect(isInsideLiquidBlock(content, blockEnd)).toBe(false);
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
|
-
it('returns true for index inside the second of multiple blocks', () => {
|
|
1343
|
-
const content = 'A {% first %} B {% second %} C';
|
|
1344
|
-
const tagIndex = content.indexOf('second');
|
|
1345
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
it('returns false for index between blocks', () => {
|
|
1349
|
-
const content = 'A {% first %} B {% second %} C';
|
|
1350
|
-
// Index of 'B' (between blocks)
|
|
1351
|
-
const tagIndex = content.indexOf('B');
|
|
1352
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(false);
|
|
1353
|
-
});
|
|
1354
|
-
|
|
1355
|
-
it('returns false for empty string', () => {
|
|
1356
|
-
expect(isInsideLiquidBlock('', 0)).toBe(false);
|
|
1357
|
-
});
|
|
1358
|
-
|
|
1359
|
-
it('returns false if there are no blocks', () => {
|
|
1360
|
-
const content = 'Just some text with no blocks';
|
|
1361
|
-
expect(isInsideLiquidBlock(content, 5)).toBe(false);
|
|
1362
|
-
});
|
|
1363
|
-
|
|
1364
|
-
it('returns false for negative index', () => {
|
|
1365
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1366
|
-
expect(isInsideLiquidBlock(content, -1)).toBe(false);
|
|
1367
|
-
});
|
|
1368
|
-
|
|
1369
|
-
it('returns false for index beyond string length', () => {
|
|
1370
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1371
|
-
expect(isInsideLiquidBlock(content, 100)).toBe(false);
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
it('works for nested-like blocks (not truly nested)', () => {
|
|
1375
|
-
const content = 'A {% outer {% inner %} outer %} B';
|
|
1376
|
-
// Index of 'inner' (should be inside the first block)
|
|
1377
|
-
const tagIndex = content.indexOf('inner');
|
|
1378
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1379
|
-
});
|
|
1380
|
-
|
|
1381
|
-
it('returns true for index at last char inside block', () => {
|
|
1382
|
-
const content = 'A {% foo %} B';
|
|
1383
|
-
// Index of last char inside block (just before %})
|
|
1384
|
-
const blockStart = content.indexOf('{%');
|
|
1385
|
-
const blockEnd = content.indexOf('%}');
|
|
1386
|
-
expect(isInsideLiquidBlock(content, blockEnd - 1)).toBe(true);
|
|
1387
|
-
});
|
|
1388
|
-
});
|
|
1389
|
-
|
|
1390
1312
|
describe('containsBase64Images', () => {
|
|
1391
1313
|
let mockCapNotification;
|
|
1392
1314
|
let mockCallback;
|