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

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 (122) hide show
  1. package/constants/unified.js +0 -18
  2. package/package.json +1 -1
  3. package/services/api.js +0 -17
  4. package/services/tests/api.test.js +0 -85
  5. package/utils/commonUtils.js +0 -28
  6. package/utils/tests/commonUtil.test.js +0 -169
  7. package/v2Components/CapTagList/index.js +0 -10
  8. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
  9. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  10. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
  11. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  12. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  13. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  14. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  15. package/v2Components/CommonTestAndPreview/SendTestMessage.js +53 -87
  16. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -20
  17. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  18. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -145
  19. package/v2Components/CommonTestAndPreview/actions.js +0 -10
  20. package/v2Components/CommonTestAndPreview/constants.js +1 -53
  21. package/v2Components/CommonTestAndPreview/index.js +168 -998
  22. package/v2Components/CommonTestAndPreview/messages.js +3 -147
  23. package/v2Components/CommonTestAndPreview/reducer.js +0 -10
  24. package/v2Components/CommonTestAndPreview/sagas.js +6 -15
  25. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +286 -328
  26. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  27. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  28. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  29. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +24 -65
  30. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  31. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
  32. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -168
  33. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
  34. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  35. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
  36. package/v2Components/FormBuilder/index.js +1 -7
  37. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  38. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  39. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  40. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  41. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
  42. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  43. package/v2Containers/CreativesContainer/constants.js +0 -9
  44. package/v2Containers/CreativesContainer/index.js +93 -292
  45. package/v2Containers/CreativesContainer/index.scss +1 -51
  46. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  47. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  48. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  49. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  50. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +10 -20
  51. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  52. package/v2Containers/Rcs/constants.js +3 -40
  53. package/v2Containers/Rcs/index.js +895 -1145
  54. package/v2Containers/Rcs/index.scss +6 -85
  55. package/v2Containers/Rcs/messages.js +2 -12
  56. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +2236 -41719
  57. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  58. package/v2Containers/Rcs/tests/index.test.js +38 -41
  59. package/v2Containers/Rcs/tests/mockData.js +0 -38
  60. package/v2Containers/Rcs/tests/utils.test.js +1 -435
  61. package/v2Containers/Rcs/utils.js +10 -405
  62. package/v2Containers/Sms/Create/index.js +38 -100
  63. package/v2Containers/SmsTrai/Create/index.js +4 -9
  64. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  65. package/v2Containers/SmsTrai/Edit/index.js +128 -636
  66. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  67. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2604 -4590
  68. package/v2Containers/SmsWrapper/index.js +8 -37
  69. package/v2Containers/TagList/index.js +0 -6
  70. package/v2Containers/Templates/_templates.scss +2 -63
  71. package/v2Containers/Templates/actions.js +0 -11
  72. package/v2Containers/Templates/constants.js +0 -2
  73. package/v2Containers/Templates/index.js +40 -90
  74. package/v2Containers/Templates/sagas.js +12 -57
  75. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
  76. package/v2Containers/Templates/tests/sagas.test.js +123 -193
  77. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  78. package/v2Containers/TemplatesV2/index.js +23 -86
  79. package/v2Containers/Whatsapp/index.js +20 -3
  80. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5790
  81. package/utils/templateVarUtils.js +0 -201
  82. package/utils/tests/templateVarUtils.test.js +0 -204
  83. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
  84. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -155
  85. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -93
  86. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  87. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
  88. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -648
  89. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -174
  90. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
  91. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  92. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
  93. package/v2Components/SmsFallback/constants.js +0 -73
  94. package/v2Components/SmsFallback/index.js +0 -955
  95. package/v2Components/SmsFallback/index.scss +0 -265
  96. package/v2Components/SmsFallback/messages.js +0 -78
  97. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
  98. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  99. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  100. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  101. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
  102. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
  103. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  104. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  105. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  106. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  107. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  108. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  109. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
  110. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  111. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  112. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  113. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  114. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  115. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  116. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  117. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  118. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  119. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  120. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  121. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  122. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,22 +1,7 @@
1
1
  import React from 'react';
