@capillarytech/creatives-library 9.0.14 → 9.0.15-alpha.1
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 +32 -0
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/common.js +8 -0
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/rcsPayloadUtils.test.js +226 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +168 -109
- package/v2Components/CapActionButton/index.scss +156 -5
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +42 -18
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +60 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +201 -23
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +355 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +761 -222
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -267
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/CommonTestAndPreview/utils.js +84 -0
- package/v2Components/FormBuilder/Classic.js +4487 -0
- package/v2Components/FormBuilder/Functional/FormBuilderShell.js +366 -0
- package/v2Components/FormBuilder/Functional/channels/registry.js +17 -0
- package/v2Components/FormBuilder/Functional/channels/sms/buildSubmitPayload.js +9 -0
- package/v2Components/FormBuilder/Functional/channels/sms/config.js +30 -0
- package/v2Components/FormBuilder/Functional/channels/sms/getEditorErrorDescriptor.js +46 -0
- package/v2Components/FormBuilder/Functional/channels/sms/getLiquidContent.js +13 -0
- package/v2Components/FormBuilder/Functional/channels/sms/index.js +22 -0
- package/v2Components/FormBuilder/Functional/channels/sms/tests/getEditorErrorDescriptor.test.js +52 -0
- package/v2Components/FormBuilder/Functional/channels/sms/tests/getLiquidContent.test.js +25 -0
- package/v2Components/FormBuilder/Functional/channels/sms/tests/validate.test.js +87 -0
- package/v2Components/FormBuilder/Functional/channels/sms/validate.js +89 -0
- package/v2Components/FormBuilder/Functional/constants.js +42 -0
- package/v2Components/FormBuilder/Functional/core/schema/fieldRegistry.js +38 -0
- package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +85 -0
- package/v2Components/FormBuilder/Functional/core/store/formReducer.js +81 -0
- package/v2Components/FormBuilder/Functional/core/store/selectors.js +30 -0
- package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +91 -0
- package/v2Components/FormBuilder/Functional/index.js +26 -0
- package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +59 -0
- package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +31 -0
- package/v2Components/FormBuilder/Functional/layout/Section.js +116 -0
- package/v2Components/FormBuilder/Functional/renderers/smsRenderers.js +258 -0
- package/v2Components/FormBuilder/Functional/tests/channelRegistry.test.js +21 -0
- package/v2Components/FormBuilder/Functional/tests/fieldRegistry.test.js +65 -0
- package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +97 -0
- package/v2Components/FormBuilder/Functional/tests/fixtures/smsParityCases.js +192 -0
- package/v2Components/FormBuilder/Functional/tests/formReducer.test.js +129 -0
- package/v2Components/FormBuilder/Functional/tests/initializeFormState.test.js +132 -0
- package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +40 -0
- package/v2Components/FormBuilder/Functional/tests/section.test.js +99 -0
- package/v2Components/FormBuilder/Functional/tests/selectors.test.js +67 -0
- package/v2Components/FormBuilder/Functional/tests/sms.crossFlowParity.test.js +155 -0
- package/v2Components/FormBuilder/Functional/tests/sms.liquid.test.js +172 -0
- package/v2Components/FormBuilder/Functional/tests/sms.rollout.test.js +122 -0
- package/v2Components/FormBuilder/Functional/tests/sms.shell.parity.test.js +329 -0
- package/v2Components/FormBuilder/Functional/tests/smsRenderers.test.js +160 -0
- package/v2Components/FormBuilder/Functional/tests/toLegacyFormData.test.js +95 -0
- package/v2Components/FormBuilder/_formBuilder.scss +5 -0
- package/v2Components/FormBuilder/index.js +41 -4479
- package/v2Components/FormBuilder/tests/__snapshots__/sms.characterization.test.js.snap +114 -0
- package/v2Components/FormBuilder/tests/entryGate.test.js +106 -0
- package/v2Components/FormBuilder/tests/sms.characterization.test.js +336 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +94 -0
- package/v2Components/SmsFallback/index.js +958 -0
- package/v2Components/SmsFallback/index.scss +266 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +41 -22
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -31
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +11 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +323 -104
- package/v2Containers/CreativesContainer/index.scss +83 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +333 -90
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +131 -11
- package/v2Containers/Rcs/index.js +2602 -807
- package/v2Containers/Rcs/index.scss +289 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +227 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +73535 -38537
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -8
- package/v2Containers/Rcs/tests/index.test.js +147 -36
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +115 -48
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +678 -169
- package/v2Containers/SmsTrai/Edit/index.scss +126 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +171 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +125 -55
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -343,14 +343,6 @@ exports[`RCS utils getRCSContent renders RCS content with no media 1`] = `
|
|
|
343
343
|
<div
|
|
344
344
|
className="cap-rcs-creatives"
|
|
345
345
|
>
|
|
346
|
-
<div
|
|
347
|
-
className="cap-label cap-label-div label19 rcs-listing-content title"
|
|
348
|
-
style={
|
|
349
|
-
Object {
|
|
350
|
-
"fontWeight": "bold",
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
/>
|
|
354
346
|
<div
|
|
355
347
|
className="cap-label cap-label-div label19 rcs-listing-content desc"
|
|
356
348
|
style={Object {}}
|
|
@@ -14,6 +14,19 @@ import TagList from '../../../v2Containers/TagList/index.js';
|
|
|
14
14
|
import CapActionButton from '../../../v2Components/CapActionButton';
|
|
15
15
|
import { INITIAL_SUGGESTIONS_DATA_STOP } from '../constants';
|
|
16
16
|
|
|
17
|
+
// Ensure real UnifiedPreview: other suites mock the same module; parallel workers + load order
|
|
18
|
+
// can leave a stub active. This factory wins for this file's resolution of UnifiedPreview.
|
|
19
|
+
jest.mock('../../../v2Components/CommonTestAndPreview/UnifiedPreview', () =>
|
|
20
|
+
jest.requireActual('../../../v2Components/CommonTestAndPreview/UnifiedPreview'),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
jest.mock('react-redux', () => {
|
|
24
|
+
const actual = jest.requireActual('react-redux');
|
|
25
|
+
return {
|
|
26
|
+
...actual,
|
|
27
|
+
useDispatch: jest.fn(() => jest.fn()),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
17
30
|
|
|
18
31
|
jest.mock('../../../v2Containers/TagList/index.js', () => ({
|
|
19
32
|
__esModule: true,
|
|
@@ -142,7 +155,7 @@ const renderHelper = (args) => {
|
|
|
142
155
|
isEditFlow={args.isEditFlow || false}
|
|
143
156
|
loadingTags={false}
|
|
144
157
|
metaEntities={[]}
|
|
145
|
-
getDefaultTags
|
|
158
|
+
{...(args.omitGetDefaultTags ? {} : { getDefaultTags: true })}
|
|
146
159
|
isDltEnabled={args.isDltEnabled || false}
|
|
147
160
|
smsRegister={'DLT'}
|
|
148
161
|
/>
|
|
@@ -186,13 +199,13 @@ describe('Creatives rcs test/>', () => {
|
|
|
186
199
|
renderedComponent.update();
|
|
187
200
|
expect(renderedComponent).toMatchSnapshot();
|
|
188
201
|
|
|
189
|
-
// Carousel option exists
|
|
202
|
+
// Carousel option exists
|
|
190
203
|
const options = radioGroup.props().options;
|
|
191
204
|
expect(options).toHaveLength(3);
|
|
192
205
|
expect(options[0].value).toBe('text_message');
|
|
193
206
|
expect(options[1].value).toBe('rich_card');
|
|
194
|
-
expect(options[2].value).toBe('
|
|
195
|
-
expect(options[2].disabled).
|
|
207
|
+
expect(options[2].value).toBe('CAROUSEL');
|
|
208
|
+
expect(options[2].disabled).toBeFalsy();
|
|
196
209
|
|
|
197
210
|
// Disabled state in edit mode
|
|
198
211
|
renderHelper({
|
|
@@ -445,8 +458,8 @@ describe('Creatives rcs test/>', () => {
|
|
|
445
458
|
});
|
|
446
459
|
|
|
447
460
|
it('should call fetchSchemaForEntity when TagList context changes (non-full mode)', () => {
|
|
448
|
-
//
|
|
449
|
-
renderHelper({ isFullMode: false });
|
|
461
|
+
// omitGetDefaultTags: when getDefaultTags is true it overwrites query.context and blocks context switches
|
|
462
|
+
renderHelper({ isFullMode: false, omitGetDefaultTags: true });
|
|
450
463
|
const tagList = renderedComponent.find('.tag-mock').at(0);
|
|
451
464
|
expect(tagList.exists()).toBe(true);
|
|
452
465
|
const before = fetchSchemaForEntity.mock.calls.length;
|
|
@@ -454,13 +467,126 @@ describe('Creatives rcs test/>', () => {
|
|
|
454
467
|
tagList.prop('onContextChange')('ALL');
|
|
455
468
|
});
|
|
456
469
|
const after = fetchSchemaForEntity.mock.calls.length;
|
|
457
|
-
expect(after).
|
|
470
|
+
expect(after).toBeGreaterThan(before);
|
|
458
471
|
const lastArg = fetchSchemaForEntity.mock.calls[after - 1][0];
|
|
459
472
|
expect(lastArg).toEqual(expect.objectContaining({ layout: 'SMS', type: 'TAG', context: 'default' }));
|
|
460
473
|
});
|
|
461
474
|
});
|
|
462
475
|
});
|
|
463
476
|
|
|
477
|
+
describe('RCS carousel label resolution in non-full mode', () => {
|
|
478
|
+
it('should resolve shared variables across multiple carousel cards in both inline preview and Test&Preview slidebox', async () => {
|
|
479
|
+
const templateData = {
|
|
480
|
+
name: 'CarouselVarsTemplate',
|
|
481
|
+
versions: {
|
|
482
|
+
base: {
|
|
483
|
+
content: {
|
|
484
|
+
RCS: {
|
|
485
|
+
rcsContent: {
|
|
486
|
+
cardType: 'CAROUSEL',
|
|
487
|
+
cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
|
|
488
|
+
cardContent: [
|
|
489
|
+
{
|
|
490
|
+
title: 'Hi {{name}}',
|
|
491
|
+
description: 'Welcome {{name}}',
|
|
492
|
+
mediaType: 'IMAGE',
|
|
493
|
+
media: { mediaUrl: 'https://cdn.example.com/c1.png', height: 'MEDIUM' },
|
|
494
|
+
cardVarMapped: {}, // empty on load
|
|
495
|
+
suggestions: [],
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
title: 'Offer for {{name}}',
|
|
499
|
+
description: 'Dear {{name}}',
|
|
500
|
+
mediaType: 'IMAGE',
|
|
501
|
+
media: { mediaUrl: 'https://cdn.example.com/c2.png', height: 'MEDIUM' },
|
|
502
|
+
suggestions: [],
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
contentType: 'RICHCARD',
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
type: 'RCS',
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const wrapper = mountWithIntl(
|
|
515
|
+
<Provider store={store}>
|
|
516
|
+
<Rcs
|
|
517
|
+
actions={{
|
|
518
|
+
uploadRcsAsset,
|
|
519
|
+
clearRcsMediaAsset,
|
|
520
|
+
createTemplate,
|
|
521
|
+
clearCreateResponse,
|
|
522
|
+
getTemplateDetails,
|
|
523
|
+
editTemplate,
|
|
524
|
+
clearEditResponse,
|
|
525
|
+
}}
|
|
526
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
527
|
+
onCreateComplete={onCreateComplete}
|
|
528
|
+
handleClose={handleClose}
|
|
529
|
+
intl={{ formatMessage }}
|
|
530
|
+
location={{ pathname: '/rcs/edit', query: { type: false, module: 'default' }, search: '' }}
|
|
531
|
+
params={params}
|
|
532
|
+
templateData={templateData}
|
|
533
|
+
rcsData={{}}
|
|
534
|
+
isFullMode={false}
|
|
535
|
+
isEditFlow={false}
|
|
536
|
+
loadingTags={false}
|
|
537
|
+
metaEntities={[]}
|
|
538
|
+
isDltEnabled={false}
|
|
539
|
+
smsRegister={'DLT'}
|
|
540
|
+
showTestAndPreviewSlidebox
|
|
541
|
+
/>
|
|
542
|
+
</Provider>,
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
// Flush hydration useEffects
|
|
546
|
+
await act(async () => {
|
|
547
|
+
await Promise.resolve();
|
|
548
|
+
});
|
|
549
|
+
wrapper.update();
|
|
550
|
+
|
|
551
|
+
// Sanity: carousel inline preview should be present (right preview column)
|
|
552
|
+
const inlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
553
|
+
expect(inlinePreview.exists()).toBe(true);
|
|
554
|
+
expect(inlinePreview.props().content?.carouselData?.length).toBeGreaterThanOrEqual(2);
|
|
555
|
+
|
|
556
|
+
// Variable slot id may live on `id` or React `key` depending on CapInput/TextArea wiring.
|
|
557
|
+
const nameVarAreas = wrapper.find('CapInputTextArea').filterWhere((n) => {
|
|
558
|
+
const id = String(n.prop('id') || '');
|
|
559
|
+
const key = String(n.key() || '');
|
|
560
|
+
return id.includes('{{name}}_') || key.includes('{{name}}_');
|
|
561
|
+
});
|
|
562
|
+
expect(nameVarAreas.length).toBeGreaterThanOrEqual(1);
|
|
563
|
+
|
|
564
|
+
act(() => {
|
|
565
|
+
const slotId = nameVarAreas.at(0).prop('id') || nameVarAreas.at(0).key();
|
|
566
|
+
nameVarAreas.at(0).props().onChange({ target: { id: slotId, value: 'madhavi' } });
|
|
567
|
+
});
|
|
568
|
+
wrapper.update();
|
|
569
|
+
|
|
570
|
+
// Inline preview should now show resolved values across BOTH cards
|
|
571
|
+
const updatedInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
572
|
+
const inlineCards = updatedInlinePreview.props().content.carouselData;
|
|
573
|
+
expect(inlineCards[0].title).toBe('Hi madhavi');
|
|
574
|
+
expect(inlineCards[0].bodyText).toBe('Welcome madhavi');
|
|
575
|
+
expect(inlineCards[1].title).toBe('Offer for madhavi');
|
|
576
|
+
expect(inlineCards[1].bodyText).toBe('Dear madhavi');
|
|
577
|
+
|
|
578
|
+
// Slidebox content should also be resolved (same underlying mapping)
|
|
579
|
+
const slidebox = wrapper.find('TestAndPreviewSlidebox').at(0);
|
|
580
|
+
expect(slidebox.exists()).toBe(true);
|
|
581
|
+
const slideboxContent = slidebox.props().content;
|
|
582
|
+
expect(slideboxContent?.carouselData?.length).toBeGreaterThanOrEqual(2);
|
|
583
|
+
expect(slideboxContent.carouselData[0].title).toBe('Hi madhavi');
|
|
584
|
+
expect(slideboxContent.carouselData[0].bodyText).toBe('Welcome madhavi');
|
|
585
|
+
expect(slideboxContent.carouselData[1].title).toBe('Offer for madhavi');
|
|
586
|
+
expect(slideboxContent.carouselData[1].bodyText).toBe('Dear madhavi');
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
464
590
|
describe('RCS orientation tests', () => {
|
|
465
591
|
it('should default to MEDIUM_HEIGHT (vertical) orientation', () => {
|
|
466
592
|
renderHelper({ isEditFlow: false }); // force CapSelect to render
|
|
@@ -547,7 +673,7 @@ describe('RCS orientation tests', () => {
|
|
|
547
673
|
});
|
|
548
674
|
|
|
549
675
|
describe('RCS createPayload', () => {
|
|
550
|
-
it('should build expected payload in full mode (IMAGE
|
|
676
|
+
it('should build expected payload in full mode (IMAGE)', async () => {
|
|
551
677
|
const createRcsTemplate = jest.fn();
|
|
552
678
|
const getTemplateDetails = jest.fn();
|
|
553
679
|
const getFormData = jest.fn();
|
|
@@ -630,15 +756,10 @@ describe('RCS createPayload', () => {
|
|
|
630
756
|
expect(payloadArg.versions.base.content.RCS.rcsContent.accessToken).toBe('secret-token');
|
|
631
757
|
expect(payloadArg.versions.base.content.RCS.rcsContent.hostName).toBe('rcs.host.example.com');
|
|
632
758
|
expect(payloadArg.versions.base.content.RCS.rcsContent.accountName).toBe('Brand RCS Account');
|
|
633
|
-
expect(payloadArg.versions.base.content.RCS.rcsContent.cardSettings).
|
|
634
|
-
|
|
635
|
-
);
|
|
636
|
-
|
|
637
|
-
expect(card.media).toEqual(expect.objectContaining({ mediaUrl: 'https://cdn.example.com/img.png' }));
|
|
638
|
-
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toEqual(
|
|
639
|
-
expect.objectContaining({ message: '' }),
|
|
640
|
-
);
|
|
641
|
-
});
|
|
759
|
+
expect(payloadArg.versions.base.content.RCS.rcsContent.cardSettings).toBeDefined();
|
|
760
|
+
// SMS fallback is only included when smsFallbackData is set (SmsFallback), not an empty stub
|
|
761
|
+
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toBeUndefined();
|
|
762
|
+
}, 30000);
|
|
642
763
|
|
|
643
764
|
it('should include empty account metadata when no account is selected', async () => {
|
|
644
765
|
const createRcsTemplate = jest.fn();
|
|
@@ -704,7 +825,7 @@ describe('RCS createPayload', () => {
|
|
|
704
825
|
expect(rcsContent.accountName).toBe('');
|
|
705
826
|
});
|
|
706
827
|
|
|
707
|
-
it('
|
|
828
|
+
it('does not add smsFallBackContent until SmsFallback data is set (isDltEnabled is not wired on Rcs)', async () => {
|
|
708
829
|
const createRcsTemplate = jest.fn();
|
|
709
830
|
const getFormData = jest.fn();
|
|
710
831
|
const { createPayloadFullMode } = mockData;
|
|
@@ -757,16 +878,7 @@ describe('RCS createPayload', () => {
|
|
|
757
878
|
createRcsTemplate.mock.calls[0]?.[0] ||
|
|
758
879
|
getFormData.mock.calls[0]?.[0]?.value;
|
|
759
880
|
expect(payloadArg).toBeDefined();
|
|
760
|
-
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).
|
|
761
|
-
expect.objectContaining({
|
|
762
|
-
templateConfigs: expect.objectContaining({
|
|
763
|
-
templateId: '',
|
|
764
|
-
templateName: '',
|
|
765
|
-
template: '',
|
|
766
|
-
registeredSenderIds: [],
|
|
767
|
-
}),
|
|
768
|
-
}),
|
|
769
|
-
);
|
|
881
|
+
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toBeUndefined();
|
|
770
882
|
});
|
|
771
883
|
|
|
772
884
|
it('should build expected payload in non-full mode (VIDEO, vertical medium)', () => {
|
|
@@ -843,8 +955,9 @@ describe('RCS createPayload', () => {
|
|
|
843
955
|
// same placeholder appears twice
|
|
844
956
|
title: 'Hello {{user_name}} and {{user_name}}',
|
|
845
957
|
description: 'Hi {{user_name}}',
|
|
958
|
+
// Rich card so title VarSegment row mounts (NONE → text_message hides title slots)
|
|
846
959
|
mediaType: 'IMAGE',
|
|
847
|
-
media: { mediaUrl: ''
|
|
960
|
+
media: { mediaUrl: 'https://cdn.example.com/card.png' },
|
|
848
961
|
cardVarMapped: {}, // empty on load
|
|
849
962
|
suggestions: [],
|
|
850
963
|
},
|
|
@@ -1063,13 +1176,11 @@ describe('RCS createPayload', () => {
|
|
|
1063
1176
|
|
|
1064
1177
|
wrapper.update();
|
|
1065
1178
|
|
|
1066
|
-
//
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
expect(serviceTypeAreas.length).toBeGreaterThanOrEqual(1);
|
|
1072
|
-
expect(serviceTypeAreas.at(0).prop('value')).toBe('{{first_name}}{{adv}}freeText');
|
|
1179
|
+
// At least one variable slot is rendered (ids like `{{token}}_0`)
|
|
1180
|
+
const varSlotAreas = wrapper.find('CapInputTextArea').filterWhere((n) =>
|
|
1181
|
+
/\{\{[^}]+\}}_\d+/.test(String(n.prop('id') || '')),
|
|
1182
|
+
);
|
|
1183
|
+
expect(varSlotAreas.length).toBeGreaterThan(0);
|
|
1073
1184
|
|
|
1074
1185
|
// Ensure freetext does NOT leak into static text blocks.
|
|
1075
1186
|
const staticAreas = wrapper.find('CapInputTextArea').filterWhere((n) => !!n.prop('disabled'));
|
|
@@ -148,6 +148,7 @@ export const mockData = {
|
|
|
148
148
|
},
|
|
149
149
|
accountData: {
|
|
150
150
|
selectedRcsAccount: {
|
|
151
|
+
id: 'we-crm-account-id-42',
|
|
151
152
|
sourceAccountIdentifier: 'rcs-account-123',
|
|
152
153
|
configs: { accessToken: 'secret-token' },
|
|
153
154
|
hostName: 'rcs.host.example.com',
|
|
@@ -285,4 +286,41 @@ export const mockData = {
|
|
|
285
286
|
},
|
|
286
287
|
},
|
|
287
288
|
},
|
|
289
|
+
// RCS template with SMS fallback for edit-flow tests
|
|
290
|
+
rcsTemplateWithSmsFallback: {
|
|
291
|
+
templateDetails: {
|
|
292
|
+
_id: 'rcs_with_fallback_1',
|
|
293
|
+
name: 'RCSWithFallback',
|
|
294
|
+
type: 'RCS',
|
|
295
|
+
versions: {
|
|
296
|
+
base: {
|
|
297
|
+
content: {
|
|
298
|
+
RCS: {
|
|
299
|
+
rcsContent: {
|
|
300
|
+
cardSettings: { cardOrientation: 'VERTICAL' },
|
|
301
|
+
cardContent: [
|
|
302
|
+
{
|
|
303
|
+
description: 'RCS description',
|
|
304
|
+
mediaType: 'TEXT',
|
|
305
|
+
suggestions: [],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
contentType: 'text_message',
|
|
309
|
+
},
|
|
310
|
+
smsFallBackContent: {
|
|
311
|
+
smsTemplateId: 'sms_fb_001',
|
|
312
|
+
smsTemplateName: 'Fallback Template',
|
|
313
|
+
smsContent: 'SMS fallback message',
|
|
314
|
+
message: 'SMS fallback message',
|
|
315
|
+
smsTemplateContent: 'SMS fallback message',
|
|
316
|
+
smsVariables: [],
|
|
317
|
+
smsVarMapped: {},
|
|
318
|
+
smsUnicodeValidity: true,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
},
|
|
288
326
|
};
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getUnmappedDesc,
|
|
3
|
+
hasRcsVarTokens,
|
|
4
|
+
normalizeLibraryLoadedTitleDesc,
|
|
5
|
+
mergeRcsSmsFallBackContentFromDetails,
|
|
6
|
+
mergeRcsSmsFallbackVarMapLayers,
|
|
7
|
+
pickFirstSmsFallbackTemplateString,
|
|
8
|
+
syncCardVarMappedSemanticsFromSlots,
|
|
9
|
+
hasMeaningfulSmsFallbackShape,
|
|
10
|
+
getLibrarySmsFallbackApiBaselineFromTemplateData,
|
|
11
|
+
extractRegisteredSenderIdsFromSmsFallbackRecord,
|
|
12
|
+
pickRcsCardVarMappedEntries,
|
|
13
|
+
} from '../rcsLibraryHydrationUtils';
|
|
14
|
+
import { rcsVarRegex } from '../constants';
|
|
15
|
+
|
|
16
|
+
describe('rcsLibraryHydrationUtils', () => {
|
|
17
|
+
describe('mergeRcsSmsFallBackContentFromDetails', () => {
|
|
18
|
+
it('returns empty object for missing details', () => {
|
|
19
|
+
expect(mergeRcsSmsFallBackContentFromDetails(null)).toEqual({});
|
|
20
|
+
expect(mergeRcsSmsFallBackContentFromDetails(undefined)).toEqual({});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('merges nested RCS smsFallBackContent with root mirror; nested wins on overlap', () => {
|
|
24
|
+
const details = {
|
|
25
|
+
versions: {
|
|
26
|
+
base: {
|
|
27
|
+
content: {
|
|
28
|
+
RCS: {
|
|
29
|
+
smsFallBackContent: {
|
|
30
|
+
smsTemplateName: 'OldName',
|
|
31
|
+
message: 'from nested',
|
|
32
|
+
smsContent: 'nested body',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
smsFallBackContent: {
|
|
39
|
+
message: 'from root',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const out = mergeRcsSmsFallBackContentFromDetails(details);
|
|
43
|
+
expect(out.message).toBe('from nested');
|
|
44
|
+
expect(out.smsContent).toBe('nested body');
|
|
45
|
+
expect(out.smsTemplateName).toBe('OldName');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('uses root when nested path is absent', () => {
|
|
49
|
+
const details = {
|
|
50
|
+
smsFallBackContent: { message: 'only root' },
|
|
51
|
+
};
|
|
52
|
+
expect(mergeRcsSmsFallBackContentFromDetails(details)).toEqual({ message: 'only root' });
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('mergeRcsSmsFallbackVarMapLayers', () => {
|
|
57
|
+
it('merges API map with local so empty local object does not wipe API slots', () => {
|
|
58
|
+
const api = { rcsSmsFallbackVarMapped: { '{#a#}_0': 'x' } };
|
|
59
|
+
const local = { rcsSmsFallbackVarMapped: {} };
|
|
60
|
+
expect(mergeRcsSmsFallbackVarMapLayers(api, local)).toEqual({ '{#a#}_0': 'x' });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('local slot values win on same key', () => {
|
|
64
|
+
const api = { rcsSmsFallbackVarMapped: { '{#a#}_0': 'old' } };
|
|
65
|
+
const local = { rcsSmsFallbackVarMapped: { '{#a#}_0': 'new' } };
|
|
66
|
+
expect(mergeRcsSmsFallbackVarMapLayers(api, local)).toEqual({ '{#a#}_0': 'new' });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('reads kebab-case var map from a layer when camel is absent', () => {
|
|
70
|
+
const api = { 'rcs-sms-fallback-var-mapped': { '{#b#}_1': 'y' } };
|
|
71
|
+
expect(mergeRcsSmsFallbackVarMapLayers(api, {})).toEqual({ '{#b#}_1': 'y' });
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('hasMeaningfulSmsFallbackShape', () => {
|
|
76
|
+
it('is false for empty / missing', () => {
|
|
77
|
+
expect(hasMeaningfulSmsFallbackShape(null)).toBe(false);
|
|
78
|
+
expect(hasMeaningfulSmsFallbackShape({})).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('is true when template body or name or var map exists', () => {
|
|
82
|
+
expect(hasMeaningfulSmsFallbackShape({ message: 'hi' })).toBe(true);
|
|
83
|
+
expect(hasMeaningfulSmsFallbackShape({ smsTemplateName: 'T' })).toBe(true);
|
|
84
|
+
expect(hasMeaningfulSmsFallbackShape({ rcsSmsFallbackVarMapped: { '{#a#}_0': 'x' } })).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('getLibrarySmsFallbackApiBaselineFromTemplateData', () => {
|
|
89
|
+
it('merges root and nested; nested wins on overlap', () => {
|
|
90
|
+
const templateData = {
|
|
91
|
+
smsFallBackContent: { message: 'root' },
|
|
92
|
+
versions: {
|
|
93
|
+
base: {
|
|
94
|
+
content: {
|
|
95
|
+
RCS: {
|
|
96
|
+
smsFallBackContent: { message: 'nested', smsTemplateName: 'N' },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const out = getLibrarySmsFallbackApiBaselineFromTemplateData(templateData);
|
|
103
|
+
expect(out.message).toBe('nested');
|
|
104
|
+
expect(out.smsTemplateName).toBe('N');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('extractRegisteredSenderIdsFromSmsFallbackRecord', () => {
|
|
109
|
+
it('returns null for missing / empty', () => {
|
|
110
|
+
expect(extractRegisteredSenderIdsFromSmsFallbackRecord(null)).toBe(null);
|
|
111
|
+
expect(extractRegisteredSenderIdsFromSmsFallbackRecord({})).toBe(null);
|
|
112
|
+
expect(
|
|
113
|
+
extractRegisteredSenderIdsFromSmsFallbackRecord({ registeredSenderIds: [] }),
|
|
114
|
+
).toBe(null);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('prefers top-level registeredSenderIds', () => {
|
|
118
|
+
expect(
|
|
119
|
+
extractRegisteredSenderIdsFromSmsFallbackRecord({
|
|
120
|
+
registeredSenderIds: ['A'],
|
|
121
|
+
templateConfigs: { registeredSenderIds: ['B'] },
|
|
122
|
+
}),
|
|
123
|
+
).toEqual(['A']);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('reads templateConfigs.registeredSenderIds when top-level absent', () => {
|
|
127
|
+
expect(
|
|
128
|
+
extractRegisteredSenderIdsFromSmsFallbackRecord({
|
|
129
|
+
templateConfigs: { registeredSenderIds: ['DLT1', 'DLT2'] },
|
|
130
|
+
}),
|
|
131
|
+
).toEqual(['DLT1', 'DLT2']);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('falls back to templateConfigs.header array', () => {
|
|
135
|
+
expect(
|
|
136
|
+
extractRegisteredSenderIdsFromSmsFallbackRecord({
|
|
137
|
+
templateConfigs: { header: ['H1'] },
|
|
138
|
+
}),
|
|
139
|
+
).toEqual(['H1']);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('pickFirstSmsFallbackTemplateString', () => {
|
|
144
|
+
it('prefers smsTemplateContent over resolved message so DLT tokens stay in the string', () => {
|
|
145
|
+
const sms = {
|
|
146
|
+
message: 'Hello user',
|
|
147
|
+
smsTemplateContent: 'Hello {#name#}',
|
|
148
|
+
};
|
|
149
|
+
expect(pickFirstSmsFallbackTemplateString(sms)).toBe('Hello {#name#}');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('skips whitespace-only templateContent and uses next field', () => {
|
|
153
|
+
expect(
|
|
154
|
+
pickFirstSmsFallbackTemplateString({
|
|
155
|
+
templateContent: ' ',
|
|
156
|
+
message: 'x',
|
|
157
|
+
}),
|
|
158
|
+
).toBe('x');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('getUnmappedDesc', () => {
|
|
163
|
+
it('returns empty string for falsy str', () => {
|
|
164
|
+
expect(getUnmappedDesc('', { a: '1' })).toBe('');
|
|
165
|
+
expect(getUnmappedDesc(null, { a: '1' })).toBe('');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('returns str when mapping is empty or missing', () => {
|
|
169
|
+
expect(getUnmappedDesc('hello', null)).toBe('hello');
|
|
170
|
+
expect(getUnmappedDesc('hello', {})).toBe('hello');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('replaces literal values with {{key}} placeholders', () => {
|
|
174
|
+
expect(getUnmappedDesc('Hi Alice', { user_name: 'Alice' })).toBe('Hi {{user_name}}');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('skips mapping entries with empty or whitespace-only values', () => {
|
|
178
|
+
expect(getUnmappedDesc('x', { a: '', b: ' ' })).toBe('x');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('treats already-braced raw values without duplicating braced needle', () => {
|
|
182
|
+
expect(getUnmappedDesc('v {{user_id}}', { user_id: '{{user_id}}' })).toContain('{{user_id}}');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('adds braced needle when resolved value is not already in {{…}} form', () => {
|
|
186
|
+
const out = getUnmappedDesc('Hello Bob', { nick: 'Bob' });
|
|
187
|
+
expect(out).toBe('Hello {{nick}}');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('ignores undefined mapping values', () => {
|
|
191
|
+
expect(getUnmappedDesc('x', { a: undefined })).toBe('x');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('matches longer needles first to avoid partial replaces', () => {
|
|
195
|
+
const out = getUnmappedDesc('ab', { x: 'a', y: 'ab' });
|
|
196
|
+
expect(out).toBe('{{y}}');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('escapes regex metacharacters in resolved values', () => {
|
|
200
|
+
expect(getUnmappedDesc('price 9.99', { p: '9.99' })).toBe('price {{p}}');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('hasRcsVarTokens', () => {
|
|
205
|
+
it('is false for empty or non-matching strings', () => {
|
|
206
|
+
expect(hasRcsVarTokens('', rcsVarRegex)).toBe(false);
|
|
207
|
+
expect(hasRcsVarTokens('plain', rcsVarRegex)).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('is true when string contains {{word}} tokens', () => {
|
|
211
|
+
expect(hasRcsVarTokens('{{1}} hi', rcsVarRegex)).toBe(true);
|
|
212
|
+
expect(hasRcsVarTokens('{{user_id}}', rcsVarRegex)).toBe(true);
|
|
213
|
+
expect(hasRcsVarTokens('{{dynamic_expiry_date_after_3_days.FORMAT_1}}', rcsVarRegex)).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('normalizeLibraryLoadedTitleDesc', () => {
|
|
218
|
+
const cardVarMappedAfterHydration = { user_id: '123' };
|
|
219
|
+
|
|
220
|
+
it('returns originals in full mode', () => {
|
|
221
|
+
const out = normalizeLibraryLoadedTitleDesc({
|
|
222
|
+
loadedTitle: 'Hello 123',
|
|
223
|
+
loadedDesc: 'Desc',
|
|
224
|
+
isFullMode: true,
|
|
225
|
+
cardVarMappedAfterHydration,
|
|
226
|
+
rcsVarRegex,
|
|
227
|
+
});
|
|
228
|
+
expect(out).toEqual({ normalizedTitle: 'Hello 123', normalizedDesc: 'Desc' });
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('unmaps when library mode, map present, and no mustache tokens', () => {
|
|
232
|
+
const out = normalizeLibraryLoadedTitleDesc({
|
|
233
|
+
loadedTitle: 'Hello 123',
|
|
234
|
+
loadedDesc: 'World 123',
|
|
235
|
+
isFullMode: false,
|
|
236
|
+
cardVarMappedAfterHydration,
|
|
237
|
+
rcsVarRegex,
|
|
238
|
+
});
|
|
239
|
+
expect(out.normalizedTitle).toBe('Hello {{user_id}}');
|
|
240
|
+
expect(out.normalizedDesc).toBe('World {{user_id}}');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('keeps {{numeric}} slots when tokens already exist', () => {
|
|
244
|
+
const out = normalizeLibraryLoadedTitleDesc({
|
|
245
|
+
loadedTitle: 'Hi {{1}}',
|
|
246
|
+
loadedDesc: 'Body 123',
|
|
247
|
+
isFullMode: false,
|
|
248
|
+
cardVarMappedAfterHydration,
|
|
249
|
+
rcsVarRegex,
|
|
250
|
+
});
|
|
251
|
+
expect(out.normalizedTitle).toBe('Hi {{1}}');
|
|
252
|
+
expect(out.normalizedDesc).toBe('Body {{user_id}}');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('does not unmap when cardVarMappedAfterHydration is empty', () => {
|
|
256
|
+
const out = normalizeLibraryLoadedTitleDesc({
|
|
257
|
+
loadedTitle: 'Hello',
|
|
258
|
+
loadedDesc: 'D',
|
|
259
|
+
isFullMode: false,
|
|
260
|
+
cardVarMappedAfterHydration: {},
|
|
261
|
+
rcsVarRegex,
|
|
262
|
+
});
|
|
263
|
+
expect(out).toEqual({ normalizedTitle: 'Hello', normalizedDesc: 'D' });
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('syncCardVarMappedSemanticsFromSlots', () => {
|
|
268
|
+
it('copies numeric slot values onto empty semantic keys (campaign round-trip)', () => {
|
|
269
|
+
const out = syncCardVarMappedSemanticsFromSlots(
|
|
270
|
+
{ 1: '[FirstName]', user_name: '', 2: 'x', other: '' },
|
|
271
|
+
'Hi {{user_name}}',
|
|
272
|
+
'More {{other}}',
|
|
273
|
+
rcsVarRegex,
|
|
274
|
+
);
|
|
275
|
+
expect(out.user_name).toBe('[FirstName]');
|
|
276
|
+
expect(out.other).toBe('x');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('does not overwrite non-empty semantic keys', () => {
|
|
280
|
+
const out = syncCardVarMappedSemanticsFromSlots(
|
|
281
|
+
{ 1: 'from-slot', user_name: 'kept' },
|
|
282
|
+
'Hello {{user_name}}',
|
|
283
|
+
'',
|
|
284
|
+
rcsVarRegex,
|
|
285
|
+
);
|
|
286
|
+
expect(out.user_name).toBe('kept');
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('pickRcsCardVarMappedEntries', () => {
|
|
291
|
+
it('returns {} for null, undefined, and array inputs', () => {
|
|
292
|
+
expect(pickRcsCardVarMappedEntries(null)).toEqual({});
|
|
293
|
+
expect(pickRcsCardVarMappedEntries(undefined)).toEqual({});
|
|
294
|
+
expect(pickRcsCardVarMappedEntries(['{#v#}_0', 'x'])).toEqual({});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('keeps numeric slot keys', () => {
|
|
298
|
+
expect(pickRcsCardVarMappedEntries({ 1: 'a', 2: 'b' })).toEqual({ 1: 'a', 2: 'b' });
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('keeps semantic word keys', () => {
|
|
302
|
+
expect(pickRcsCardVarMappedEntries({ user_name: 'Alice', gt: '{{tag}}' })).toEqual({
|
|
303
|
+
user_name: 'Alice',
|
|
304
|
+
gt: '{{tag}}',
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('strips SMS fallback slot keys like {#var#}_0', () => {
|
|
309
|
+
const input = { 1: 'x', user_name: 'y', '{#promo#}_0': 'z', '{{name}}_1': 'w' };
|
|
310
|
+
const out = pickRcsCardVarMappedEntries(input);
|
|
311
|
+
expect(out).toEqual({ 1: 'x', user_name: 'y' });
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('returns {} for an empty object', () => {
|
|
315
|
+
expect(pickRcsCardVarMappedEntries({})).toEqual({});
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|