@capillarytech/creatives-library 9.0.28 → 9.0.29-alpha.2
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 +29 -0
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +22 -1
- package/utils/rcsPayloadUtils.js +99 -0
- package/utils/templateVarUtils.js +198 -0
- package/utils/tests/rcsPayloadUtils.test.js +243 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +20 -1
- package/v2Components/CapActionButton/index.js +193 -144
- package/v2Components/CapActionButton/index.scss +232 -14
- 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 +9 -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 +58 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +35 -2
- package/v2Components/CommonTestAndPreview/index.js +770 -231
- 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 +412 -257
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
- 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 +108 -15
- 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/tests/utils.test.js +151 -0
- package/v2Components/CommonTestAndPreview/utils.js +84 -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 +471 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +147 -32
- 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 +27 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -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 +77 -0
- package/v2Containers/CreativesContainer/index.js +328 -106
- package/v2Containers/CreativesContainer/index.scss +102 -1
- package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
- 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/Email/index.js +3 -63
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +4 -9
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/carouselUtils.js +256 -0
- package/v2Containers/Rcs/components/CarouselCard.js +315 -0
- package/v2Containers/Rcs/components/CarouselCardButtons.js +93 -0
- package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
- package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
- package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
- package/v2Containers/Rcs/constants.js +137 -16
- package/v2Containers/Rcs/index.js +1912 -958
- package/v2Containers/Rcs/index.scss +443 -8
- package/v2Containers/Rcs/messages.js +45 -22
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +522 -0
- package/v2Containers/Rcs/tests/CarouselCard.test.js +465 -0
- package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
- package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
- package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
- package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74176 -39409
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
- package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
- package/v2Containers/Rcs/tests/index.test.js +400 -40
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +873 -0
- package/v2Containers/Rcs/tests/utils.test.js +762 -30
- package/v2Containers/Rcs/utils.js +571 -12
- 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 +5 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +196 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +131 -59
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -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 +88 -25
- 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
|
@@ -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,21 +199,21 @@ 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
|
-
//
|
|
198
|
-
renderHelper({
|
|
210
|
+
// Edit mode: radio group is replaced by a plain readonly value, not shown disabled
|
|
211
|
+
renderHelper({
|
|
199
212
|
params,
|
|
200
213
|
rcsData: { templateDetails: mockData.rcsRichCardTemplate }
|
|
201
214
|
});
|
|
202
|
-
|
|
203
|
-
expect(
|
|
215
|
+
expect(renderedComponent.find('CapRadioGroup#select-rcs-template-type')).toHaveLength(0);
|
|
216
|
+
expect(renderedComponent.find('CapHeading.rcs-creative-name-value').length).toBeGreaterThan(0);
|
|
204
217
|
expect(renderedComponent).toMatchSnapshot();
|
|
205
218
|
});
|
|
206
219
|
|
|
@@ -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,375 @@ 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('keeps every carousel variable slot independent, even when they share a literal tag name, 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
|
+
const nameVarAreaForCard = (cardIndex) =>
|
|
557
|
+
wrapper
|
|
558
|
+
.find('CarouselCard')
|
|
559
|
+
.filterWhere((n) => n.prop('cardIndex') === cardIndex)
|
|
560
|
+
.find('CapInputTextArea')
|
|
561
|
+
.filterWhere((n) => {
|
|
562
|
+
const id = String(n.prop('id') || '');
|
|
563
|
+
const key = String(n.key() || '');
|
|
564
|
+
return id.includes('{{name}}_') || key.includes('{{name}}_');
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Card 0 has two `{{name}}` occurrences — title's, then description's, in DOM order — and
|
|
568
|
+
// each is now its own independent slot despite sharing the literal name.
|
|
569
|
+
const card0NameAreas = nameVarAreaForCard(0);
|
|
570
|
+
expect(card0NameAreas.length).toBe(2);
|
|
571
|
+
|
|
572
|
+
act(() => {
|
|
573
|
+
const slotId = nameVarAreas.at(0).prop('id') || nameVarAreas.at(0).key();
|
|
574
|
+
nameVarAreas.at(0).props().onChange({ target: { id: slotId, value: 'madhavi' } });
|
|
575
|
+
});
|
|
576
|
+
wrapper.update();
|
|
577
|
+
|
|
578
|
+
// Only the title resolves — description's own `{{name}}` slot is untouched (independent slot).
|
|
579
|
+
const updatedInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
580
|
+
const inlineCards = updatedInlinePreview.props().content.carouselData;
|
|
581
|
+
expect(inlineCards[0].title).toBe('Hi madhavi');
|
|
582
|
+
expect(inlineCards[0].bodyText).toBe('Welcome {{name}}');
|
|
583
|
+
expect(inlineCards[1].title).toBe('Offer for {{name}}');
|
|
584
|
+
expect(inlineCards[1].bodyText).toBe('Dear {{name}}');
|
|
585
|
+
|
|
586
|
+
// Filling card 0's description slot separately resolves it without disturbing the title.
|
|
587
|
+
act(() => {
|
|
588
|
+
const descSlotId = nameVarAreaForCard(0).at(1).prop('id') || nameVarAreaForCard(0).at(1).key();
|
|
589
|
+
nameVarAreaForCard(0).at(1).props().onChange({ target: { id: descSlotId, value: 'lakshmi' } });
|
|
590
|
+
});
|
|
591
|
+
wrapper.update();
|
|
592
|
+
|
|
593
|
+
const afterDescInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
594
|
+
const afterDescInlineCards = afterDescInlinePreview.props().content.carouselData;
|
|
595
|
+
expect(afterDescInlineCards[0].title).toBe('Hi madhavi');
|
|
596
|
+
expect(afterDescInlineCards[0].bodyText).toBe('Welcome lakshmi');
|
|
597
|
+
|
|
598
|
+
// Slidebox content should mirror the same independent-slot resolution.
|
|
599
|
+
const slidebox = wrapper.find('TestAndPreviewSlidebox').at(0);
|
|
600
|
+
expect(slidebox.exists()).toBe(true);
|
|
601
|
+
const slideboxContent = slidebox.props().content;
|
|
602
|
+
expect(slideboxContent?.carouselData?.length).toBeGreaterThanOrEqual(2);
|
|
603
|
+
expect(slideboxContent.carouselData[0].title).toBe('Hi madhavi');
|
|
604
|
+
expect(slideboxContent.carouselData[0].bodyText).toBe('Welcome lakshmi');
|
|
605
|
+
expect(slideboxContent.carouselData[1].title).toBe('Offer for {{name}}');
|
|
606
|
+
expect(slideboxContent.carouselData[1].bodyText).toBe('Dear {{name}}');
|
|
607
|
+
|
|
608
|
+
// Setting card 1's own `{{name}}` slots independently resolves card 1 without disturbing card 0.
|
|
609
|
+
// Inactive tab panes aren't mounted until switched to, so activate card 1's tab first.
|
|
610
|
+
act(() => {
|
|
611
|
+
wrapper.find('CapTab').prop('onChange')('1');
|
|
612
|
+
});
|
|
613
|
+
wrapper.update();
|
|
614
|
+
const card1NameAreas = nameVarAreaForCard(1);
|
|
615
|
+
expect(card1NameAreas.length).toBe(2);
|
|
616
|
+
act(() => {
|
|
617
|
+
const slotId = card1NameAreas.at(0).prop('id') || card1NameAreas.at(0).key();
|
|
618
|
+
card1NameAreas.at(0).props().onChange({ target: { id: slotId, value: 'raghav' } });
|
|
619
|
+
});
|
|
620
|
+
wrapper.update();
|
|
621
|
+
act(() => {
|
|
622
|
+
const descSlotId = nameVarAreaForCard(1).at(1).prop('id') || nameVarAreaForCard(1).at(1).key();
|
|
623
|
+
nameVarAreaForCard(1).at(1).props().onChange({ target: { id: descSlotId, value: 'suresh' } });
|
|
624
|
+
});
|
|
625
|
+
wrapper.update();
|
|
626
|
+
|
|
627
|
+
const finalInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
628
|
+
const finalInlineCards = finalInlinePreview.props().content.carouselData;
|
|
629
|
+
expect(finalInlineCards[0].title).toBe('Hi madhavi');
|
|
630
|
+
expect(finalInlineCards[0].bodyText).toBe('Welcome lakshmi');
|
|
631
|
+
expect(finalInlineCards[1].title).toBe('Offer for raghav');
|
|
632
|
+
expect(finalInlineCards[1].bodyText).toBe('Dear suresh');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('picking a tag from "Add Labels" for card 2\'s description (a bare semantic token with no numeric backing) shows the value live, without touching card 2\'s title slot', async () => {
|
|
636
|
+
const templateData = {
|
|
637
|
+
name: 'CarouselTagPickTemplate',
|
|
638
|
+
versions: {
|
|
639
|
+
base: {
|
|
640
|
+
content: {
|
|
641
|
+
RCS: {
|
|
642
|
+
rcsContent: {
|
|
643
|
+
cardType: 'CAROUSEL',
|
|
644
|
+
cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
|
|
645
|
+
cardContent: [
|
|
646
|
+
{
|
|
647
|
+
title: 'Hi {{name}}',
|
|
648
|
+
description: 'Welcome {{name}}',
|
|
649
|
+
mediaType: 'IMAGE',
|
|
650
|
+
media: { mediaUrl: 'https://cdn.example.com/c1.png', height: 'MEDIUM' },
|
|
651
|
+
cardVarMapped: {},
|
|
652
|
+
suggestions: [],
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
title: 'sdvsdvs {{first_name_capital}}',
|
|
656
|
+
description: 'dssdfv {{adv}}',
|
|
657
|
+
mediaType: 'IMAGE',
|
|
658
|
+
media: { mediaUrl: 'https://cdn.example.com/c2.png', height: 'MEDIUM' },
|
|
659
|
+
cardVarMapped: {},
|
|
660
|
+
suggestions: [],
|
|
661
|
+
},
|
|
662
|
+
],
|
|
663
|
+
contentType: 'RICHCARD',
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
type: 'RCS',
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const wrapper = mountWithIntl(
|
|
673
|
+
<Provider store={store}>
|
|
674
|
+
<Rcs
|
|
675
|
+
actions={{
|
|
676
|
+
uploadRcsAsset,
|
|
677
|
+
clearRcsMediaAsset,
|
|
678
|
+
createTemplate,
|
|
679
|
+
clearCreateResponse,
|
|
680
|
+
getTemplateDetails,
|
|
681
|
+
editTemplate,
|
|
682
|
+
clearEditResponse,
|
|
683
|
+
}}
|
|
684
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
685
|
+
onCreateComplete={onCreateComplete}
|
|
686
|
+
handleClose={handleClose}
|
|
687
|
+
intl={{ formatMessage }}
|
|
688
|
+
location={{ pathname: '/rcs/edit', query: { type: false, module: 'default' }, search: '' }}
|
|
689
|
+
params={params}
|
|
690
|
+
templateData={templateData}
|
|
691
|
+
rcsData={{}}
|
|
692
|
+
isFullMode={false}
|
|
693
|
+
isEditFlow={false}
|
|
694
|
+
loadingTags={false}
|
|
695
|
+
metaEntities={[]}
|
|
696
|
+
isDltEnabled={false}
|
|
697
|
+
smsRegister={'DLT'}
|
|
698
|
+
/>
|
|
699
|
+
</Provider>,
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
// Flush hydration useEffects
|
|
703
|
+
await act(async () => {
|
|
704
|
+
await Promise.resolve();
|
|
705
|
+
});
|
|
706
|
+
wrapper.update();
|
|
707
|
+
|
|
708
|
+
// Card 2 is inactive until switched to.
|
|
709
|
+
act(() => {
|
|
710
|
+
wrapper.find('CapTab').prop('onChange')('1');
|
|
711
|
+
});
|
|
712
|
+
wrapper.update();
|
|
713
|
+
|
|
714
|
+
const card2 = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
715
|
+
|
|
716
|
+
const descVarArea = card2
|
|
717
|
+
.find('CapInputTextArea')
|
|
718
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{adv}}_'));
|
|
719
|
+
expect(descVarArea.length).toBeGreaterThanOrEqual(1);
|
|
720
|
+
|
|
721
|
+
// Focus card 2's description var slot, exactly like a user clicking into that box.
|
|
722
|
+
act(() => {
|
|
723
|
+
const id = descVarArea.at(0).prop('id');
|
|
724
|
+
descVarArea.at(0).props().onFocus({ target: { id } });
|
|
725
|
+
});
|
|
726
|
+
wrapper.update();
|
|
727
|
+
|
|
728
|
+
// Card 2's description TagList (the second .tag-mock under this card) picks "adv".
|
|
729
|
+
const card2AfterFocus = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
730
|
+
const descTagList = card2AfterFocus.find('.tag-mock').at(1);
|
|
731
|
+
act(() => {
|
|
732
|
+
descTagList.props().onTagSelect('adv');
|
|
733
|
+
});
|
|
734
|
+
wrapper.update();
|
|
735
|
+
|
|
736
|
+
// Live (no save/reload) — the description's own box should now show the picked tag as its value.
|
|
737
|
+
const card2Final = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
738
|
+
const descVarAreaAfterPick = card2Final
|
|
739
|
+
.find('CapInputTextArea')
|
|
740
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{adv}}_'));
|
|
741
|
+
expect(descVarAreaAfterPick.at(0).prop('value')).toBe('{{adv}}');
|
|
742
|
+
|
|
743
|
+
// Title's own slot must be untouched by the description's tag pick.
|
|
744
|
+
const titleVarAreaAfterPick = card2Final
|
|
745
|
+
.find('CapInputTextArea')
|
|
746
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{first_name_capital}}_'));
|
|
747
|
+
expect(titleVarAreaAfterPick.at(0).prop('value')).toBe('');
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('reopening a carousel after a prior save shows card 2\'s description value (persisted under its global positional key), not empty', async () => {
|
|
751
|
+
// Shape of a template that was already saved once: card 2 (index 1) picked "adv" for its
|
|
752
|
+
// description slot in an earlier session. Card 0 has 2 var tokens (title + desc), so card 2's
|
|
753
|
+
// title is global position 3 and its description is global position 4 — key "4".
|
|
754
|
+
const templateData = {
|
|
755
|
+
name: 'CarouselReopenTemplate',
|
|
756
|
+
versions: {
|
|
757
|
+
base: {
|
|
758
|
+
content: {
|
|
759
|
+
RCS: {
|
|
760
|
+
rcsContent: {
|
|
761
|
+
cardType: 'CAROUSEL',
|
|
762
|
+
cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
|
|
763
|
+
cardContent: [
|
|
764
|
+
{
|
|
765
|
+
title: 'Hi {{name}}',
|
|
766
|
+
description: 'Welcome {{name}}',
|
|
767
|
+
mediaType: 'IMAGE',
|
|
768
|
+
media: { mediaUrl: 'https://cdn.example.com/c1.png', height: 'MEDIUM' },
|
|
769
|
+
cardVarMapped: {},
|
|
770
|
+
suggestions: [],
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
title: 'sdvsdvs {{first_name_capital}}',
|
|
774
|
+
description: 'dssdfv {{adv}}',
|
|
775
|
+
mediaType: 'IMAGE',
|
|
776
|
+
media: { mediaUrl: 'https://cdn.example.com/c2.png', height: 'MEDIUM' },
|
|
777
|
+
cardVarMapped: { '4': '{{adv}}' },
|
|
778
|
+
suggestions: [],
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
contentType: 'RICHCARD',
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
type: 'RCS',
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
const wrapper = mountWithIntl(
|
|
791
|
+
<Provider store={store}>
|
|
792
|
+
<Rcs
|
|
793
|
+
actions={{
|
|
794
|
+
uploadRcsAsset,
|
|
795
|
+
clearRcsMediaAsset,
|
|
796
|
+
createTemplate,
|
|
797
|
+
clearCreateResponse,
|
|
798
|
+
getTemplateDetails,
|
|
799
|
+
editTemplate,
|
|
800
|
+
clearEditResponse,
|
|
801
|
+
}}
|
|
802
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
803
|
+
onCreateComplete={onCreateComplete}
|
|
804
|
+
handleClose={handleClose}
|
|
805
|
+
intl={{ formatMessage }}
|
|
806
|
+
location={{ pathname: '/rcs/edit', query: { type: false, module: 'default' }, search: '' }}
|
|
807
|
+
params={params}
|
|
808
|
+
templateData={templateData}
|
|
809
|
+
rcsData={{}}
|
|
810
|
+
isFullMode={false}
|
|
811
|
+
isEditFlow={false}
|
|
812
|
+
loadingTags={false}
|
|
813
|
+
metaEntities={[]}
|
|
814
|
+
isDltEnabled={false}
|
|
815
|
+
smsRegister={'DLT'}
|
|
816
|
+
/>
|
|
817
|
+
</Provider>,
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
await act(async () => {
|
|
821
|
+
await Promise.resolve();
|
|
822
|
+
});
|
|
823
|
+
wrapper.update();
|
|
824
|
+
|
|
825
|
+
act(() => {
|
|
826
|
+
wrapper.find('CapTab').prop('onChange')('1');
|
|
827
|
+
});
|
|
828
|
+
wrapper.update();
|
|
829
|
+
|
|
830
|
+
const card2 = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
831
|
+
const descVarArea = card2
|
|
832
|
+
.find('CapInputTextArea')
|
|
833
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{adv}}_'));
|
|
834
|
+
expect(descVarArea.length).toBeGreaterThanOrEqual(1);
|
|
835
|
+
expect(descVarArea.at(0).prop('value')).toBe('{{adv}}');
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
|
|
464
839
|
describe('RCS orientation tests', () => {
|
|
465
840
|
it('should default to MEDIUM_HEIGHT (vertical) orientation', () => {
|
|
466
841
|
renderHelper({ isEditFlow: false }); // force CapSelect to render
|
|
@@ -547,7 +922,7 @@ describe('RCS orientation tests', () => {
|
|
|
547
922
|
});
|
|
548
923
|
|
|
549
924
|
describe('RCS createPayload', () => {
|
|
550
|
-
it('should build expected payload in full mode (IMAGE
|
|
925
|
+
it('should build expected payload in full mode (IMAGE)', async () => {
|
|
551
926
|
const createRcsTemplate = jest.fn();
|
|
552
927
|
const getTemplateDetails = jest.fn();
|
|
553
928
|
const getFormData = jest.fn();
|
|
@@ -630,15 +1005,10 @@ describe('RCS createPayload', () => {
|
|
|
630
1005
|
expect(payloadArg.versions.base.content.RCS.rcsContent.accessToken).toBe('secret-token');
|
|
631
1006
|
expect(payloadArg.versions.base.content.RCS.rcsContent.hostName).toBe('rcs.host.example.com');
|
|
632
1007
|
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
|
-
});
|
|
1008
|
+
expect(payloadArg.versions.base.content.RCS.rcsContent.cardSettings).toBeDefined();
|
|
1009
|
+
// SMS fallback is only included when smsFallbackData is set (SmsFallback), not an empty stub
|
|
1010
|
+
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toBeUndefined();
|
|
1011
|
+
}, 30000);
|
|
642
1012
|
|
|
643
1013
|
it('should include empty account metadata when no account is selected', async () => {
|
|
644
1014
|
const createRcsTemplate = jest.fn();
|
|
@@ -704,7 +1074,7 @@ describe('RCS createPayload', () => {
|
|
|
704
1074
|
expect(rcsContent.accountName).toBe('');
|
|
705
1075
|
});
|
|
706
1076
|
|
|
707
|
-
it('
|
|
1077
|
+
it('does not add smsFallBackContent until SmsFallback data is set (isDltEnabled is not wired on Rcs)', async () => {
|
|
708
1078
|
const createRcsTemplate = jest.fn();
|
|
709
1079
|
const getFormData = jest.fn();
|
|
710
1080
|
const { createPayloadFullMode } = mockData;
|
|
@@ -757,16 +1127,7 @@ describe('RCS createPayload', () => {
|
|
|
757
1127
|
createRcsTemplate.mock.calls[0]?.[0] ||
|
|
758
1128
|
getFormData.mock.calls[0]?.[0]?.value;
|
|
759
1129
|
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
|
-
);
|
|
1130
|
+
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toBeUndefined();
|
|
770
1131
|
});
|
|
771
1132
|
|
|
772
1133
|
it('should build expected payload in non-full mode (VIDEO, vertical medium)', () => {
|
|
@@ -843,8 +1204,9 @@ describe('RCS createPayload', () => {
|
|
|
843
1204
|
// same placeholder appears twice
|
|
844
1205
|
title: 'Hello {{user_name}} and {{user_name}}',
|
|
845
1206
|
description: 'Hi {{user_name}}',
|
|
1207
|
+
// Rich card so title VarSegment row mounts (NONE → text_message hides title slots)
|
|
846
1208
|
mediaType: 'IMAGE',
|
|
847
|
-
media: { mediaUrl: ''
|
|
1209
|
+
media: { mediaUrl: 'https://cdn.example.com/card.png' },
|
|
848
1210
|
cardVarMapped: {}, // empty on load
|
|
849
1211
|
suggestions: [],
|
|
850
1212
|
},
|
|
@@ -1063,13 +1425,11 @@ describe('RCS createPayload', () => {
|
|
|
1063
1425
|
|
|
1064
1426
|
wrapper.update();
|
|
1065
1427
|
|
|
1066
|
-
//
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
expect(serviceTypeAreas.length).toBeGreaterThanOrEqual(1);
|
|
1072
|
-
expect(serviceTypeAreas.at(0).prop('value')).toBe('{{first_name}}{{adv}}freeText');
|
|
1428
|
+
// At least one variable slot is rendered (ids like `{{token}}_0`)
|
|
1429
|
+
const varSlotAreas = wrapper.find('CapInputTextArea').filterWhere((n) =>
|
|
1430
|
+
/\{\{[^}]+\}}_\d+/.test(String(n.prop('id') || '')),
|
|
1431
|
+
);
|
|
1432
|
+
expect(varSlotAreas.length).toBeGreaterThan(0);
|
|
1073
1433
|
|
|
1074
1434
|
// Ensure freetext does NOT leak into static text blocks.
|
|
1075
1435
|
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
|
};
|