2
2
  import renderer from 'react-test-renderer';
3
3
  import { render, screen } from '../../../utils/test-utils';
4
- import {
5
- getRCSContent,
6
- getRcsStatusType,
7
- getTemplateStatusType,
8
- normalizeCardVarMapped,
9
- coalesceCardVarMappedToTemplate,
10
- getRcsSemanticVarNamesSpanningTitleAndDesc,
11
- resolveCardVarMappedSlotValue,
12
- isRcsTextOnlyCardMediaType,
13
- mapRcsCardContentForConsumerWithResolvedTags,
14
- resolveRcsCardPreviewStrings,
15
- sanitizeCardVarMappedValue,
16
- areAllRcsSmsFallbackVarSlotsFilled,
17
- buildRcsNumericMustachePlaceholderRegex,
18
- } from '../utils';
19
- import { rcsVarRegex } from '../constants';
4
+ import { getRCSContent, getRcsStatusType, getTemplateStatusType } from '../utils';
20
5
  import { RCS, RCS_BUTTON_TYPES, STATUS_OPTIONS, RCS_STATUSES } from '../constants';
21
6
  import { mockData } from './mockData';
22
7
 
@@ -74,25 +59,6 @@ describe('RCS utils - renderRcsSuggestionsPreview', () => {
74
59
  expect(labels.length).toBe(1);
75
60
  expect(labels[0].textContent).toContain('Call');
76
61
  });
77
-
78
- it('renders only divider for unknown suggestion type', () => {
79
- const template = JSON.parse(JSON.stringify(templateBase));
80
- template.versions.base.content[RCS].rcsContent.cardContent[0].suggestions = [
81
- { type: RCS_BUTTON_TYPES.NONE, text: 'Ignored' },
82
- ];
83
- render(getRCSContent(template));
84
- expect(document.querySelectorAll('.rcs-cta-preview').length).toBe(0);
85
- expect(document.querySelectorAll('.whatsapp-divider').length).toBeGreaterThan(0);
86
- });
87
-
88
- it('prefers thumbnailUrl over mediaUrl for preview image', () => {
89
- const template = JSON.parse(JSON.stringify(templateBase));
90
- const card = template.versions.base.content[RCS].rcsContent.cardContent[0];
91
- card.media = { thumbnailUrl: 'thumb.jpg', mediaUrl: 'full.jpg' };
92
- render(getRCSContent(template));
93
- const img = document.querySelector('.rcs-listing-image');
94
- expect(img?.getAttribute('src')).toBe('thumb.jpg');
95
- });
96
62
  });
97
63
 
98
64
  describe('RCS utils', () => {
@@ -170,404 +136,4 @@ describe('RCS utils', () => {
170
136
  expect(getTemplateStatusType('some_unknown_status')).toBe('warning');
171
137
  });
172
138
  });
