@capillarytech/creatives-library 8.0.310-alpha.0 → 8.0.310

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/constants/unified.js +1 -5
  2. package/initialState.js +2 -0
  3. package/package.json +1 -1
  4. package/services/api.js +0 -17
  5. package/services/tests/api.test.js +0 -85
  6. package/utils/common.js +8 -5
  7. package/utils/commonUtils.js +93 -46
  8. package/utils/tagValidations.js +223 -83
  9. package/utils/tests/commonUtil.test.js +124 -316
  10. package/utils/tests/tagValidations.test.js +358 -441
  11. package/v2Components/CommonTestAndPreview/SendTestMessage.js +49 -78
  12. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -134
  13. package/v2Components/CommonTestAndPreview/actions.js +0 -10
  14. package/v2Components/CommonTestAndPreview/constants.js +1 -15
  15. package/v2Components/CommonTestAndPreview/index.js +19 -80
  16. package/v2Components/CommonTestAndPreview/messages.js +0 -94
  17. package/v2Components/CommonTestAndPreview/reducer.js +0 -10
  18. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -53
  19. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
  20. package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -36
  21. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
  22. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -377
  23. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
  24. package/v2Components/ErrorInfoNote/index.js +5 -2
  25. package/v2Components/FormBuilder/index.js +203 -137
  26. package/v2Components/FormBuilder/messages.js +8 -0
  27. package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
  28. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
  30. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
  31. package/v2Containers/Cap/mockData.js +14 -0
  32. package/v2Containers/Cap/reducer.js +55 -3
  33. package/v2Containers/Cap/tests/reducer.test.js +102 -0
  34. package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -5
  35. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
  36. package/v2Containers/CreativesContainer/constants.js +0 -6
  37. package/v2Containers/CreativesContainer/index.js +7 -47
  38. package/v2Containers/Email/index.js +5 -1
  39. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
  40. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +120 -20
  41. package/v2Containers/FTP/index.js +51 -2
  42. package/v2Containers/FTP/messages.js +4 -0
  43. package/v2Containers/InApp/index.js +107 -35
  44. package/v2Containers/InApp/tests/index.test.js +6 -17
  45. package/v2Containers/InappAdvance/index.js +112 -4
  46. package/v2Containers/InappAdvance/tests/index.test.js +0 -2
  47. package/v2Containers/Line/Container/Text/index.js +1 -0
  48. package/v2Containers/MobilePush/Create/index.js +19 -59
  49. package/v2Containers/MobilePush/Edit/index.js +20 -48
  50. package/v2Containers/MobilePushNew/index.js +32 -12
  51. package/v2Containers/MobilepushWrapper/index.js +1 -3
  52. package/v2Containers/Rcs/index.js +37 -12
  53. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1276 -1408
  54. package/v2Containers/Sms/Create/index.js +3 -39
  55. package/v2Containers/Sms/Create/messages.js +0 -4
  56. package/v2Containers/Sms/Edit/index.js +3 -35
  57. package/v2Containers/Sms/commonMethods.js +6 -3
  58. package/v2Containers/SmsTrai/Edit/index.js +47 -11
  59. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +294 -327
  60. package/v2Containers/SmsWrapper/index.js +0 -2
  61. package/v2Containers/TemplatesV2/index.js +13 -28
  62. package/v2Containers/Viber/index.js +1 -0
  63. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
  64. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
  65. package/v2Containers/WebPush/Create/index.js +2 -2
  66. package/v2Containers/WebPush/Create/utils/validation.js +8 -17
  67. package/v2Containers/WebPush/Create/utils/validation.test.js +24 -44
  68. package/v2Containers/Whatsapp/index.js +17 -9
  69. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5246
  70. package/v2Containers/Zalo/index.js +11 -3
  71. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
  72. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -284
  73. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -72
  74. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
  75. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -657
  76. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +0 -172
  77. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -466
  78. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
  79. package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
@@ -8,11 +8,6 @@ import {
8
8
  validateCarouselCards,
9
9
  hasPersonalizationTags,
10
10
  checkForPersonalizationTokens,
11
- isValidEmail,
12
- isValidMobile,
13
- formatPhoneNumber,
14
- getMessageForDevice,
15
- getTitleForDevice,
16
11
  } from "../commonUtils";
17
12
  import { skipTags } from "../tagValidations";
18
13
  import { SMS_TRAI_VAR } from '../../v2Containers/SmsTrai/Edit/constants';
