@capillarytech/creatives-library 8.0.329 → 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.
- package/constants/unified.js +0 -14
- package/package.json +1 -1
- package/services/api.js +0 -17
- package/services/tests/api.test.js +0 -85
- package/utils/commonUtils.js +0 -10
- package/utils/tests/commonUtil.test.js +0 -169
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +53 -87
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -20
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -145
- package/v2Components/CommonTestAndPreview/actions.js +0 -10
- package/v2Components/CommonTestAndPreview/constants.js +1 -53
- package/v2Components/CommonTestAndPreview/index.js +168 -1006
- package/v2Components/CommonTestAndPreview/messages.js +3 -147
- package/v2Components/CommonTestAndPreview/reducer.js +0 -10
- package/v2Components/CommonTestAndPreview/sagas.js +6 -15
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +286 -328
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +24 -65
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -168
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
- package/v2Components/FormBuilder/index.js +1 -7
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -8
- package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
- package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
- package/v2Containers/CreativesContainer/constants.js +0 -9
- package/v2Containers/CreativesContainer/index.js +93 -286
- package/v2Containers/CreativesContainer/index.scss +1 -51
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +10 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/Rcs/constants.js +1 -34
- package/v2Containers/Rcs/index.js +884 -999
- package/v2Containers/Rcs/index.scss +6 -85
- package/v2Containers/Rcs/messages.js +1 -10
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +2453 -41456
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
- package/v2Containers/Rcs/tests/index.test.js +38 -41
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +1 -379
- package/v2Containers/Rcs/utils.js +10 -358
- package/v2Containers/Sms/Create/index.js +38 -100
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +128 -609
- package/v2Containers/SmsTrai/Edit/messages.js +4 -9
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2600 -4586
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +2 -63
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +40 -90
- package/v2Containers/Templates/sagas.js +12 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
- package/v2Containers/Templates/tests/sagas.test.js +123 -193
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
- package/v2Containers/TemplatesV2/index.js +23 -86
- package/v2Containers/Whatsapp/index.js +20 -3
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5790
- package/utils/templateVarUtils.js +0 -172
- package/utils/tests/templateVarUtils.test.js +0 -160
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -155
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -93
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -648
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -174
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
- package/v2Components/SmsFallback/constants.js +0 -73
- package/v2Components/SmsFallback/index.js +0 -955
- package/v2Components/SmsFallback/index.scss +0 -265
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -107
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -261
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/index.js +0 -125
- package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -205
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -251
- package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
- package/v2Containers/SmsTrai/Edit/index.scss +0 -121
- package/v2Containers/Templates/TemplatesActionBar.js +0 -101
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
|
@@ -301,6 +301,11 @@ exports[`RCS utils getRCSContent renders RCS content with no media 1`] = `
|
|
|
301
301
|
<div
|
|
302
302
|
className="cap-rcs-creatives"
|
|
303
303
|
>
|
|
304
|
+
<div
|
|
305
|
+
className="CapLabel-n7zsf5-0 gtGqsG rcs-listing-content title"
|
|
306
|
+
fontWeight="bold"
|
|
307
|
+
type="label19"
|
|
308
|
+
/>
|
|
304
309
|
<div
|
|
305
310
|
className="CapLabel-n7zsf5-0 ekUKMg rcs-listing-content desc"
|
|
306
311
|
type="label19"
|
|
@@ -15,14 +15,6 @@ import CapActionButton from '../../../v2Components/CapActionButton';
|
|
|
15
15
|
import { INITIAL_SUGGESTIONS_DATA_STOP } from '../constants';
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
jest.mock('react-redux', () => {
|
|
19
|
-
const actual = jest.requireActual('react-redux');
|
|
20
|
-
return {
|
|
21
|
-
...actual,
|
|
22
|
-
useDispatch: jest.fn(() => jest.fn()),
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
18
|
jest.mock('../../../v2Containers/TagList/index.js', () => ({
|
|
27
19
|
__esModule: true,
|
|
28
20
|
default: (props) => (
|
|
@@ -150,7 +142,7 @@ const renderHelper = (args) => {
|
|
|
150
142
|
isEditFlow={args.isEditFlow || false}
|
|
151
143
|
loadingTags={false}
|
|
152
144
|
metaEntities={[]}
|
|
153
|
-
|
|
145
|
+
getDefaultTags
|
|
154
146
|
isDltEnabled={args.isDltEnabled || false}
|
|
155
147
|
smsRegister={'DLT'}
|
|
156
148
|
/>
|
|
@@ -453,8 +445,8 @@ describe('Creatives rcs test/>', () => {
|
|
|
453
445
|
});
|
|
454
446
|
|
|
455
447
|
it('should call fetchSchemaForEntity when TagList context changes (non-full mode)', () => {
|
|
456
|
-
//
|
|
457
|
-
renderHelper({ isFullMode: false
|
|
448
|
+
// Re-render in non-full mode so TagList is visible
|
|
449
|
+
renderHelper({ isFullMode: false });
|
|
458
450
|
const tagList = renderedComponent.find('.tag-mock').at(0);
|
|
459
451
|
expect(tagList.exists()).toBe(true);
|
|
460
452
|
const before = fetchSchemaForEntity.mock.calls.length;
|
|
@@ -462,7 +454,7 @@ describe('Creatives rcs test/>', () => {
|
|
|
462
454
|
tagList.prop('onContextChange')('ALL');
|
|
463
455
|
});
|
|
464
456
|
const after = fetchSchemaForEntity.mock.calls.length;
|
|
465
|
-
expect(after).
|
|
457
|
+
expect(after).toBe(before + 1);
|
|
466
458
|
const lastArg = fetchSchemaForEntity.mock.calls[after - 1][0];
|
|
467
459
|
expect(lastArg).toEqual(expect.objectContaining({ layout: 'SMS', type: 'TAG', context: 'default' }));
|
|
468
460
|
});
|
|
@@ -638,16 +630,15 @@ describe('RCS createPayload', () => {
|
|
|
638
630
|
expect(payloadArg.versions.base.content.RCS.rcsContent.accessToken).toBe('secret-token');
|
|
639
631
|
expect(payloadArg.versions.base.content.RCS.rcsContent.hostName).toBe('rcs.host.example.com');
|
|
640
632
|
expect(payloadArg.versions.base.content.RCS.rcsContent.accountName).toBe('Brand RCS Account');
|
|
641
|
-
// templateType effect (text_message → rich_card) can reset selectedDimension after hydration on first mount;
|
|
642
|
-
// payload cardSettings follow `selectedDimension` state (see Rcs createPayload).
|
|
643
633
|
expect(payloadArg.versions.base.content.RCS.rcsContent.cardSettings).toEqual(
|
|
644
|
-
expect.objectContaining({ cardOrientation: '
|
|
634
|
+
expect.objectContaining({ cardOrientation: 'HORIZONTAL', mediaAlignment: 'LEFT', cardWidth: 'SMALL' }),
|
|
645
635
|
);
|
|
646
|
-
|
|
647
|
-
expect(card.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
636
|
+
expect(card.mediaType).toBe('IMAGE');
|
|
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
|
+
});
|
|
651
642
|
|
|
652
643
|
it('should include empty account metadata when no account is selected', async () => {
|
|
653
644
|
const createRcsTemplate = jest.fn();
|
|
@@ -713,7 +704,7 @@ describe('RCS createPayload', () => {
|
|
|
713
704
|
expect(rcsContent.accountName).toBe('');
|
|
714
705
|
});
|
|
715
706
|
|
|
716
|
-
it('
|
|
707
|
+
it('should attach DLT template configs when enabled', async () => {
|
|
717
708
|
const createRcsTemplate = jest.fn();
|
|
718
709
|
const getFormData = jest.fn();
|
|
719
710
|
const { createPayloadFullMode } = mockData;
|
|
@@ -766,7 +757,16 @@ describe('RCS createPayload', () => {
|
|
|
766
757
|
createRcsTemplate.mock.calls[0]?.[0] ||
|
|
767
758
|
getFormData.mock.calls[0]?.[0]?.value;
|
|
768
759
|
expect(payloadArg).toBeDefined();
|
|
769
|
-
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).
|
|
760
|
+
expect(payloadArg.versions.base.content.RCS.smsFallBackContent).toEqual(
|
|
761
|
+
expect.objectContaining({
|
|
762
|
+
templateConfigs: expect.objectContaining({
|
|
763
|
+
templateId: '',
|
|
764
|
+
templateName: '',
|
|
765
|
+
template: '',
|
|
766
|
+
registeredSenderIds: [],
|
|
767
|
+
}),
|
|
768
|
+
}),
|
|
769
|
+
);
|
|
770
770
|
});
|
|
771
771
|
|
|
772
772
|
it('should build expected payload in non-full mode (VIDEO, vertical medium)', () => {
|
|
@@ -818,7 +818,7 @@ describe('RCS createPayload', () => {
|
|
|
818
818
|
expect(payloadArg.type).toBe('RCS');
|
|
819
819
|
expect(payloadArg.versions.base.content.RCS.rcsContent.contentType).toBe('RICHCARD');
|
|
820
820
|
expect(payloadArg.versions.base.content.RCS.rcsContent.cardSettings).toEqual(
|
|
821
|
-
expect.objectContaining({ cardOrientation: 'VERTICAL', cardWidth: '
|
|
821
|
+
expect.objectContaining({ cardOrientation: 'VERTICAL', cardWidth: 'SMALL' }),
|
|
822
822
|
);
|
|
823
823
|
expect(card.mediaType).toBe('VIDEO');
|
|
824
824
|
expect(card.media).toEqual(
|
|
@@ -843,9 +843,7 @@ describe('RCS createPayload', () => {
|
|
|
843
843
|
// same placeholder appears twice
|
|
844
844
|
title: 'Hello {{user_name}} and {{user_name}}',
|
|
845
845
|
description: 'Hi {{user_name}}',
|
|
846
|
-
|
|
847
|
-
mediaType: 'IMAGE',
|
|
848
|
-
media: { mediaUrl: 'https://cdn.example.com/card.png' },
|
|
846
|
+
mediaType: 'NONE',
|
|
849
847
|
cardVarMapped: {}, // empty on load
|
|
850
848
|
suggestions: [],
|
|
851
849
|
},
|
|
@@ -913,8 +911,6 @@ describe('RCS createPayload', () => {
|
|
|
913
911
|
});
|
|
914
912
|
|
|
915
913
|
it('should insert TagList label into focused placeholder and sync duplicates in non-full mode', () => {
|
|
916
|
-
// One title slot so numeric slot key + semantic user_name stay aligned (duplicate {{user_name}} would
|
|
917
|
-
// leave other slots on empty "2","3",… keys from hydration).
|
|
918
914
|
const templateData = {
|
|
919
915
|
name: 'DupVarsTemplate2',
|
|
920
916
|
versions: {
|
|
@@ -926,11 +922,9 @@ describe('RCS createPayload', () => {
|
|
|
926
922
|
cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
|
|
927
923
|
cardContent: [
|
|
928
924
|
{
|
|
929
|
-
title: 'Hello {{user_name}}',
|
|
930
|
-
description: 'Hi',
|
|
931
|
-
|
|
932
|
-
mediaType: 'IMAGE',
|
|
933
|
-
media: { mediaUrl: 'https://cdn.example.com/card.png' },
|
|
925
|
+
title: 'Hello {{user_name}} and {{user_name}}',
|
|
926
|
+
description: 'Hi {{user_name}}',
|
|
927
|
+
mediaType: 'NONE',
|
|
934
928
|
cardVarMapped: {},
|
|
935
929
|
suggestions: [],
|
|
936
930
|
},
|
|
@@ -978,12 +972,12 @@ describe('RCS createPayload', () => {
|
|
|
978
972
|
const id = n.prop('id') || '';
|
|
979
973
|
return id.includes('{{user_name}}_');
|
|
980
974
|
});
|
|
981
|
-
expect(titleVarAreas.length).toBeGreaterThanOrEqual(
|
|
975
|
+
expect(titleVarAreas.length).toBeGreaterThanOrEqual(2);
|
|
982
976
|
|
|
983
|
-
//
|
|
977
|
+
// Focus the first variable textarea so TagList knows where to insert
|
|
984
978
|
act(() => {
|
|
985
979
|
const id = titleVarAreas.at(0).prop('id');
|
|
986
|
-
titleVarAreas.at(0).props().onFocus(id);
|
|
980
|
+
titleVarAreas.at(0).props().onFocus({ target: { id } });
|
|
987
981
|
});
|
|
988
982
|
wrapper.update();
|
|
989
983
|
|
|
@@ -999,6 +993,7 @@ describe('RCS createPayload', () => {
|
|
|
999
993
|
return id.includes('{{user_name}}_');
|
|
1000
994
|
});
|
|
1001
995
|
expect(updatedTitleVarAreas.at(0).prop('value')).toBe('{{first_name}}');
|
|
996
|
+
expect(updatedTitleVarAreas.at(1).prop('value')).toBe('{{first_name}}');
|
|
1002
997
|
});
|
|
1003
998
|
|
|
1004
999
|
it('should keep two tags + freetext inside the variable textarea in non-full mode edit (not merged into static text)', () => {
|
|
@@ -1066,11 +1061,13 @@ describe('RCS createPayload', () => {
|
|
|
1066
1061
|
|
|
1067
1062
|
wrapper.update();
|
|
1068
1063
|
|
|
1069
|
-
//
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1064
|
+
// The placeholder {{service_type}} should exist as a variable textarea id, and its value should be the mixed string.
|
|
1065
|
+
const serviceTypeAreas = wrapper.find('TextArea').filterWhere((n) => {
|
|
1066
|
+
const id = n.prop('id') || '';
|
|
1067
|
+
return id.includes('{{service_type}}_');
|
|
1068
|
+
});
|
|
1069
|
+
expect(serviceTypeAreas.length).toBeGreaterThanOrEqual(1);
|
|
1070
|
+
expect(serviceTypeAreas.at(0).prop('value')).toBe('{{first_name}}{{adv}}freeText');
|
|
1074
1071
|
|
|
1075
1072
|
// Ensure freetext does NOT leak into static text blocks.
|
|
1076
1073
|
const staticAreas = wrapper.find('TextArea').filterWhere((n) => !!n.prop('disabled'));
|
|
@@ -148,7 +148,6 @@ export const mockData = {
|
|
|
148
148
|
},
|
|
149
149
|
accountData: {
|
|
150
150
|
selectedRcsAccount: {
|
|
151
|
-
id: 'we-crm-account-id-42',
|
|
152
151
|
sourceAccountIdentifier: 'rcs-account-123',
|
|
153
152
|
configs: { accessToken: 'secret-token' },
|
|
154
153
|
hostName: 'rcs.host.example.com',
|
|
@@ -286,41 +285,4 @@ export const mockData = {
|
|
|
286
285
|
},
|
|
287
286
|
},
|
|
288
287
|
},
|
|
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
|
-
},
|
|
326
288
|
};
|
|
@@ -1,21 +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
|
-
resolveCardVarMappedSlotValue,
|
|
11
|
-
isRcsTextOnlyCardMediaType,
|
|
12
|
-
mapRcsCardContentForConsumerWithResolvedTags,
|
|
13
|
-
resolveRcsCardPreviewStrings,
|
|
14
|
-
sanitizeCardVarMappedValue,
|
|
15
|
-
areAllRcsSmsFallbackVarSlotsFilled,
|
|
16
|
-
buildRcsNumericMustachePlaceholderRegex,
|
|
17
|
-
} from '../utils';
|
|
18
|
-
import { rcsVarRegex } from '../constants';
|
|
4
|
+
import { getRCSContent, getRcsStatusType, getTemplateStatusType } from '../utils';
|
|
19
5
|
import { RCS, RCS_BUTTON_TYPES, STATUS_OPTIONS, RCS_STATUSES } from '../constants';
|
|
20
6
|
import { mockData } from './mockData';
|
|
21
7
|
|
|
@@ -73,25 +59,6 @@ describe('RCS utils - renderRcsSuggestionsPreview', () => {
|
|
|
73
59
|
expect(labels.length).toBe(1);
|
|
74
60
|
expect(labels[0].textContent).toContain('Call');
|
|
75
61
|
});
|
|
76
|
-
|
|
77
|
-
it('renders only divider for unknown suggestion type', () => {
|
|
78
|
-
const template = JSON.parse(JSON.stringify(templateBase));
|
|
79
|
-
template.versions.base.content[RCS].rcsContent.cardContent[0].suggestions = [
|
|
80
|
-
{ type: RCS_BUTTON_TYPES.NONE, text: 'Ignored' },
|
|
81
|
-
];
|
|
82
|
-
render(getRCSContent(template));
|
|
83
|
-
expect(document.querySelectorAll('.rcs-cta-preview').length).toBe(0);
|
|
84
|
-
expect(document.querySelectorAll('.whatsapp-divider').length).toBeGreaterThan(0);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('prefers thumbnailUrl over mediaUrl for preview image', () => {
|
|
88
|
-
const template = JSON.parse(JSON.stringify(templateBase));
|
|
89
|
-
const card = template.versions.base.content[RCS].rcsContent.cardContent[0];
|
|
90
|
-
card.media = { thumbnailUrl: 'thumb.jpg', mediaUrl: 'full.jpg' };
|
|
91
|
-
render(getRCSContent(template));
|
|
92
|
-
const img = document.querySelector('.rcs-listing-image');
|
|
93
|
-
expect(img?.getAttribute('src')).toBe('thumb.jpg');
|
|
94
|
-
});
|
|
95
62
|
});
|
|
96
63
|
|
|
97
64
|
describe('RCS utils', () => {
|
|
@@ -169,349 +136,4 @@ describe('RCS utils', () => {
|
|
|
169
136
|
expect(getTemplateStatusType('some_unknown_status')).toBe('warning');
|
|
170
137
|
});
|
|
171
138
|
});
|
|
172
|
-
|
|
173
|
-
describe('normalizeCardVarMapped', () => {
|
|
174
|
-
it('maps numeric key + placeholder value to tag name', () => {
|
|
175
|
-
expect(normalizeCardVarMapped({ 1: '{{user_id_b64}}' })).toEqual({ user_id_b64: '' });
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('maps numeric key + literal value when orderedTagNames is provided', () => {
|
|
179
|
-
expect(
|
|
180
|
-
normalizeCardVarMapped({ 1: 'hello' }, ['user_id_b64']),
|
|
181
|
-
).toEqual({ user_id_b64: 'hello' });
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('keeps numeric key when no orderedTagNames for literal value', () => {
|
|
185
|
-
expect(normalizeCardVarMapped({ 1: 'hello' })).toEqual({ 1: 'hello' });
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('keeps semantic key + different tag token (TagList value)', () => {
|
|
189
|
-
expect(normalizeCardVarMapped({ myKey: '{{tag}}' })).toEqual({ myKey: '{{tag}}' });
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('maps numeric slot + chosen tag onto template token when ordered names provided', () => {
|
|
193
|
-
expect(
|
|
194
|
-
normalizeCardVarMapped({ 1: '{{FirstName}}' }, ['user_name']),
|
|
195
|
-
).toEqual({ user_name: '{{FirstName}}' });
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('returns {} for null, undefined, or non-object raw', () => {
|
|
199
|
-
expect(normalizeCardVarMapped(null)).toEqual({});
|
|
200
|
-
expect(normalizeCardVarMapped(undefined)).toEqual({});
|
|
201
|
-
expect(normalizeCardVarMapped('not-object')).toEqual({});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('maps numeric key to order slot only when index is within orderedTagNames', () => {
|
|
205
|
-
expect(normalizeCardVarMapped({ 3: 'z' }, ['a', 'b'])).toEqual({ 3: 'z' });
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('does not overwrite a literal slot value with a duplicate semantic placeholder entry', () => {
|
|
209
|
-
expect(
|
|
210
|
-
normalizeCardVarMapped(
|
|
211
|
-
{ 1: 'hello', user_id_b64: '{{user_id_b64}}' },
|
|
212
|
-
['user_id_b64'],
|
|
213
|
-
),
|
|
214
|
-
).toEqual({ user_id_b64: 'hello' });
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe('coalesceCardVarMappedToTemplate', () => {
|
|
219
|
-
it('maps slot 1 to first token name from title', () => {
|
|
220
|
-
expect(
|
|
221
|
-
coalesceCardVarMappedToTemplate(
|
|
222
|
-
{ 1: 'abc' },
|
|
223
|
-
'Hi {{user_name}}',
|
|
224
|
-
'',
|
|
225
|
-
rcsVarRegex,
|
|
226
|
-
),
|
|
227
|
-
).toEqual({ 1: 'abc', user_name: 'abc' });
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('keeps {{1}} style token as key 1', () => {
|
|
231
|
-
expect(
|
|
232
|
-
coalesceCardVarMappedToTemplate(
|
|
233
|
-
{ 1: '' },
|
|
234
|
-
'Hi {{1}}',
|
|
235
|
-
'',
|
|
236
|
-
rcsVarRegex,
|
|
237
|
-
),
|
|
238
|
-
).toEqual({ 1: '' });
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('returns clone when no tokens', () => {
|
|
242
|
-
const raw = { 1: 'x' };
|
|
243
|
-
const out = coalesceCardVarMappedToTemplate(raw, 'no vars', '', rcsVarRegex);
|
|
244
|
-
expect(out).toEqual({ 1: 'x' });
|
|
245
|
-
expect(out).not.toBe(raw);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('merges tokens from title and description', () => {
|
|
249
|
-
const out = coalesceCardVarMappedToTemplate(
|
|
250
|
-
{ a: '1', b: '2' },
|
|
251
|
-
'T {{a}}',
|
|
252
|
-
'D {{b}}',
|
|
253
|
-
rcsVarRegex,
|
|
254
|
-
);
|
|
255
|
-
expect(out).toEqual({ a: '1', b: '2', 1: '1', 2: '2' });
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('returns {} when there are no tokens and raw is null or not an object', () => {
|
|
259
|
-
expect(coalesceCardVarMappedToTemplate(null, 'plain', '', rcsVarRegex)).toEqual({});
|
|
260
|
-
expect(coalesceCardVarMappedToTemplate(undefined, 'plain', '', rcsVarRegex)).toEqual({});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('fills token values from legacy numeric slots when named key is missing', () => {
|
|
264
|
-
expect(
|
|
265
|
-
coalesceCardVarMappedToTemplate(
|
|
266
|
-
{ 1: 'legacy-val' },
|
|
267
|
-
'Hello {{name}}',
|
|
268
|
-
'',
|
|
269
|
-
rcsVarRegex,
|
|
270
|
-
),
|
|
271
|
-
).toEqual({ 1: 'legacy-val', name: 'legacy-val' });
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('maps values when raw is non-null but missing named keys', () => {
|
|
275
|
-
expect(
|
|
276
|
-
coalesceCardVarMappedToTemplate(
|
|
277
|
-
{},
|
|
278
|
-
'X {{a}}',
|
|
279
|
-
'',
|
|
280
|
-
rcsVarRegex,
|
|
281
|
-
),
|
|
282
|
-
).toEqual({ 1: '', a: '' });
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
describe('resolveCardVarMappedSlotValue', () => {
|
|
287
|
-
it('prefers per-slot numeric key when both semantic and numeric are present', () => {
|
|
288
|
-
expect(
|
|
289
|
-
resolveCardVarMappedSlotValue({ user_name: 'A', 1: 'B' }, 'user_name', 0),
|
|
290
|
-
).toBe('B');
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it('falls back to slot 1 when name missing', () => {
|
|
294
|
-
expect(resolveCardVarMappedSlotValue({ 1: 'legacy' }, 'user_name', 0)).toBe('legacy');
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('uses global slot index for second variable', () => {
|
|
298
|
-
expect(resolveCardVarMappedSlotValue({ 2: 'second' }, 'b', 1)).toBe('second');
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it('reads slot from string slot key when varName has no direct mapping', () => {
|
|
302
|
-
expect(resolveCardVarMappedSlotValue({ 1: 'only-slot' }, 'missing', 0)).toBe('only-slot');
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it('reads numeric object key for slot when string slot key is absent', () => {
|
|
306
|
-
expect(resolveCardVarMappedSlotValue({ 2: 'n2' }, 'x', 1)).toBe('n2');
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('returns empty when named key is cleared, not numeric slot fallback', () => {
|
|
310
|
-
expect(
|
|
311
|
-
resolveCardVarMappedSlotValue({ user_name: '', 1: 'old value' }, 'user_name', 0),
|
|
312
|
-
).toBe('');
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('library mode: semantic empty does not hide non-empty numeric slot (campaign / journey payload)', () => {
|
|
316
|
-
expect(
|
|
317
|
-
resolveCardVarMappedSlotValue(
|
|
318
|
-
{ user_id_b64: '', 1: 'selected-from-library' },
|
|
319
|
-
'user_id_b64',
|
|
320
|
-
0,
|
|
321
|
-
true,
|
|
322
|
-
),
|
|
323
|
-
).toBe('selected-from-library');
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
it('empty numeric slot falls back to semantic tag (preview after hydration leaves 1:"")', () => {
|
|
327
|
-
expect(
|
|
328
|
-
resolveCardVarMappedSlotValue(
|
|
329
|
-
{ promotion_points: '{{loyalty_points}}', 1: '' },
|
|
330
|
-
'promotion_points',
|
|
331
|
-
0,
|
|
332
|
-
),
|
|
333
|
-
).toBe('{{loyalty_points}}');
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
describe('mapRcsCardContentForConsumerWithResolvedTags', () => {
|
|
338
|
-
it('sets title and description to resolved tag strings on each card', () => {
|
|
339
|
-
const out = mapRcsCardContentForConsumerWithResolvedTags(
|
|
340
|
-
[
|
|
341
|
-
{
|
|
342
|
-
title: '',
|
|
343
|
-
description: 'Visit {{gt}} discount',
|
|
344
|
-
mediaType: 'NONE',
|
|
345
|
-
cardVarMapped: { gt: '{{loyalty_points}}', 1: '{{loyalty_points}}' },
|
|
346
|
-
},
|
|
347
|
-
],
|
|
348
|
-
{},
|
|
349
|
-
false,
|
|
350
|
-
);
|
|
351
|
-
expect(out[0].title).toBe('');
|
|
352
|
-
expect(out[0].description).toBe('Visit {{loyalty_points}} discount');
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it('merges root rcsCardVarMapped with nested cardVarMapped', () => {
|
|
356
|
-
const out = mapRcsCardContentForConsumerWithResolvedTags(
|
|
357
|
-
[
|
|
358
|
-
{
|
|
359
|
-
title: 'Hi {{first_name}}',
|
|
360
|
-
description: 'Pts {{promotion_points}}',
|
|
361
|
-
mediaType: 'IMAGE',
|
|
362
|
-
cardVarMapped: {
|
|
363
|
-
promotion_points: '{{loyalty_points}}',
|
|
364
|
-
2: '{{loyalty_points}}',
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
],
|
|
368
|
-
{
|
|
369
|
-
first_name: '{{customer_name}}',
|
|
370
|
-
1: '{{customer_name}}',
|
|
371
|
-
},
|
|
372
|
-
false,
|
|
373
|
-
);
|
|
374
|
-
expect(out[0].title).toBe('Hi {{customer_name}}');
|
|
375
|
-
expect(out[0].description).toBe('Pts {{loyalty_points}}');
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
describe('isRcsTextOnlyCardMediaType', () => {
|
|
380
|
-
it('returns true for NONE and TEXT (text message card)', () => {
|
|
381
|
-
expect(isRcsTextOnlyCardMediaType('NONE')).toBe(true);
|
|
382
|
-
expect(isRcsTextOnlyCardMediaType('TEXT')).toBe(true);
|
|
383
|
-
expect(isRcsTextOnlyCardMediaType('text')).toBe(true);
|
|
384
|
-
});
|
|
385
|
-
it('returns false for rich card media', () => {
|
|
386
|
-
expect(isRcsTextOnlyCardMediaType('IMAGE')).toBe(false);
|
|
387
|
-
expect(isRcsTextOnlyCardMediaType('VIDEO')).toBe(false);
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
describe('resolveRcsCardPreviewStrings', () => {
|
|
392
|
-
it('substitutes mapped tags for title and description (campaign preview parity)', () => {
|
|
393
|
-
const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
|
|
394
|
-
'Hi {{first_name}}',
|
|
395
|
-
'Pts {{promotion_points}}',
|
|
396
|
-
{
|
|
397
|
-
first_name: '{{customer_name}}',
|
|
398
|
-
promotion_points: '{{loyalty_points}}',
|
|
399
|
-
1: '{{customer_name}}',
|
|
400
|
-
2: '{{loyalty_points}}',
|
|
401
|
-
},
|
|
402
|
-
true,
|
|
403
|
-
);
|
|
404
|
-
expect(rcsTitle).toBe('Hi {{customer_name}}');
|
|
405
|
-
expect(rcsDesc).toBe('Pts {{loyalty_points}}');
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it('leaves placeholders when map has no value', () => {
|
|
409
|
-
const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
|
|
410
|
-
'{{a}}',
|
|
411
|
-
'{{b}}',
|
|
412
|
-
{},
|
|
413
|
-
false,
|
|
414
|
-
);
|
|
415
|
-
expect(rcsTitle).toBe('{{a}}');
|
|
416
|
-
expect(rcsDesc).toBe('{{b}}');
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it('text-only card: ignores stale title and resolves description from global slot 0', () => {
|
|
420
|
-
const { rcsTitle, rcsDesc } = resolveRcsCardPreviewStrings(
|
|
421
|
-
'Stale {{old_tag}} title',
|
|
422
|
-
'Visit Store for {{gt}} discount',
|
|
423
|
-
{ gt: '{{loyalty_points}}', 1: '{{loyalty_points}}' },
|
|
424
|
-
true,
|
|
425
|
-
true,
|
|
426
|
-
);
|
|
427
|
-
expect(rcsTitle).toBe('');
|
|
428
|
-
expect(rcsDesc).toBe('Visit Store for {{loyalty_points}} discount');
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
describe('sanitizeCardVarMappedValue', () => {
|
|
433
|
-
it('returns empty for null', () => {
|
|
434
|
-
expect(sanitizeCardVarMappedValue(null)).toBe('');
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
it('strips numeric-only self-placeholder stored as value', () => {
|
|
438
|
-
expect(sanitizeCardVarMappedValue('{{1}}')).toBe('');
|
|
439
|
-
expect(sanitizeCardVarMappedValue('{{12}}')).toBe('');
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
it('keeps semantic mustache values from TagList', () => {
|
|
443
|
-
expect(sanitizeCardVarMappedValue('{{FirstName}}')).toBe('{{FirstName}}');
|
|
444
|
-
expect(sanitizeCardVarMappedValue('{{user_name}}')).toBe('{{user_name}}');
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
it('returns original string for non-placeholder values', () => {
|
|
448
|
-
expect(sanitizeCardVarMappedValue(' literal ')).toBe(' literal ');
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
describe('areAllRcsSmsFallbackVarSlotsFilled', () => {
|
|
453
|
-
it('returns true when template empty', () => {
|
|
454
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('', { '{{a}}_0': 'x' })).toBe(true);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
it('returns false when a mustache slot is only whitespace (parity with RCS title/desc vars)', () => {
|
|
458
|
-
expect(
|
|
459
|
-
areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': ' ' }),
|
|
460
|
-
).toBe(false);
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
it('returns false when a mustache slot is missing or empty string', () => {
|
|
464
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', {})).toBe(false);
|
|
465
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': '' })).toBe(
|
|
466
|
-
false,
|
|
467
|
-
);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it('returns false when a DLT {#var#} slot is empty', () => {
|
|
471
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('Hi {#x#}', { '{#x#}_1': ' ' })).toBe(false);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
it('requires mapping for {{optout}} token like any other variable', () => {
|
|
475
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('test {{optout}}', {})).toBe(false);
|
|
476
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('test {{ optout }}', {})).toBe(false);
|
|
477
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('test {{optout}}', { '{{optout}}_1': '{{optout}}' })).toBe(true);
|
|
478
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('test {{optout}}', { '{{optout}}_1': '' })).toBe(false);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it('returns true when template is missing or not a string', () => {
|
|
482
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled(null, {})).toBe(true);
|
|
483
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled(undefined, {})).toBe(true);
|
|
484
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled(123, {})).toBe(true);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
it('returns true when all variable slots have non-whitespace values', () => {
|
|
488
|
-
expect(
|
|
489
|
-
areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': 'Ann' }),
|
|
490
|
-
).toBe(true);
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
it('coerces non-string slot values with String() so DLT/hydration payloads still count as filled', () => {
|
|
494
|
-
expect(
|
|
495
|
-
areAllRcsSmsFallbackVarSlotsFilled('Hello {{name}}', { '{{name}}_1': 42 }),
|
|
496
|
-
).toBe(true);
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
it('accepts legacy 1-based ordinal keys when the only token is at segment index 0 (DLT / API parity)', () => {
|
|
500
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('{#shop#}', { '{#shop#}_1': 'Mart' })).toBe(true);
|
|
501
|
-
expect(areAllRcsSmsFallbackVarSlotsFilled('{#shop#}', { '{#shop#}_1': '' })).toBe(false);
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
describe('buildRcsNumericMustachePlaceholderRegex', () => {
|
|
506
|
-
it('escapes regex metacharacters in numeric name', () => {
|
|
507
|
-
const re = buildRcsNumericMustachePlaceholderRegex('1.5');
|
|
508
|
-
expect(re.test('{{1.5}}')).toBe(true);
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
it('matches simple numeric placeholder', () => {
|
|
512
|
-
const re = buildRcsNumericMustachePlaceholderRegex('2');
|
|
513
|
-
expect(re.test('{{2}}')).toBe(true);
|
|
514
|
-
expect(re.test('{{3}}')).toBe(false);
|
|
515
|
-
});
|
|
516
|
-
});
|
|
517
139
|
});
|