173
-
174
- describe('normalizeCardVarMapped', () => {
175
- it('maps numeric key + placeholder value to tag name', () => {
176
- expect(normalizeCardVarMapped({ 1: '{{user_id_b64}}' })).toEqual({ user_id_b64: '' });
177
- });
178
-
179
- it('maps numeric key + literal value when orderedTagNames is provided', () => {
180
- expect(
181
- normalizeCardVarMapped({ 1: 'hello' }, ['user_id_b64']),
182
- ).toEqual({ user_id_b64: 'hello' });
183
- });
184
-
185
- it('keeps numeric key when no orderedTagNames for literal value', () => {
186
- expect(normalizeCardVarMapped({ 1: 'hello' })).toEqual({ 1: 'hello' });
187
- });
188
-
189
- it('keeps semantic key + different tag token (TagList value)', () => {
190
- expect(normalizeCardVarMapped({ myKey: '{{tag}}' })).toEqual({ myKey: '{{tag}}' });
191
- });
192
-
193
- it('maps numeric slot + chosen tag onto template token when ordered names provided', () => {
194
- expect(
195
- normalizeCardVarMapped({ 1: '{{FirstName}}' }, ['user_name']),
196
- ).toEqual({ user_name: '{{FirstName}}' });
197
- });
198
-
199
- it('returns {} for null, undefined, or non-object raw', () => {
200
- expect(normalizeCardVarMapped(null)).toEqual({});
201
- expect(normalizeCardVarMapped(undefined)).toEqual({});
202
- expect(normalizeCardVarMapped('not-object')).toEqual({});
203
- });
204
-
205
- it('maps numeric key to order slot only when index is within orderedTagNames', () => {
206
- expect(normalizeCardVarMapped({ 3: 'z' }, ['a', 'b'])).toEqual({ 3: 'z' });
207
- });
208
-
209
- it('does not overwrite a literal slot value with a duplicate semantic placeholder entry', () => {
210
- expect(
211
- normalizeCardVarMapped(
212
- { 1: 'hello', user_id_b64: '{{user_id_b64}}' },
213
- ['user_id_b64'],
214
- ),
215
- ).toEqual({ user_id_b64: 'hello' });
216
- });
217
- });
218
-
219
- describe('getRcsSemanticVarNamesSpanningTitleAndDesc', () => {
220
- it('returns names that appear in both title and description', () => {
221
- const set = getRcsSemanticVarNamesSpanningTitleAndDesc(
222
- 'Hello {{adv}}',
223
- 'Body {{adv}} x {{other}}',
224
- rcsVarRegex,
225
- );
226
- expect(Array.from(set).sort()).toEqual(['adv']);
227
- });
228
-
229
- it('is empty when the same name is only repeated in the title', () => {
230
- const set = getRcsSemanticVarNamesSpanningTitleAndDesc(
231
- '{{user_name}} and {{user_name}}',
232
- 'Hi',
233
- rcsVarRegex,
234
- );
235
- expect(set.size).toBe(0);
236
- });
237
- });
238
-
239
- describe('coalesceCardVarMappedToTemplate', () => {
240
- it('does not copy a shared semantic value into the second slot when the tag spans title and description', () => {
241
- const out = coalesceCardVarMappedToTemplate(
242
- { adv: 'shared' },
243
- 'T {{adv}}',
244
- 'D {{adv}}',
245
- rcsVarRegex,
246
- );
247
- expect(out[1]).toBe('shared');
248
- expect(out[2]).toBe('');
249
- expect(out.adv).toBe('shared');
250
- });
251
-
252
- it('maps slot 1 to first token name from title', () => {
253
- expect(
254
- coalesceCardVarMappedToTemplate(
255
- { 1: 'abc' },
256
- 'Hi {{user_name}}',
257
- '',
258
- rcsVarRegex,
259
- ),
260
- ).toEqual({ 1: 'abc', user_name: 'abc' });
261
- });
262
-
263
- it('keeps {{1}} style token as key 1', () => {
264
- expect(
265
- coalesceCardVarMappedToTemplate(
266
- { 1: '' },
267
- 'Hi {{1}}',
268
- '',
269
- rcsVarRegex,
270
- ),
271
- ).toEqual({ 1: '' });
272
- });
273
-
274
- it('returns clone when no tokens', () => {
275
- const raw = { 1: 'x' };
276
- const out = coalesceCardVarMappedToTemplate(raw, 'no vars', '', rcsVarRegex);
277
- expect(out).toEqual({ 1: 'x' });
278
- expect(out).not.toBe(raw);
279
- });
280
-
281
- it('merges tokens from title and description', () => {
282
- const out = coalesceCardVarMappedToTemplate(
283
- { a: '1', b: '2' },
284
- 'T {{a}}',
285
- 'D {{b}}',
286
- rcsVarRegex,
287
- );
288
- expect(out).toEqual({ a: '1', b: '2', 1: '1', 2: '2' });
289
- });
290
-
291
- it('returns {} when there are no tokens and raw is null or not an object', () => {
292
- expect(coalesceCardVarMappedToTemplate(null, 'plain', '', rcsVarRegex)).toEqual({});
293
- expect(coalesceCardVarMappedToTemplate(undefined, 'plain', '', rcsVarRegex)).toEqual({});
294
- });
295
-
296
- it('fills token values from legacy numeric slots when named key is missing', () => {
297
- expect(
298
- coalesceCardVarMappedToTemplate(
299
- { 1: 'legacy-val' },
300
- 'Hello {{name}}',
301
- '',
302
- rcsVarRegex,
303
- ),
304
- ).toEqual({ 1: 'legacy-val', name: 'legacy-val' });
305
- });
306
-
307
- it('maps values when raw is non-null but missing named keys', () => {
308
- expect(
309
- coalesceCardVarMappedToTemplate(
310
- {},
311
- 'X {{a}}',
312
- '',
313
- rcsVarRegex,
314
- ),
315
- ).toEqual({ 1: '', a: '' });
316
- });
317
- });
318
-
319
- describe('resolveCardVarMappedSlotValue', () => {
320
- it('prefers per-slot numeric key when both semantic and numeric are present', () => {
321
- expect(
322
- resolveCardVarMappedSlotValue({ user_name: 'A', 1: 'B' }, 'user_name', 0),
323
- ).toBe('B');
324
- });
325
-
326
- it('falls back to slot 1 when name missing', () => {
327
- expect(resolveCardVarMappedSlotValue({ 1: 'legacy' }, 'user_name', 0)).toBe('legacy');
328
- });
329
-
330
- it('uses global slot index for second variable', () => {
331
- expect(resolveCardVarMappedSlotValue({ 2: 'second' }, 'b', 1)).toBe('second');
332
- });
333
-
334
- it('reads slot from string slot key when varName has no direct mapping', () => {
335
- expect(resolveCardVarMappedSlotValue({ 1: 'only-slot' }, 'missing', 0)).toBe('only-slot');
336
- });
337
-
338
- it('reads numeric object key for slot when string slot key is absent', () => {
339
- expect(resolveCardVarMappedSlotValue({ 2: 'n2' }, 'x', 1)).toBe('n2');
340
- });
341
-
342
- it('returns empty when named key is cleared, not numeric slot fallback', () => {
343
- expect(
344
- resolveCardVarMappedSlotValue({ user_name: '', 1: 'old value' }, 'user_name', 0),
345
- ).toBe('');
346
- });
347
-
348
- it('library mode: semantic empty does not hide non-empty numeric slot (campaign / journey payload)', () => {
349
- expect(
350
- resolveCardVarMappedSlotValue(
351
- { user_id_b64: '', 1: 'selected-from-library' },
352
- 'user_id_b64',
353
- 0,
354
- true,
355
- ),
356
- ).toBe('selected-from-library');
357
- });
358
-
359
- it('empty numeric slot falls back to semantic tag (preview after hydration leaves 1:"")', () => {
360
- expect(
361
- resolveCardVarMappedSlotValue(
362
- { promotion_points: '{{loyalty_points}}', 1: '' },
363
- 'promotion_points',
364
- 0,
365
- ),
366
- ).toBe('{{loyalty_points}}');
367
- });
368
-
369
- it('omitSemanticFallback: duplicate title+desc tag does not read shared semantic for either slot', () => {
370
- expect(
371
- resolveCardVarMappedSlotValue({ adv: '{{adv}}', 1: '', 2: '' }, 'adv', 0, false, true),
372
- ).toBe('');
373
- expect(
374
- resolveCardVarMappedSlotValue({ adv: '{{adv}}', 1: 'A', 2: 'B' }, 'adv', 0, false, true),
375
- ).toBe('A');
376
- expect(
377
- resolveCardVarMappedSlotValue({ adv: '{{adv}}', 1: 'A', 2: 'B' }, 'adv', 1, false, true),
378
- ).toBe('B');
379
- });
380
-
381
- it('omitSemanticFallback: cleared semantic does not force empty when numeric slot has a value', () => {
382
- expect(
383
- resolveCardVarMappedSlotValue({ adv: '', 1: 'first', 2: 'second' }, 'adv', 1, false, true),
384
- ).toBe('second');
385
- });
386
- });
387
-
388
- describe('mapRcsCardContentForConsumerWithResolvedTags', () => {
389
- it('sets title and description to resolved tag strings on each card', () => {
390
- const out = mapRcsCardContentForConsumerWithResolvedTags(
391
- [
392
- {
393
- title: '',
394
- description: 'Visit {{gt}} discount',
395
- mediaType: 'NONE',
396
- cardVarMapped: { gt: '{{loyalty_points}}', 1: '{{loyalty_points}}' },
397
- },
398
- ],
399
- {},
400
- false,
401
- );
402
- expect(out[0].title).toBe('');
403
- expect(out[0].description).toBe('Visit {{loyalty_points}} discount');
404
- });
405
-
406
- it('merges root rcsCardVarMapped with nested cardVarMapped', () => {
407
- const out = mapRcsCardContentForConsumerWithResolvedTags(
408
- [
409
- {
410
- title: 'Hi {{first_name}}',
411
- description: 'Pts {{promotion_points}}',
412
- mediaType: 'IMAGE',
413
- cardVarMapped: {
414
- promotion_points: '{{loyalty_points}}',
415
- 2: '{{loyalty_points}}',
416
- },
417
- },
418
- ],
419
- {
420
- first_name: '{{customer_name}}',
421
- 1: '{{customer_name}}',
422
- },
423
- false,
424
- );
425
- expect(out[0].title).toBe('Hi {{customer_name}}');
426
- expect(out[0].description).toBe('Pts {{loyalty_points}}');
427
- });
428
-
429
- it('passes through null or non-object items in the card array unchanged', () => {
430
- const out = mapRcsCardContentForConsumerWithResolvedTags([null, 'string', 42], {}, false);
431
- expect(out).toEqual([null, 'string', 42]);
432
- });
433
- });
434
-
435
- describe('isRcsTextOnlyCardMediaType', () => {
436
- it('returns true for NONE and TEXT (text message card)', () => {
437
- expect(isRcsTextOnlyCardMediaType('NONE')).toBe(true);
438
- expect(isRcsTextOnlyCardMediaType('TEXT')).toBe(true);
439
- expect(isRcsTextOnlyCardMediaType('text')).toBe(true);
440
- });
441
- it('returns false for rich card media', () => {
442
- expect(isRcsTextOnlyCardMediaType('IMAGE')).toBe(false);
443
- expect(isRcsTextOnlyCardMediaType('VIDEO')).toBe(false);
444
- });
445
- });
446
-
447
- describe('resolveRcsCardPreviewStrings', () => {
448
- it('substitutes mapped tags for title and description (campaign preview parity)', () => {
449
- const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
450
- 'Hi {{first_name}}',
451
- 'Pts {{promotion_points}}',
452
- {
453
- first_name: '{{customer_name}}',
454
- promotion_points: '{{loyalty_points}}',
455
- 1: '{{customer_name}}',
456
- 2: '{{loyalty_points}}',
457
- },
458
- true,
459
- );
460
- expect(rcsTitle).toBe('Hi {{customer_name}}');
461
- expect(rcsDesc).toBe('Pts {{loyalty_points}}');
462
- });
463
-
464
- it('leaves placeholders when map has no value', () => {
465
- const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
466
- '{{a}}',
467
- '{{b}}',
468
- {},
469
- false,
470
- );
471
- expect(rcsTitle).toBe('{{a}}');
472
- expect(rcsDesc).toBe('{{b}}');
473
- });
474
-
475
- it('text-only card: ignores stale title and resolves description from global slot 0', () => {
476
- const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
477
- 'Stale {{old_tag}} title',
478
- 'Visit Store for {{gt}} discount',
479
- { gt: '{{loyalty_points}}', 1: '{{loyalty_points}}' },
480
- true,
481
- true,
482
- );
483
- expect(rcsTitle).toBe('');
484
- expect(rcsDesc).toBe('Visit Store for {{loyalty_points}} discount');
485
- });
486
- });
487
-
488
- describe('sanitizeCardVarMappedValue', () => {
489
- it('returns empty for null', () => {
490
- expect(sanitizeCardVarMappedValue(null)).toBe('');
491
- });
492
-
493
- it('strips numeric-only self-placeholder stored as value', () => {
494
- expect(sanitizeCardVarMappedValue('{{1}}')).toBe('');
495
- expect(sanitizeCardVarMappedValue('{{12}}')).toBe('');
496
- });
497
-
498
- it('keeps semantic mustache values from TagList', () => {
499
- expect(sanitizeCardVarMappedValue('{{FirstName}}')).toBe('{{FirstName}}');
500
- expect(sanitizeCardVarMappedValue('{{user_name}}')).toBe('{{user_name}}');
501
- });
502
-
503
- it('returns original string for non-placeholder values', () => {
504
- expect(sanitizeCardVarMappedValue(' literal ')).toBe(' literal ');
505
- });
506
- });
507
-
508
- describe('areAllRcsSmsFallbackVarSlotsFilled', () => {
509
- it('returns true when template empty', () => {
510
- expect(areAllRcsSmsFallbackVarSlotsFilled('', { '{{a}}_0': 'x' })).toBe(true);
511
- });
512
-
513
- it('returns false when a mustache slot is only whitespace (parity with RCS title/desc vars)', () => {
514
- expect(
515
- areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': ' ' }),
516
- ).toBe(false);
517
- });
518
-
519
- it('returns false when a mustache slot is missing or empty string', () => {
520
- expect(areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', {})).toBe(false);
521
- expect(areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': '' })).toBe(
522
- false,
523
- );
524
- });
525
-
526
- it('returns false when a DLT {#var#} slot is empty', () => {
527
- expect(areAllRcsSmsFallbackVarSlotsFilled('Hi {#x#}', { '{#x#}_1': ' ' })).toBe(false);
528
- });
529
-
530
- it('requires mapping for {{optout}} token like any other variable', () => {
531
- expect(areAllRcsSmsFallbackVarSlotsFilled('test {{optout}}', {})).toBe(false);
532
- expect(areAllRcsSmsFallbackVarSlotsFilled('test {{ optout }}', {})).toBe(false);
533
- expect(areAllRcsSmsFallbackVarSlotsFilled('test {{optout}}', { '{{optout}}_1': '{{optout}}' })).toBe(true);
534
- expect(areAllRcsSmsFallbackVarSlotsFilled('test {{optout}}', { '{{optout}}_1': '' })).toBe(false);
535
- });
536
-
537
- it('returns true when template is missing or not a string', () => {
538
- expect(areAllRcsSmsFallbackVarSlotsFilled(null, {})).toBe(true);
539
- expect(areAllRcsSmsFallbackVarSlotsFilled(undefined, {})).toBe(true);
540
- expect(areAllRcsSmsFallbackVarSlotsFilled(123, {})).toBe(true);
541
- });
542
-
543
- it('returns true when all variable slots have non-whitespace values', () => {
544
- expect(
545
- areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': 'Ann' }),
546
- ).toBe(true);
547
- });
548
-
549
- it('coerces non-string slot values with String() so DLT/hydration payloads still count as filled', () => {
550
- expect(
551
- areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': 42 }),
552
- ).toBe(true);
553
- });
554
-
555
- it('accepts legacy 1-based ordinal keys when the only token is at segment index 0 (DLT / API parity)', () => {
556
- expect(areAllRcsSmsFallbackVarSlotsFilled('{#shop#}', { '{#shop#}_1': 'Mart' })).toBe(true);
557
- expect(areAllRcsSmsFallbackVarSlotsFilled('{#shop#}', { '{#shop#}_1': '' })).toBe(false);
558
- });
559
- });
560
-
561
- describe('buildRcsNumericMustachePlaceholderRegex', () => {
562
- it('escapes regex metacharacters in numeric name', () => {
563
- const re = buildRcsNumericMustachePlaceholderRegex('1.5');
564
- expect(re.test('{{1.5}}')).toBe(true);
565
- });
566
-
567
- it('matches simple numeric placeholder', () => {
568
- const re = buildRcsNumericMustachePlaceholderRegex('2');
569
- expect(re.test('{{2}}')).toBe(true);
570
- expect(re.test('{{3}}')).toBe(false);
571
- });
572
- });
573
139
  });