@@ -27,17 +22,13 @@ describe("validateLiquidTemplateContent", () => {
27
22
  somethingWentWrong: { id: "wrong" },
28
23
  unsupportedTagsValidationError: { id: "unsupported" }
29
24
  };
25
+ const tagLookupMap = { foo: true };
30
26
  const eventContextTags = [{ tagName: "bar" }];
31
27
  const onError = jest.fn();
32
28
  const onSuccess = jest.fn();
33
29
 
34
30
  beforeEach(() => {
35
31
  jest.clearAllMocks();
36
- formatMessage.mockImplementation((msg, vars) =>
37
- vars && vars.unsupportedTags != null
38
- ? `${msg?.id}:${vars.unsupportedTags}`
39
- : (msg?.id ?? "unsupported")
40
- );
41
32
  });
42
33
 
43
34
  it("calls onError for empty content", async () => {
@@ -50,10 +41,11 @@ describe("validateLiquidTemplateContent", () => {
50
41
  messages,
51
42
  onError,
52
43
  onSuccess,
44
+ tagLookupMap,
53
45
  eventContextTags
54
46
  });
55
47
  expect(onError).toHaveBeenCalledWith({
56
- standardErrors: ["empty"],
48
+ standardErrors: [undefined],
57
49
  liquidErrors: [],
58
50
  tabType: undefined
59
51
  });
@@ -68,6 +60,7 @@ describe("validateLiquidTemplateContent", () => {
68
60
  getLiquidTags,
69
61
  formatMessage,
70
62
  messages,
63
+ tagLookupMap,
71
64
  eventContextTags
72
65
  });
73
66
  expect(onError).not.toHaveBeenCalled();
@@ -84,6 +77,7 @@ describe("validateLiquidTemplateContent", () => {
84
77
  messages,
85
78
  onError,
86
79
  onSuccess,
80
+ tagLookupMap,
87
81
  eventContextTags
88
82
  });
89
83
  expect(onError).toHaveBeenCalledWith({
@@ -107,6 +101,7 @@ describe("validateLiquidTemplateContent", () => {
107
101
  messages,
108
102
  onError,
109
103
  onSuccess,
104
+ tagLookupMap,
110
105
  eventContextTags
111
106
  });
112
107
  expect(onError).toHaveBeenCalledWith({
@@ -129,6 +124,7 @@ describe("validateLiquidTemplateContent", () => {
129
124
  messages,
130
125
  onError,
131
126
  onSuccess,
127
+ tagLookupMap,
132
128
  eventContextTags
133
129
  });
134
130
  expect(onError).toHaveBeenCalledWith({
@@ -149,6 +145,7 @@ describe("validateLiquidTemplateContent", () => {
149
145
  messages,
150
146
  onError,
151
147
  onSuccess,
148
+ tagLookupMap,
152
149
  eventContextTags
153
150
  });
154
151
  expect(onError).toHaveBeenCalledWith({
@@ -169,6 +166,7 @@ describe("validateLiquidTemplateContent", () => {
169
166
  messages,
170
167
  onError,
171
168
  onSuccess,
169
+ tagLookupMap,
172
170
  eventContextTags
173
171
  });
174
172
  expect(onError).toHaveBeenCalledWith({
@@ -199,6 +197,7 @@ describe("validateLiquidTemplateContent", () => {
199
197
  messages,
200
198
  onError,
201
199
  onSuccess,
200
+ tagLookupMap,
202
201
  eventContextTags
203
202
  });
204
203
  expect(onError).toHaveBeenCalledWith({
@@ -219,6 +218,7 @@ describe("validateLiquidTemplateContent", () => {
219
218
  messages,
220
219
  onError,
221
220
  onSuccess,
221
+ tagLookupMap,
222
222
  eventContextTags
223
223
  });
224
224
  expect(onError).toHaveBeenCalledWith({
@@ -228,15 +228,9 @@ describe("validateLiquidTemplateContent", () => {
228
228
  });
229
229
  });
230
230
 
231
- it("calls onError when API returns success but response.errors has validation errors (e.g. unsupported tag)", async () => {
231
+ it("calls onError for unsupported tags", async () => {
232
232
  const getLiquidTags = jest.fn((content, cb) =>
233
- cb({
234
- askAiraResponse: {
235
- errors: [{ message: "Unsupported tag: custom_tag" }],
236
- data: []
237
- },
238
- isError: false
239
- })
233
+ cb({ askAiraResponse: { errors: [], data: [{ name: "baz" }] }, isError: false })
240
234
  );
241
235
  await validateLiquidTemplateContent("foo", {
242
236
  getLiquidTags,
@@ -244,33 +238,18 @@ describe("validateLiquidTemplateContent", () => {
244
238
  messages,
245
239
  onError,
246
240
  onSuccess,
241
+ tagLookupMap,
247
242
  eventContextTags
248
243
  });
249
244
  expect(onError).toHaveBeenCalledWith({
250
245
  standardErrors: [],
251
- liquidErrors: ["Unsupported tag: custom_tag"],
246
+ liquidErrors: [undefined],
252
247
  tabType: undefined
253
248
  });
254
249
  expect(onSuccess).not.toHaveBeenCalled();
255
250
  });
256
251
 
257
- it("calls onSuccess when API returns no errors and a single extracted tag (extracted tags are not validated)", async () => {
258
- const getLiquidTags = jest.fn((content, cb) =>
259
- cb({ askAiraResponse: { errors: [], data: [{ name: "foo" }] }, isError: false })
260
- );
261
- await validateLiquidTemplateContent("foo", {
262
- getLiquidTags,
263
- formatMessage,
264
- messages,
265
- onError,
266
- onSuccess,
267
- eventContextTags
268
- });
269
- expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
270
- expect(onError).not.toHaveBeenCalled();
271
- });
272
-
273
- it("calls onSuccess for valid content when API returns multiple extracted tags but no errors", async () => {
252
+ it("calls onSuccess for valid content", async () => {
274
253
  const getLiquidTags = jest.fn((content, cb) =>
275
254
  cb({ askAiraResponse: { errors: [], data: [{ name: "foo" }, { name: "bar" }] }, isError: false })
276
255
  );
@@ -280,6 +259,7 @@ describe("validateLiquidTemplateContent", () => {
280
259
  messages,
281
260
  onError,
282
261
  onSuccess,
262
+ tagLookupMap,
283
263
  eventContextTags
284
264
  });
285
265
  expect(onSuccess).toHaveBeenCalledWith("foo", undefined);
@@ -295,6 +275,7 @@ describe("validateLiquidTemplateContent", () => {
295
275
  messages,
296
276
  onError,
297
277
  onSuccess,
278
+ tagLookupMap,
298
279
  eventContextTags,
299
280
  skipTags
300
281
  });
@@ -312,6 +293,7 @@ describe("validateLiquidTemplateContent", () => {
312
293
  messages,
313
294
  onError,
314
295
  onSuccess,
296
+ tagLookupMap,
315
297
  eventContextTags,
316
298
  skipTags
317
299
  });
@@ -329,6 +311,7 @@ describe("validateLiquidTemplateContent", () => {
329
311
  messages,
330
312
  onError,
331
313
  onSuccess,
314
+ tagLookupMap,
332
315
  eventContextTags,
333
316
  skipTags
334
317
  });
@@ -346,6 +329,7 @@ describe("validateLiquidTemplateContent", () => {
346
329
  messages,
347
330
  onError,
348
331
  onSuccess,
332
+ tagLookupMap,
349
333
  eventContextTags,
350
334
  skipTags
351
335
  });
@@ -363,6 +347,7 @@ describe("validateLiquidTemplateContent", () => {
363
347
  messages,
364
348
  onError,
365
349
  onSuccess,
350
+ tagLookupMap,
366
351
  eventContextTags,
367
352
  skipTags
368
353
  });
@@ -380,6 +365,7 @@ describe("validateLiquidTemplateContent", () => {
380
365
  messages,
381
366
  onError,
382
367
  onSuccess,
368
+ tagLookupMap,
383
369
  eventContextTags,
384
370
  skipTags
385
371
  });
@@ -397,6 +383,7 @@ describe("validateLiquidTemplateContent", () => {
397
383
  messages,
398
384
  onError,
399
385
  onSuccess,
386
+ tagLookupMap,
400
387
  eventContextTags,
401
388
  skipTags
402
389
  });
@@ -414,6 +401,7 @@ describe("validateLiquidTemplateContent", () => {
414
401
  messages,
415
402
  onError,
416
403
  onSuccess,
404
+ tagLookupMap,
417
405
  eventContextTags,
418
406
  skipTags
419
407
  });
@@ -431,6 +419,7 @@ describe("validateLiquidTemplateContent", () => {
431
419
  };
432
420
  const onError = jest.fn();
433
421
  const onSuccess = jest.fn();
422
+ const tagLookupMap = {};
434
423
  const eventContextTags = [];
435
424
  await validateLiquidTemplateContent('', {
436
425
  getLiquidTags,
@@ -438,6 +427,7 @@ describe("validateLiquidTemplateContent", () => {
438
427
  messages,
439
428
  onError,
440
429
  onSuccess,
430
+ tagLookupMap,
441
431
  eventContextTags,
442
432
  });
443
433
  expect(formatMessage).toHaveBeenCalledWith(messages.emailBodyEmptyError);
@@ -449,7 +439,7 @@ describe("validateLiquidTemplateContent", () => {
449
439
  expect(onSuccess).not.toHaveBeenCalled();
450
440
  });
451
441
 
452
- it("calls onSuccess when API returns extracted tags from {% for %} template but no errors (extracted tags are not validated)", async () => {
442
+ it("should skip tags that appear in {% %} syntax (like order.items in for loop)", async () => {
453
443
  const content = '{% for item in order.items %} Hello {% endfor %}';
454
444
  const getLiquidTags = jest.fn((content, cb) =>
455
445
  cb({ askAiraResponse: { errors: [], data: [{ name: "order.items" }] }, isError: false })
@@ -460,13 +450,15 @@ describe("validateLiquidTemplateContent", () => {
460
450
  messages,
461
451
  onError,
462
452
  onSuccess,
453
+ tagLookupMap,
463
454
  eventContextTags
464
455
  });
456
+ // order.items appears in {% %} syntax, so it should be skipped and validation should pass
465
457
  expect(onSuccess).toHaveBeenCalledWith(content, undefined);
466
458
  expect(onError).not.toHaveBeenCalled();
467
459
  });
468
460
 
469
- it("calls onSuccess when API returns extracted tags in {% %} blocks but no errors", async () => {
461
+ it("should skip tags that appear only inside {% %} blocks (like item.name in for loop)", async () => {
470
462
  const content = '{% for item in order.items %} {{ item.name }} - {{ item.quantity }} {% endfor %}';
471
463
  const getLiquidTags = jest.fn((content, cb) =>
472
464
  cb({ askAiraResponse: { errors: [], data: [{ name: "item.name" }, { name: "item.quantity" }] }, isError: false })
@@ -477,13 +469,15 @@ describe("validateLiquidTemplateContent", () => {
477
469
  messages,
478
470
  onError,
479
471
  onSuccess,
472
+ tagLookupMap,
480
473
  eventContextTags
481
474
  });
475
+ // item.name and item.quantity appear inside {% for %} block, so they should be skipped
482
476
  expect(onSuccess).toHaveBeenCalledWith(content, undefined);
483
477
  expect(onError).not.toHaveBeenCalled();
484
478
  });
485
479
 
486
- it("calls onSuccess when API returns extracted tags not in content but no errors", async () => {
480
+ it("should validate tags that don't appear in content but are extracted by API", async () => {
487
481
  const content = 'Some content without the tag';
488
482
  const getLiquidTags = jest.fn((content, cb) =>
489
483
  cb({ askAiraResponse: { errors: [], data: [{ name: "extractedTag" }] }, isError: false })
@@ -494,13 +488,19 @@ describe("validateLiquidTemplateContent", () => {
494
488
  messages,
495
489
  onError,
496
490
  onSuccess,
491
+ tagLookupMap,
497
492
  eventContextTags
498
493
  });
499
- expect(onSuccess).toHaveBeenCalledWith(content, undefined);
500
- expect(onError).not.toHaveBeenCalled();
494
+ // extractedTag doesn't appear in content but was extracted by API, should be validated
495
+ expect(onError).toHaveBeenCalledWith({
496
+ standardErrors: [],
497
+ liquidErrors: [undefined],
498
+ tabType: undefined
499
+ });
500
+ expect(onSuccess).not.toHaveBeenCalled();
501
501
  });
502
502
 
503
- it("calls onSuccess when API returns tags outside {% %} but no errors", async () => {
503
+ it("should validate tags that appear outside {% %} blocks", async () => {
504
504
  const content = '{{ outsideTag }} {% for item in order.items %} {{ item.name }} {% endfor %}';
505
505
  const getLiquidTags = jest.fn((content, cb) =>
506
506
  cb({ askAiraResponse: { errors: [], data: [{ name: "outsideTag" }, { name: "item.name" }] }, isError: false })
@@ -511,70 +511,20 @@ describe("validateLiquidTemplateContent", () => {
511
511
  messages,
512
512
  onError,
513
513
  onSuccess,
514
+ tagLookupMap,
514
515
  eventContextTags
515
516
  });
516
- expect(onSuccess).toHaveBeenCalledWith(content, undefined);
517
- expect(onError).not.toHaveBeenCalled();
518
- });
519
-
520
- it("calls onSuccess when API returns tag in {{ }} and no errors", async () => {
521
- const content = 'Hello {{ unsupportedTag }} world';
522
- const getLiquidTags = jest.fn((content, cb) =>
523
- cb({ askAiraResponse: { errors: [], data: [{ name: "unsupportedTag" }] }, isError: false })
524
- );
525
- await validateLiquidTemplateContent(content, {
526
- getLiquidTags,
527
- formatMessage,
528
- messages,
529
- onError,
530
- onSuccess,
531
- eventContextTags
532
- });
533
- expect(onSuccess).toHaveBeenCalledWith(content, undefined);
534
- expect(onError).not.toHaveBeenCalled();
535
- });
536
-
537
- it("calls onSuccess when API returns tag with spacing variants and no errors", async () => {
538
- const content = '{{ tag}} and {{tag }} and {{ tag }}';
539
- const getLiquidTags = jest.fn((content, cb) =>
540
- cb({
541
- askAiraResponse: {
542
- errors: [],
543
- data: [{ name: "tag" }]
544
- },
545
- isError: false
546
- })
547
- );
548
- await validateLiquidTemplateContent(content, {
549
- getLiquidTags,
550
- formatMessage,
551
- messages,
552
- onError,
553
- onSuccess,
554
- eventContextTags
555
- });
556
- expect(onSuccess).toHaveBeenCalledWith(content, undefined);
557
- expect(onError).not.toHaveBeenCalled();
558
- });
559
-
560
- it("calls onSuccess when API returns tag inside {% %} blocks but no errors", async () => {
561
- const content = '{% for x in some.unsupported %} {{ x }} {% endfor %}';
562
- const getLiquidTags = jest.fn((content, cb) =>
563
- cb({ askAiraResponse: { errors: [], data: [{ name: "some.unsupported" }] }, isError: false })
564
- );
565
- await validateLiquidTemplateContent(content, {
566
- getLiquidTags,
567
- formatMessage,
568
- messages,
569
- onError,
570
- onSuccess,
571
- eventContextTags
517
+ // outsideTag appears outside {% %} block, so it should be validated
518
+ // item.name appears inside block, so it should be skipped
519
+ expect(onError).toHaveBeenCalledWith({
520
+ standardErrors: [],
521
+ liquidErrors: [undefined],
522
+ tabType: undefined
572
523
  });
573
- expect(onSuccess).toHaveBeenCalledWith(content, undefined);
574
- expect(onError).not.toHaveBeenCalled();
524
+ expect(onSuccess).not.toHaveBeenCalled();
575
525
  });
576
526
 
577
- it("calls onSuccess when API returns tags with dots in {% %} syntax but no errors", async () => {
527
+ it("should skip tags with dots that appear in {% %} syntax", async () => {
578
528
  const content = '{% assign myVar = order.items %} Some text';
579
529
  const getLiquidTags = jest.fn((content, cb) =>
580
530
  cb({ askAiraResponse: { errors: [], data: [{ name: "order.items" }] }, isError: false })
@@ -585,8 +535,10 @@ describe("validateLiquidTemplateContent", () => {
585
535
  messages,
586
536
  onError,
587
537
  onSuccess,
538
+ tagLookupMap,
588
539
  eventContextTags
589
540
  });
541
+ // order.items appears in {% %} syntax, so it should be skipped
590
542
  expect(onSuccess).toHaveBeenCalledWith(content, undefined);
591
543
  expect(onError).not.toHaveBeenCalled();
592
544
  });
@@ -634,170 +586,6 @@ describe("validateLiquidTemplateContent", () => {
634
586
  });
635
587
  });
636
588
 
637
- describe("isValidEmail", () => {
638
- it("returns true for valid email addresses", () => {
639
- expect(isValidEmail("user@example.com")).toBe(true);
640
- expect(isValidEmail("test.user@domain.co")).toBe(true);
641
- expect(isValidEmail("a@b.co")).toBe(true);
642
- });
643
-
644
- it("returns false for invalid email addresses", () => {
645
- expect(isValidEmail("")).toBe(false);
646
- expect(isValidEmail("invalid")).toBe(false);
647
- expect(isValidEmail("@nodomain.com")).toBe(false);
648
- expect(isValidEmail("noatsign.com")).toBe(false);
649
- expect(isValidEmail("missingtld@domain")).toBe(false);
650
- expect(isValidEmail(" user@example.com ")).toBe(false);
651
- });
652
-
653
- it("returns true for edge case single-char local part", () => {
654
- expect(isValidEmail("x@y.co")).toBe(true);
655
- });
656
- });
657
-
658
- describe("isValidMobile", () => {
659
- it("returns true for valid mobile numbers (8-15 digits, no leading zero)", () => {
660
- expect(isValidMobile("12345678")).toBe(true);
661
- expect(isValidMobile("9123456789")).toBe(true);
662
- expect(isValidMobile("123456789012345")).toBe(true);
663
- });
664
-
665
- it("returns false for invalid mobile numbers", () => {
666
- expect(isValidMobile("")).toBe(false);
667
- expect(isValidMobile("01234567")).toBe(false);
668
- expect(isValidMobile("1234567")).toBe(false);
669
- expect(isValidMobile("1234567890123456")).toBe(false);
670
- expect(isValidMobile("abc12345678")).toBe(false);
671
- expect(isValidMobile("12345 67890")).toBe(false);
672
- });
673
-
674
- it("returns true for exactly 8 and exactly 15 digits", () => {
675
- expect(isValidMobile("12345678")).toBe(true);
676
- expect(isValidMobile("123456789012345")).toBe(true);
677
- });
678
- });
679
-
680
- describe("formatPhoneNumber", () => {
681
- it("returns empty string for falsy input", () => {
682
- expect(formatPhoneNumber("")).toBe("");
683
- expect(formatPhoneNumber(null)).toBe("");
684
- expect(formatPhoneNumber(undefined)).toBe("");
685
- });
686
-
687
- it("strips non-digit characters", () => {
688
- expect(formatPhoneNumber("91 234 567 890")).toBe("91234567890");
689
- expect(formatPhoneNumber("+91-234567890")).toBe("91234567890");
690
- expect(formatPhoneNumber("(123) 456-7890")).toBe("1234567890");
691
- });
692
-
693
- it("returns digits-only string unchanged", () => {
694
- expect(formatPhoneNumber("9123456789")).toBe("9123456789");
695
- });
696
-
697
- it("returns empty string for whitespace-only input", () => {
698
- expect(formatPhoneNumber(" ")).toBe("");
699
- });
700
- });
701
-
702
- describe("hasPersonalizationTags", () => {
703
- it("returns true when text has liquid tags {{ }}", () => {
704
- expect(hasPersonalizationTags("Hello {{name}}")).toBe(true);
705
- expect(hasPersonalizationTags("{{foo}}")).toBe(true);
706
- });
707
-
708
- it("returns true when text has bracket tags [ ]", () => {
709
- expect(hasPersonalizationTags("Hello [event.name]")).toBe(true);
710
- expect(hasPersonalizationTags("[tag]")).toBe(true);
711
- });
712
-
713
- it("returns false for empty or no tags", () => {
714
- expect(hasPersonalizationTags("")).toBeFalsy();
715
- expect(hasPersonalizationTags()).toBeFalsy();
716
- expect(hasPersonalizationTags("plain text")).toBe(false);
717
- expect(hasPersonalizationTags("only {{ open")).toBe(false);
718
- });
719
-
720
- it("returns false when only [ or only ] present without matching pair", () => {
721
- expect(hasPersonalizationTags("only [ open")).toBe(false);
722
- expect(hasPersonalizationTags("only ] close")).toBe(false);
723
- });
724
-
725
- it("returns true when liquid tags have content with spaces", () => {
726
- expect(hasPersonalizationTags("Hello {{ customer.name }}")).toBe(true);
727
- });
728
- });
729
-
730
- describe("getMessageForDevice", () => {
731
- it("returns message for device from templateData", () => {
732
- const templateData = {
733
- versions: {
734
- android: { base: { expandableDetails: { message: "Android msg" } } },
735
- ios: { base: { expandableDetails: { message: "iOS msg" } } },
736
- },
737
- };
738
- expect(getMessageForDevice(templateData, "android")).toBe("Android msg");
739
- expect(getMessageForDevice(templateData, "ios")).toBe("iOS msg");
740
- });
741
-
742
- it("returns undefined for missing path", () => {
743
- expect(getMessageForDevice(null, "android")).toBeUndefined();
744
- expect(getMessageForDevice({}, "android")).toBeUndefined();
745
- expect(getMessageForDevice({ versions: {} }, "android")).toBeUndefined();
746
- });
747
-
748
- it("returns undefined when base exists but expandableDetails is missing", () => {
749
- expect(getMessageForDevice({ versions: { android: { base: {} } } }, "android")).toBeUndefined();
750
- });
751
- });
752
-
753
- describe("getTitleForDevice", () => {
754
- it("returns title for device from templateData", () => {
755
- const templateData = {
756
- versions: {
757
- android: { base: { title: "Android title" } },
758
- ios: { base: { title: "iOS title" } },
759
- },
760
- };
761
- expect(getTitleForDevice(templateData, "android")).toBe("Android title");
762
- expect(getTitleForDevice(templateData, "ios")).toBe("iOS title");
763
- });
764
-
765
- it("returns empty string for missing title", () => {
766
- expect(getTitleForDevice(null, "android")).toBe("");
767
- expect(getTitleForDevice({}, "android")).toBe("");
768
- expect(getTitleForDevice({ versions: { android: { base: {} } } }, "android")).toBe("");
769
- });
770
-
771
- it("returns empty string when base.title is undefined", () => {
772
- expect(getTitleForDevice({ versions: { android: { base: { title: undefined } } } }, "android")).toBe("");
773
- });
774
- });
775
-
776
- describe("checkForPersonalizationTokens", () => {
777
- it("returns true when formData contains liquid or bracket tokens", () => {
778
- expect(checkForPersonalizationTokens({ 0: { content: "Hi {{name}}" } })).toBe(true);
779
- expect(checkForPersonalizationTokens({ tab1: { message: "Hello [event.id]" } })).toBe(true);
780
- });
781
-
782
- it("returns false for empty or no tokens", () => {
783
- expect(checkForPersonalizationTokens(null)).toBe(false);
784
- expect(checkForPersonalizationTokens({})).toBe(false);
785
- expect(checkForPersonalizationTokens({ 0: { content: "plain" } })).toBe(false);
786
- });
787
-
788
- it("returns false for non-object formData", () => {
789
- expect(checkForPersonalizationTokens("string")).toBe(false);
790
- });
791
-
792
- it("returns true for two-level nested object containing token", () => {
793
- expect(checkForPersonalizationTokens({ tab: { body: "Hi {{name}}" } })).toBe(true);
794
- });
795
-
796
- it("returns false for formData with array value (no string tokens)", () => {
797
- expect(checkForPersonalizationTokens({ 0: { items: ["a", "b"] } })).toBe(false);
798
- });
799
- });
800
-
801
589
  describe("validateMobilePushContent", () => {
802
590
  const formatMessage = jest.fn(msg => msg.id);
803
591
  const messages = {
@@ -805,6 +593,7 @@ describe("validateMobilePushContent", () => {
805
593
  somethingWentWrong: { id: "wrong" },
806
594
  unsupportedTagsValidationError: { id: "unsupported" }
807
595
  };
596
+ const tagLookupMap = { foo: true };
808
597
  const eventContextTags = [{ tagName: "foo" }];
809
598
  const onError = jest.fn();
810
599
  const onSuccess = jest.fn();
@@ -813,7 +602,7 @@ describe("validateMobilePushContent", () => {
813
602
  jest.clearAllMocks();
814
603
  });
815
604
 
816
- it("calls onError for empty formData (validateMobilePushContent)", async () => {
605
+ it("calls onError for empty formData", async () => {
817
606
  const getLiquidTags = jest.fn((content, cb) =>
818
607
  cb({ askAiraResponse: { errors: [], data: [] }, isError: false })
819
608
  );
@@ -822,17 +611,18 @@ describe("validateMobilePushContent", () => {
822
611
  {
823
612
  getLiquidTags,
824
613
  formatMessage,
825
- messages,
826
- onError,
827
- onSuccess,
828
- eventContextTags,
614
+ messages,
615
+ onError,
616
+ onSuccess,
617
+ tagLookupMap,
618
+ eventContextTags,
829
619
  currentTab: 1
830
620
  }
831
621
  );
832
622
  expect(onError).toHaveBeenCalled();
833
623
  });
834
624
 
835
- it("calls onSuccess for valid android and ios content (validateMobilePushContent)", async () => {
625
+ it("calls onSuccess for valid android and ios content", async () => {
836
626
  const getLiquidTags = jest.fn((content, cb) =>
837
627
  cb({ askAiraResponse: { errors: [], data: [] }, isError: false })
838
628
  );
@@ -843,6 +633,7 @@ describe("validateMobilePushContent", () => {
843
633
  messages,
844
634
  onError,
845
635
  onSuccess,
636
+ tagLookupMap,
846
637
  eventContextTags,
847
638
  currentTab: 1
848
639
  });
@@ -860,6 +651,7 @@ describe("validateMobilePushContent", () => {
860
651
  messages,
861
652
  onError,
862
653
  onSuccess,
654
+ tagLookupMap,
863
655
  eventContextTags,
864
656
  currentTab: 1
865
657
  });
@@ -877,6 +669,7 @@ describe("validateMobilePushContent", () => {
877
669
  messages,
878
670
  onError,
879
671
  onSuccess,
672
+ tagLookupMap,
880
673
  eventContextTags,
881
674
  currentTab: 2
882
675
  });
@@ -894,6 +687,7 @@ describe("validateMobilePushContent", () => {
894
687
  messages,
895
688
  onError,
896
689
  onSuccess,
690
+ tagLookupMap,
897
691
  eventContextTags
898
692
  });
899
693
  expect(onSuccess).toHaveBeenCalledWith(JSON.stringify(formData[0]), "android");
@@ -910,6 +704,7 @@ describe("validateMobilePushContent", () => {
910
704
  messages,
911
705
  onError,
912
706
  onSuccess,
707
+ tagLookupMap,
913
708
  eventContextTags
914
709
  });
915
710
  expect(onSuccess).toHaveBeenCalledWith("null", "android");
@@ -922,10 +717,11 @@ describe("validateMobilePushContent", () => {
922
717
  {
923
718
  getLiquidTags,
924
719
  formatMessage,
925
- messages,
926
- onError,
927
- onSuccess,
928
- eventContextTags,
720
+ messages,
721
+ onError,
722
+ onSuccess,
723
+ tagLookupMap,
724
+ eventContextTags,
929
725
  currentTab: 1,
930
726
  },
931
727
  );
@@ -939,10 +735,11 @@ describe("validateMobilePushContent", () => {
939
735
  {
940
736
  getLiquidTags,
941
737
  formatMessage,
942
- messages,
943
- onError,
944
- onSuccess,
945
- eventContextTags,
738
+ messages,
739
+ onError,
740
+ onSuccess,
741
+ tagLookupMap,
742
+ eventContextTags,
946
743
  currentTab: 1,
947
744
  },
948
745
  );
@@ -956,10 +753,11 @@ describe("validateMobilePushContent", () => {
956
753
  {
957
754
  getLiquidTags,
958
755
  formatMessage,
959
- messages,
960
- onError,
961
- onSuccess,
962
- eventContextTags,
756
+ messages,
757
+ onError,
758
+ onSuccess,
759
+ tagLookupMap,
760
+ eventContextTags,
963
761
  currentTab: 1,
964
762
  },
965
763
  );
@@ -973,10 +771,11 @@ describe("validateMobilePushContent", () => {
973
771
  {
974
772
  getLiquidTags,
975
773
  formatMessage,
976
- messages,
977
- onError,
978
- onSuccess,
979
- eventContextTags,
774
+ messages,
775
+ onError,
776
+ onSuccess,
777
+ tagLookupMap,
778
+ eventContextTags,
980
779
  currentTab: 1,
981
780
  },
982
781
  );
@@ -990,10 +789,11 @@ describe("validateMobilePushContent", () => {
990
789
  {
991
790
  getLiquidTags,
992
791
  formatMessage,
993
- messages,
994
- onError,
995
- onSuccess,
996
- eventContextTags,
792
+ messages,
793
+ onError,
794
+ onSuccess,
795
+ tagLookupMap,
796
+ eventContextTags,
997
797
  },
998
798
  );
999
799
  expect(onError).toHaveBeenCalled();
@@ -1006,10 +806,11 @@ describe("validateMobilePushContent", () => {
1006
806
  {
1007
807
  getLiquidTags,
1008
808
  formatMessage,
1009
- messages,
1010
- onError,
1011
- onSuccess,
1012
- eventContextTags,
809
+ messages,
810
+ onError,
811
+ onSuccess,
812
+ tagLookupMap,
813
+ eventContextTags,
1013
814
  },
1014
815
  );
1015
816
  expect(onError).toHaveBeenCalled();
@@ -1022,10 +823,11 @@ describe("validateMobilePushContent", () => {
1022
823
  {
1023
824
  getLiquidTags,
1024
825
  formatMessage,
1025
- messages,
1026
- onError,
1027
- onSuccess,
1028
- eventContextTags,
826
+ messages,
827
+ onError,
828
+ onSuccess,
829
+ tagLookupMap,
830
+ eventContextTags,
1029
831
  },
1030
832
  );
1031
833
  expect(onError).toHaveBeenCalled();
@@ -1038,10 +840,11 @@ describe("validateMobilePushContent", () => {
1038
840
  {
1039
841
  getLiquidTags,
1040
842
  formatMessage,
1041
- messages,
1042
- onError,
1043
- onSuccess,
1044
- eventContextTags,
843
+ messages,
844
+ onError,
845
+ onSuccess,
846
+ tagLookupMap,
847
+ eventContextTags,
1045
848
  },
1046
849
  );
1047
850
  expect(onError).toHaveBeenCalled();
@@ -1054,10 +857,11 @@ describe("validateMobilePushContent", () => {
1054
857
  {
1055
858
  getLiquidTags,
1056
859
  formatMessage,
1057
- messages,
1058
- onError,
1059
- onSuccess,
1060
- eventContextTags,
860
+ messages,
861
+ onError,
862
+ onSuccess,
863
+ tagLookupMap,
864
+ eventContextTags,
1061
865
  },
1062
866
  );
1063
867
  expect(onError).toHaveBeenCalled();
@@ -1071,6 +875,7 @@ describe("validateInAppContent", () => {
1071
875
  somethingWentWrong: { id: "wrong" },
1072
876
  unsupportedTagsValidationError: { id: "unsupported" }
1073
877
  };
878
+ const tagLookupMap = { foo: true };
1074
879
  const eventContextTags = [{ tagName: "foo" }];
1075
880
  const onError = jest.fn();
1076
881
  const onSuccess = jest.fn();
@@ -1079,7 +884,7 @@ describe("validateInAppContent", () => {
1079
884
  jest.clearAllMocks();
1080
885
  });
1081
886
 
1082
- it("calls onError for empty formData (validateInAppContent)", async () => {
887
+ it("calls onError for empty formData", async () => {
1083
888
  const getLiquidTags = jest.fn((content, cb) =>
1084
889
  cb({ askAiraResponse: { errors: [], data: [] }, isError: false })
1085
890
  );
@@ -1088,16 +893,17 @@ describe("validateInAppContent", () => {
1088
893
  {
1089
894
  getLiquidTags,
1090
895
  formatMessage,
1091
- messages,
1092
- onError,
1093
- onSuccess,
1094
- eventContextTags,
896
+ messages,
897
+ onError,
898
+ onSuccess,
899
+ tagLookupMap,
900
+ eventContextTags,
1095
901
  }
1096
902
  );
1097
903
  expect(onError).toHaveBeenCalled();
1098
904
  });
1099
905
 
1100
- it("calls onSuccess for valid android and ios content (validateInAppContent)", async () => {
906
+ it("calls onSuccess for valid android and ios content", async () => {
1101
907
  const getLiquidTags = jest.fn((content, cb) =>
1102
908
  cb({ askAiraResponse: { errors: [], data: [] }, isError: false })
1103
909
  );
@@ -1117,6 +923,7 @@ describe("validateInAppContent", () => {
1117
923
  messages,
1118
924
  onError,
1119
925
  onSuccess,
926
+ tagLookupMap,
1120
927
  eventContextTags,
1121
928
  singleTab: ANDROID,
1122
929
  });
@@ -1143,6 +950,7 @@ describe("validateInAppContent", () => {
1143
950
  messages,
1144
951
  onError,
1145
952
  onSuccess,
953
+ tagLookupMap,
1146
954
  eventContextTags,
1147
955
  singleTab: IOS,
1148
956
  });
@@ -1169,11 +977,11 @@ describe("getChannelData", () => {
1169
977
  expect(getChannelData("SMS", formData)).toBe("Hi Test");
1170
978
  });
1171
979
 
1172
- it("returns string with undefineds for SMS when base and template-name are empty or undefined", () => {
980
+ it("returns string with undefineds for SMS channel with missing fields", () => {
1173
981
  const formData = { base: {}, "template-name": undefined };
1174
982
  expect(getChannelData("SMS", formData)).toBe("undefined undefined");
1175
983
  });
1176
- it("returns string with undefineds for SMS when formData is empty string", () => {
984
+ it("returns string with undefineds for SMS channel with missing fields", () => {
1177
985
  expect(getChannelData("SMS", "")).toBe("undefined undefined");
1178
986
  });
1179
987