@capillarytech/creatives-library 9.0.73-alpha.sprint-testpreview.22 → 9.1.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/package.json +1 -1
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +58 -1
- package/v2Components/CapTagListWithInput/index.js +64 -35
- package/v2Components/CapTagListWithInput/index.scss +9 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/EmailPreviewContent.js +8 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +7 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +1 -0
- package/v2Components/CommonTestAndPreview/actions.js +1 -1
- package/v2Components/CommonTestAndPreview/index.js +36 -75
- package/v2Components/CommonTestAndPreview/sagas.js +2 -1
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/EmailPreviewContent.test.js +27 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +40 -0
- package/v2Components/CommonTestAndPreview/tests/index.test.js +92 -97
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -0
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +0 -66
- package/v2Components/CommonTestAndPreview/utils.js +0 -14
- package/v2Components/EmailMobilePreview/index.js +8 -1
- package/v2Components/EmailMobilePreview/index.scss +12 -3
- package/v2Components/EmailMobilePreview/tests/index.test.js +31 -0
- package/v2Components/EmailPreviewV2/_emailPreviewV2.scss +37 -5
- package/v2Components/EmailPreviewV2/index.js +18 -6
- package/v2Components/EmailPreviewV2/messages.js +4 -0
- package/v2Components/EmailPreviewV2/tests/index.test.js +43 -0
- package/v2Components/FormBuilder/Classic.js +134 -14
- package/v2Components/FormBuilder/Functional/FormBuilderShell.js +9 -0
- package/v2Components/FormBuilder/Functional/layout/TabsContainer.js +11 -12
- package/v2Components/FormBuilder/Functional/tests/mpush.singlePlatform.test.js +144 -0
- package/v2Components/FormBuilder/Functional/tests/tabsContainer.test.js +17 -5
- package/v2Components/FormBuilder/_formBuilder.scss +21 -0
- package/v2Components/FormBuilder/messages.js +12 -0
- package/v2Components/FormBuilder/tests/mpush.characterization.test.js +45 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +2 -1
- package/v2Containers/CommunicationFlow/Tests/constants.test.js +4 -4
- package/v2Containers/CommunicationFlow/constants.js +1 -0
- package/v2Containers/CommunicationFlow/utils/Tests/mapCommDefinitionToStepData.test.js +2 -1
- package/v2Containers/CommunicationFlow/utils/mapCommDefinitionToStepData.js +1 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +0 -2
- package/v2Containers/CreativesContainer/index.js +7 -1
- package/v2Containers/CreativesContainer/tests/index.test.js +81 -0
- package/v2Containers/Email/index.js +31 -4
- package/v2Containers/Email/initialSchema.js +30 -0
- package/v2Containers/Email/messages.js +8 -0
- package/v2Containers/Email/tests/index.test.js +107 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +172 -8
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +264 -5
- package/v2Containers/EmailWrapper/components/_emailHTMLEditor.scss +29 -0
- package/v2Containers/EmailWrapper/constants.js +5 -0
- package/v2Containers/EmailWrapper/messages.js +24 -0
- package/v2Containers/MobilePush/Create/constants.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +2 -2
- package/v2Containers/MobilePush/Create/test/contentValidity.test.js +58 -0
- package/v2Containers/MobilePush/Edit/index.js +2 -2
- package/v2Containers/MobilePush/Edit/test/contentValidity.test.js +17 -0
- package/v2Containers/MobilePush/commonMethods.js +7 -8
- package/v2Containers/Rcs/index.js +1 -27
- package/v2Containers/TagList/index.js +7 -0
- package/v2Containers/TagList/tests/TagList.test.js +42 -0
- package/v2Containers/Templates/index.js +4 -9
- package/v2Containers/Templates/tests/index.test.js +35 -107
- package/v2Containers/Viber/index.scss +31 -0
- package/v2Containers/Whatsapp/index.js +1 -18
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +2 -2
- package/v2Containers/Zalo/index.js +0 -18
- package/v2Containers/Rcs/tests/testAndPreviewFromListing.test.js +0 -132
- package/v2Containers/Whatsapp/tests/testAndPreviewFromListing.test.js +0 -91
- package/v2Containers/Zalo/tests/testAndPreviewFromListing.test.js +0 -98
|
@@ -481,6 +481,33 @@ describe('EmailPreviewContent', () => {
|
|
|
481
481
|
|
|
482
482
|
expect(container.querySelector('.email-content')).toBeTruthy();
|
|
483
483
|
});
|
|
484
|
+
|
|
485
|
+
it('should not render a pre-header row when preheader is not provided', () => {
|
|
486
|
+
const { container } = render(
|
|
487
|
+
<TestWrapper>
|
|
488
|
+
<ComponentToRender {...defaultProps} />
|
|
489
|
+
</TestWrapper>
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
expect(container.querySelector('.email-preheader')).toBeNull();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('should render the pre-header text when provided', () => {
|
|
496
|
+
const props = {
|
|
497
|
+
...defaultProps,
|
|
498
|
+
preheader: 'Unlock 20% off your next order',
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const { container } = render(
|
|
502
|
+
<TestWrapper>
|
|
503
|
+
<ComponentToRender {...props} />
|
|
504
|
+
</TestWrapper>
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const preheaderElement = container.querySelector('.email-preheader');
|
|
508
|
+
expect(preheaderElement).toBeTruthy();
|
|
509
|
+
expect(preheaderElement.textContent).toBe('Unlock 20% off your next order');
|
|
510
|
+
});
|
|
484
511
|
});
|
|
485
512
|
|
|
486
513
|
describe('Device Handling', () => {
|
|
@@ -46,6 +46,7 @@ jest.mock('../../UnifiedPreview/EmailPreviewContent', () => ({
|
|
|
46
46
|
<div data-testid="email-preview">
|
|
47
47
|
<div data-testid="email-content">{props.content}</div>
|
|
48
48
|
<div data-testid="email-subject">{props.subject}</div>
|
|
49
|
+
<div data-testid="email-preheader">{props.preheader}</div>
|
|
49
50
|
<div data-testid="email-device">{props.device}</div>
|
|
50
51
|
</div>
|
|
51
52
|
),
|
|
@@ -288,6 +289,45 @@ describe('UnifiedPreview', () => {
|
|
|
288
289
|
expect(screen.getByTestId('email-subject')).toHaveTextContent('Email Subject');
|
|
289
290
|
});
|
|
290
291
|
|
|
292
|
+
it('should pass resolvedPreheader through as the preheader prop', () => {
|
|
293
|
+
const props = {
|
|
294
|
+
...defaultProps,
|
|
295
|
+
channel: CHANNELS.EMAIL,
|
|
296
|
+
content: {
|
|
297
|
+
resolvedBody: '<html>Email Content</html>',
|
|
298
|
+
resolvedTitle: 'Email Subject',
|
|
299
|
+
resolvedPreheader: 'Unlock 20% off',
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
render(
|
|
304
|
+
<TestWrapper>
|
|
305
|
+
<ComponentToRender {...props} />
|
|
306
|
+
</TestWrapper>
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
expect(screen.getByTestId('email-preheader')).toHaveTextContent('Unlock 20% off');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should default preheader to an empty string when resolvedPreheader is absent', () => {
|
|
313
|
+
const props = {
|
|
314
|
+
...defaultProps,
|
|
315
|
+
channel: CHANNELS.EMAIL,
|
|
316
|
+
content: {
|
|
317
|
+
resolvedBody: '<html>Email Content</html>',
|
|
318
|
+
resolvedTitle: 'Email Subject',
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
render(
|
|
323
|
+
<TestWrapper>
|
|
324
|
+
<ComponentToRender {...props} />
|
|
325
|
+
</TestWrapper>
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
expect(screen.getByTestId('email-preheader')).toHaveTextContent('');
|
|
329
|
+
});
|
|
330
|
+
|
|
291
331
|
it('should handle empty resolvedBody', () => {
|
|
292
332
|
const props = {
|
|
293
333
|
...defaultProps,
|
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import PropTypes from 'prop-types';
|
|
10
|
-
import {
|
|
11
|
-
render, screen, waitFor, act,
|
|
12
|
-
} from '@testing-library/react';
|
|
10
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
13
11
|
import { IntlProvider } from 'react-intl';
|
|
14
12
|
import CommonTestAndPreview from '../index';
|
|
15
13
|
import { CHANNELS, MEDIA_TYPE_CAROUSEL } from '../constants';
|
|
@@ -72,14 +70,12 @@ jest.mock('../SendTestMessage', () => {
|
|
|
72
70
|
};
|
|
73
71
|
});
|
|
74
72
|
|
|
75
|
-
let lastPreviewSectionProps = null;
|
|
76
73
|
jest.mock('../PreviewSection', () => {
|
|
77
74
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
78
75
|
const ReactLib = require('react');
|
|
79
76
|
return {
|
|
80
77
|
__esModule: true,
|
|
81
|
-
default: function MockPreviewSection(
|
|
82
|
-
lastPreviewSectionProps = props;
|
|
78
|
+
default: function MockPreviewSection() {
|
|
83
79
|
return ReactLib.createElement('div', { 'data-testid': 'preview-section' }, 'Preview Section');
|
|
84
80
|
},
|
|
85
81
|
};
|
|
@@ -203,7 +199,6 @@ describe('CommonTestAndPreview', () => {
|
|
|
203
199
|
lastSendTestMessageProps = null;
|
|
204
200
|
lastLeftPanelContentProps = null;
|
|
205
201
|
lastCustomValuesEditorProps = null;
|
|
206
|
-
lastPreviewSectionProps = null;
|
|
207
202
|
// Reset all mock function implementations
|
|
208
203
|
Object.values(mockActions).forEach((mockFn) => {
|
|
209
204
|
if (jest.isMockFunction(mockFn)) {
|
|
@@ -541,6 +536,45 @@ describe('CommonTestAndPreview', () => {
|
|
|
541
536
|
expect(initialPayload.emailDeliverySettings.channelSettings.channel).toBe(CHANNELS.EMAIL);
|
|
542
537
|
});
|
|
543
538
|
|
|
539
|
+
it('should send the pre-header as messagePreHeader alongside messageSubject/messageBody for EMAIL', async () => {
|
|
540
|
+
render(
|
|
541
|
+
<TestWrapper>
|
|
542
|
+
<CommonTestAndPreview
|
|
543
|
+
{...defaultProps}
|
|
544
|
+
channel={CHANNELS.EMAIL}
|
|
545
|
+
formData={{
|
|
546
|
+
...defaultProps.formData,
|
|
547
|
+
'template-preheader': 'Test Preheader',
|
|
548
|
+
}}
|
|
549
|
+
/>
|
|
550
|
+
</TestWrapper>
|
|
551
|
+
);
|
|
552
|
+
await waitFor(() => {
|
|
553
|
+
expect(lastSendTestMessageProps).toBeDefined();
|
|
554
|
+
expect(typeof lastSendTestMessageProps.handleSendTestMessage).toBe('function');
|
|
555
|
+
});
|
|
556
|
+
lastSendTestMessageProps.handleSendTestMessage();
|
|
557
|
+
expect(mockActions.createMessageMetaRequested).toHaveBeenCalled();
|
|
558
|
+
const [initialPayload] = mockActions.createMessageMetaRequested.mock.calls[0];
|
|
559
|
+
expect(initialPayload.emailMessageContent.messageSubject).toBe('Test Subject');
|
|
560
|
+
expect(initialPayload.emailMessageContent.messagePreHeader).toBe('Test Preheader');
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('should default messagePreHeader to an empty string when no pre-header is present for EMAIL', async () => {
|
|
564
|
+
render(
|
|
565
|
+
<TestWrapper>
|
|
566
|
+
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.EMAIL} />
|
|
567
|
+
</TestWrapper>
|
|
568
|
+
);
|
|
569
|
+
await waitFor(() => {
|
|
570
|
+
expect(lastSendTestMessageProps).toBeDefined();
|
|
571
|
+
expect(typeof lastSendTestMessageProps.handleSendTestMessage).toBe('function');
|
|
572
|
+
});
|
|
573
|
+
lastSendTestMessageProps.handleSendTestMessage();
|
|
574
|
+
const [initialPayload] = mockActions.createMessageMetaRequested.mock.calls[0];
|
|
575
|
+
expect(initialPayload.emailMessageContent.messagePreHeader).toBe('');
|
|
576
|
+
});
|
|
577
|
+
|
|
544
578
|
it('should include delivery settings in payload when handleSendTestMessage is called for SMS', async () => {
|
|
545
579
|
render(
|
|
546
580
|
<TestWrapper>
|
|
@@ -2982,6 +3016,37 @@ describe('CommonTestAndPreview', () => {
|
|
|
2982
3016
|
expect(mockActions.extractTagsRequested).toHaveBeenCalled();
|
|
2983
3017
|
});
|
|
2984
3018
|
|
|
3019
|
+
it('should pass template-preheader separately (not merged into templateSubject) in the extraction payload for EMAIL', async () => {
|
|
3020
|
+
const props = {
|
|
3021
|
+
...defaultProps,
|
|
3022
|
+
channel: CHANNELS.EMAIL,
|
|
3023
|
+
formData: {
|
|
3024
|
+
'template-subject': 'Hello {{name}}',
|
|
3025
|
+
'template-preheader': 'Enjoy {{discount}} off, {{name}}',
|
|
3026
|
+
0: {
|
|
3027
|
+
activeTab: 'base',
|
|
3028
|
+
base: {
|
|
3029
|
+
'template-content': '<html>Your order {{orderId}} is ready</html>',
|
|
3030
|
+
},
|
|
3031
|
+
},
|
|
3032
|
+
},
|
|
3033
|
+
};
|
|
3034
|
+
|
|
3035
|
+
render(
|
|
3036
|
+
<TestWrapper>
|
|
3037
|
+
<CommonTestAndPreview {...props} />
|
|
3038
|
+
</TestWrapper>
|
|
3039
|
+
);
|
|
3040
|
+
|
|
3041
|
+
expect(mockActions.extractTagsRequested).toHaveBeenCalled();
|
|
3042
|
+
const call = mockActions.extractTagsRequested.mock.calls.find(
|
|
3043
|
+
([payload]) => payload?.templatePreheader?.includes('{{discount}}')
|
|
3044
|
+
);
|
|
3045
|
+
expect(call).toBeDefined();
|
|
3046
|
+
expect(call[0].templateSubject).toBe('Hello {{name}}');
|
|
3047
|
+
expect(call[0].templatePreheader).toBe('Enjoy {{discount}} off, {{name}}');
|
|
3048
|
+
});
|
|
3049
|
+
|
|
2985
3050
|
it('should extract tags from SMS content', async () => {
|
|
2986
3051
|
const props = {
|
|
2987
3052
|
...defaultProps,
|
|
@@ -3764,17 +3829,16 @@ describe('CommonTestAndPreview', () => {
|
|
|
3764
3829
|
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3765
3830
|
});
|
|
3766
3831
|
|
|
3767
|
-
it('should
|
|
3832
|
+
it('should include messageSubtitle alongside messageTitle/messageBody in the EMAIL preview payload', async () => {
|
|
3768
3833
|
render(
|
|
3769
3834
|
<TestWrapper>
|
|
3770
3835
|
<CommonTestAndPreview
|
|
3771
3836
|
{...defaultProps}
|
|
3772
|
-
channel={CHANNELS.
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3837
|
+
channel={CHANNELS.EMAIL}
|
|
3838
|
+
formData={{
|
|
3839
|
+
...defaultProps.formData,
|
|
3840
|
+
'template-preheader': 'Enjoy {{discount}}',
|
|
3776
3841
|
}}
|
|
3777
|
-
formData={{}}
|
|
3778
3842
|
/>
|
|
3779
3843
|
</TestWrapper>
|
|
3780
3844
|
);
|
|
@@ -3783,103 +3847,34 @@ describe('CommonTestAndPreview', () => {
|
|
|
3783
3847
|
|
|
3784
3848
|
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3785
3849
|
|
|
3786
|
-
// updatePreviewRequested is called; syncSmsFallbackPreview (which hits Api directly) is NOT called
|
|
3787
3850
|
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
const getPreviewContent = () => lastPreviewSectionProps.unifiedPreviewProps.content;
|
|
3793
|
-
|
|
3794
|
-
const enterValuesAndUpdatePreview = async (values) => {
|
|
3795
|
-
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3796
|
-
act(() => {
|
|
3797
|
-
Object.entries(values).forEach(([tag, value]) => lastCustomValuesEditorProps.handleCustomValueChange(tag, value));
|
|
3798
|
-
});
|
|
3799
|
-
await act(async () => {
|
|
3800
|
-
await lastCustomValuesEditorProps.handleUpdatePreview();
|
|
3801
|
-
});
|
|
3802
|
-
};
|
|
3803
|
-
|
|
3804
|
-
const whatsappContent = {
|
|
3805
|
-
templateHeader: 'Order for {{1}}',
|
|
3806
|
-
templateMsg: 'Hi {{1}}, delivered by {{2}}',
|
|
3807
|
-
};
|
|
3808
|
-
|
|
3809
|
-
it('fills entered values into the WhatsApp header and message', async () => {
|
|
3810
|
-
render(
|
|
3811
|
-
<TestWrapper>
|
|
3812
|
-
<CommonTestAndPreview
|
|
3813
|
-
{...defaultProps}
|
|
3814
|
-
channel={CHANNELS.WHATSAPP}
|
|
3815
|
-
content={whatsappContent}
|
|
3816
|
-
formData={whatsappContent}
|
|
3817
|
-
/>
|
|
3818
|
-
</TestWrapper>
|
|
3819
|
-
);
|
|
3820
|
-
|
|
3821
|
-
await enterValuesAndUpdatePreview({ 1: 'Sh', 2: 'Monday' });
|
|
3822
|
-
|
|
3823
|
-
expect(getPreviewContent().templateHeader).toBe('Order for Sh');
|
|
3824
|
-
expect(getPreviewContent().templateMsg).toBe('Hi Sh, delivered by Monday');
|
|
3825
|
-
});
|
|
3826
|
-
|
|
3827
|
-
it('fills entered values when the preview API returns an object with unresolved {{N}}', async () => {
|
|
3828
|
-
render(
|
|
3829
|
-
<TestWrapper>
|
|
3830
|
-
<CommonTestAndPreview
|
|
3831
|
-
{...defaultProps}
|
|
3832
|
-
channel={CHANNELS.WHATSAPP}
|
|
3833
|
-
content={whatsappContent}
|
|
3834
|
-
formData={whatsappContent}
|
|
3835
|
-
previewData={{ resolvedBody: JSON.stringify(whatsappContent) }}
|
|
3836
|
-
/>
|
|
3837
|
-
</TestWrapper>
|
|
3838
|
-
);
|
|
3839
|
-
|
|
3840
|
-
await enterValuesAndUpdatePreview({ 1: 'Shash', 2: 'Name' });
|
|
3841
|
-
|
|
3842
|
-
expect(getPreviewContent().templateHeader).toBe('Order for Shash');
|
|
3843
|
-
expect(getPreviewContent().templateMsg).toBe('Hi Shash, delivered by Name');
|
|
3851
|
+
const [payload] = mockActions.updatePreviewRequested.mock.calls[0];
|
|
3852
|
+
expect(payload.messageTitle).toBe('Test Subject');
|
|
3853
|
+
expect(payload.messageSubtitle).toBe('Enjoy {{discount}}');
|
|
3854
|
+
expect(payload.messageBody).toBeDefined();
|
|
3844
3855
|
});
|
|
3845
3856
|
|
|
3846
|
-
it('
|
|
3847
|
-
render(
|
|
3848
|
-
<TestWrapper>
|
|
3849
|
-
<CommonTestAndPreview
|
|
3850
|
-
{...defaultProps}
|
|
3851
|
-
channel={CHANNELS.WHATSAPP}
|
|
3852
|
-
content={whatsappContent}
|
|
3853
|
-
formData={whatsappContent}
|
|
3854
|
-
/>
|
|
3855
|
-
</TestWrapper>
|
|
3856
|
-
);
|
|
3857
|
-
|
|
3858
|
-
await enterValuesAndUpdatePreview({ 1: 'Sh', 2: 'Monday' });
|
|
3859
|
-
act(() => {
|
|
3860
|
-
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3861
|
-
});
|
|
3862
|
-
|
|
3863
|
-
expect(getPreviewContent().templateMsg).toBe('Hi {{1}}, delivered by {{2}}');
|
|
3864
|
-
});
|
|
3865
|
-
|
|
3866
|
-
it('fills entered values into an RCS standalone card', async () => {
|
|
3867
|
-
const rcsContent = { templateHeader: 'Store {{1}}', templateMessage: 'Visit store for {{1}} offer' };
|
|
3857
|
+
it('should call updatePreviewRequested with RCS channel when handleDiscardCustomValues is invoked for RCS with fallback', async () => {
|
|
3868
3858
|
render(
|
|
3869
3859
|
<TestWrapper>
|
|
3870
3860
|
<CommonTestAndPreview
|
|
3871
3861
|
{...defaultProps}
|
|
3872
3862
|
channel={CHANNELS.RCS}
|
|
3873
|
-
|
|
3874
|
-
|
|
3863
|
+
smsFallbackContent={{
|
|
3864
|
+
templateContent: 'Fallback {{name}}',
|
|
3865
|
+
content: 'Fallback {{name}}',
|
|
3866
|
+
}}
|
|
3867
|
+
formData={{}}
|
|
3875
3868
|
/>
|
|
3876
3869
|
</TestWrapper>
|
|
3877
3870
|
);
|
|
3878
3871
|
|
|
3879
|
-
await
|
|
3872
|
+
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3880
3873
|
|
|
3881
|
-
|
|
3882
|
-
|
|
3874
|
+
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3875
|
+
|
|
3876
|
+
// updatePreviewRequested is called; syncSmsFallbackPreview (which hits Api directly) is NOT called
|
|
3877
|
+
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3883
3878
|
});
|
|
3884
3879
|
});
|
|
3885
3880
|
});
|
|
@@ -224,6 +224,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
224
224
|
expect(generator.next().value).toEqual(
|
|
225
225
|
call(Api.extractTagsWithMetaData, {
|
|
226
226
|
messageTitle: 'Hello {{name}}',
|
|
227
|
+
messageSubtitle: '',
|
|
227
228
|
messageBody: 'Welcome {{email}}',
|
|
228
229
|
})
|
|
229
230
|
);
|
|
@@ -238,6 +239,25 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
238
239
|
expect(generator.next().done).toBe(true);
|
|
239
240
|
});
|
|
240
241
|
|
|
242
|
+
it('should pass templatePreheader through as messageSubtitle', () => {
|
|
243
|
+
const action = {
|
|
244
|
+
payload: {
|
|
245
|
+
templateSubject: 'Hello {{name}}',
|
|
246
|
+
templatePreheader: 'Enjoy {{discount}}',
|
|
247
|
+
templateContent: 'Welcome {{email}}',
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
const generator = extractTagsSaga(action);
|
|
251
|
+
|
|
252
|
+
expect(generator.next().value).toEqual(
|
|
253
|
+
call(Api.extractTagsWithMetaData, {
|
|
254
|
+
messageTitle: 'Hello {{name}}',
|
|
255
|
+
messageSubtitle: 'Enjoy {{discount}}',
|
|
256
|
+
messageBody: 'Welcome {{email}}',
|
|
257
|
+
})
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
241
261
|
it('should handle empty templateSubject', () => {
|
|
242
262
|
const action = {
|
|
243
263
|
payload: {
|
|
@@ -250,6 +270,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
250
270
|
expect(generator.next().value).toEqual(
|
|
251
271
|
call(Api.extractTagsWithMetaData, {
|
|
252
272
|
messageTitle: '',
|
|
273
|
+
messageSubtitle: '',
|
|
253
274
|
messageBody: 'Welcome {{email}}',
|
|
254
275
|
})
|
|
255
276
|
);
|
|
@@ -268,6 +289,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
268
289
|
expect(generator.next().value).toEqual(
|
|
269
290
|
call(Api.extractTagsWithMetaData, {
|
|
270
291
|
messageTitle: 'Hello {{name}}',
|
|
292
|
+
messageSubtitle: '',
|
|
271
293
|
messageBody: '',
|
|
272
294
|
})
|
|
273
295
|
);
|
|
@@ -284,6 +306,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
284
306
|
expect(generator.next().value).toEqual(
|
|
285
307
|
call(Api.extractTagsWithMetaData, {
|
|
286
308
|
messageTitle: '',
|
|
309
|
+
messageSubtitle: '',
|
|
287
310
|
messageBody: 'Welcome {{email}}',
|
|
288
311
|
})
|
|
289
312
|
);
|
|
@@ -301,6 +324,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
301
324
|
expect(generator.next().value).toEqual(
|
|
302
325
|
call(Api.extractTagsWithMetaData, {
|
|
303
326
|
messageTitle: 'Hello {{name}}',
|
|
327
|
+
messageSubtitle: '',
|
|
304
328
|
messageBody: '',
|
|
305
329
|
})
|
|
306
330
|
);
|
|
@@ -10,9 +10,6 @@ import {
|
|
|
10
10
|
hasTrimmedText,
|
|
11
11
|
getCarouselButtonsWithTitles,
|
|
12
12
|
getCarouselButtonSlotCount,
|
|
13
|
-
mergeSyntheticMustacheTags,
|
|
14
|
-
getWhatsappTagExtractionText,
|
|
15
|
-
mergeZaloUnfilledParamTags,
|
|
16
13
|
} from '../utils';
|
|
17
14
|
|
|
18
15
|
describe('CommonTestAndPreview/utils', () => {
|
|
@@ -200,67 +197,4 @@ describe('CommonTestAndPreview/utils', () => {
|
|
|
200
197
|
expect(getCarouselButtonSlotCount([{ buttons: [{ title: ' ' }] }])).toBe(0);
|
|
201
198
|
});
|
|
202
199
|
});
|
|
203
|
-
|
|
204
|
-
describe('getWhatsappTagExtractionText', () => {
|
|
205
|
-
it('joins header and message', () => {
|
|
206
|
-
expect(getWhatsappTagExtractionText({ templateHeader: 'Order {{1}}', templateMsg: 'Hi {{2}}' }))
|
|
207
|
-
.toBe('Order {{1}} Hi {{2}}');
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('handles missing fields', () => {
|
|
211
|
-
expect(getWhatsappTagExtractionText({ templateMsg: 'Hi {{1}}' })).toBe('Hi {{1}}');
|
|
212
|
-
expect(getWhatsappTagExtractionText(undefined)).toBe('');
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('surfaces unmapped WhatsApp slots as tags alongside API tags', () => {
|
|
216
|
-
const apiTags = [{ name: 'adv', metaData: { userDriven: false }, children: [] }];
|
|
217
|
-
const text = getWhatsappTagExtractionText({
|
|
218
|
-
templateHeader: 'Order {{1}}',
|
|
219
|
-
templateMsg: 'Hi {{adv}}, by {{2}}',
|
|
220
|
-
});
|
|
221
|
-
expect(mergeSyntheticMustacheTags(apiTags, text).map((tag) => tag.name)).toEqual(['adv', '1', '2']);
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
describe('mergeZaloUnfilledParamTags', () => {
|
|
226
|
-
const formData = {
|
|
227
|
-
templateListParams: [
|
|
228
|
-
{ name: 'danh_xung', value: '' },
|
|
229
|
-
{ name: 'name', value: '{{first_name}}' },
|
|
230
|
-
{ name: 'ma_so', value: ' ' },
|
|
231
|
-
],
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
it('adds a tag for each empty param', () => {
|
|
235
|
-
expect(mergeZaloUnfilledParamTags([], formData)).toEqual([
|
|
236
|
-
{ name: 'danh_xung', metaData: { userDriven: false }, children: [] },
|
|
237
|
-
{ name: 'ma_so', metaData: { userDriven: false }, children: [] },
|
|
238
|
-
]);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('keeps API tags and does not duplicate names', () => {
|
|
242
|
-
const apiTags = [{ name: 'danh_xung', metaData: { userDriven: true }, children: [] }];
|
|
243
|
-
const result = mergeZaloUnfilledParamTags(apiTags, formData);
|
|
244
|
-
expect(result.map((tag) => tag.name)).toEqual(['danh_xung', 'ma_so']);
|
|
245
|
-
expect(result[0]).toBe(apiTags[0]);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('returns API tags when there are no params', () => {
|
|
249
|
-
expect(mergeZaloUnfilledParamTags(undefined, {})).toEqual([]);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it('handles missing args, unnamed params and null values', () => {
|
|
253
|
-
expect(mergeZaloUnfilledParamTags()).toEqual([]);
|
|
254
|
-
expect(mergeZaloUnfilledParamTags(null, {
|
|
255
|
-
templateListParams: [{ value: '' }, { name: 'a', value: null }, { name: 'b' }],
|
|
256
|
-
}).map((tag) => tag.name)).toEqual(['a', 'b']);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe('mergeSyntheticMustacheTags', () => {
|
|
261
|
-
it('handles null API tags', () => {
|
|
262
|
-
expect(mergeSyntheticMustacheTags(null, 'Hi {{1}}').map((tag) => tag.name)).toEqual(['1']);
|
|
263
|
-
expect(mergeSyntheticMustacheTags(null, 'no tags')).toEqual([]);
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
200
|
});
|
|
@@ -61,20 +61,6 @@ export const mergeSyntheticMustacheTags = (apiTags = [], rawText = '') => {
|
|
|
61
61
|
return [...(apiTags ?? []), ...synthetic];
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
/** WhatsApp header + body text, scanned for unmapped `{{N}}` slots. */
|
|
65
|
-
export const getWhatsappTagExtractionText = (formDataObj) => (
|
|
66
|
-
`${formDataObj?.templateHeader || ''} ${formDataObj?.templateMsg || ''}`.trim()
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
/** Zalo params left empty on Overview, surfaced as fillable test values (keyed by param name). */
|
|
70
|
-
export const mergeZaloUnfilledParamTags = (apiTags = [], formDataObj = {}) => {
|
|
71
|
-
const existingNames = new Set((apiTags ?? []).map((tag) => tag?.name));
|
|
72
|
-
const unfilledParamTags = (formDataObj?.templateListParams || [])
|
|
73
|
-
.filter((param) => param?.name && !String(param?.value ?? '').trim() && !existingNames.has(param.name))
|
|
74
|
-
.map((param) => ({ name: param.name, metaData: { userDriven: false }, children: [] }));
|
|
75
|
-
return [...(apiTags ?? []), ...unfilledParamTags];
|
|
76
|
-
};
|
|
77
|
-
|
|
78
64
|
/** RCS createMessageMeta: normalize media shape (mediaUrl, thumbnailUrl, height string). */
|
|
79
65
|
export const normalizeRcsTestCardMedia = (media) => {
|
|
80
66
|
if (!media || typeof media !== 'object') return undefined;
|
|
@@ -29,7 +29,7 @@ const deviceAspectRatio = {
|
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
class EmailMobilePreview extends React.Component {
|
|
32
|
+
export class EmailMobilePreview extends React.Component {
|
|
33
33
|
static getDerivedStateFromProps(props) {
|
|
34
34
|
if (props.showAllDevices) {
|
|
35
35
|
return {
|
|
@@ -66,6 +66,7 @@ class EmailMobilePreview extends React.Component {
|
|
|
66
66
|
templateContent,
|
|
67
67
|
enableShowAllDevices,
|
|
68
68
|
templateSubject,
|
|
69
|
+
templatePreHeader,
|
|
69
70
|
} = this.props;
|
|
70
71
|
const copyContent = templateContent && removeLinksFromHtmlContent(templateContent);
|
|
71
72
|
const updateCopyContent = templateContent && iframePreviewAdjustWidth(copyContent);
|
|
@@ -76,6 +77,10 @@ class EmailMobilePreview extends React.Component {
|
|
|
76
77
|
<CapLabel className="email-subject-mobile" type="label2">
|
|
77
78
|
<FormattedMessage {...messages.subject} /> {templateSubject}</CapLabel>
|
|
78
79
|
);
|
|
80
|
+
const emailPreHeaderLabel = templatePreHeader && (
|
|
81
|
+
<CapLabel className="email-preheader-mobile" type="label2">
|
|
82
|
+
<FormattedMessage {...messages.preheader} /> {templatePreHeader}</CapLabel>
|
|
83
|
+
);
|
|
79
84
|
|
|
80
85
|
return (
|
|
81
86
|
<div className="email-mobile-preview-container">
|
|
@@ -100,6 +105,7 @@ class EmailMobilePreview extends React.Component {
|
|
|
100
105
|
<img src={mobileBody} alt="capillary" width="386" />
|
|
101
106
|
<div className={`${messageContainerName} ${emailSubjectLabel ? 'with-subject' : ''}`}>
|
|
102
107
|
{emailSubjectLabel}
|
|
108
|
+
{emailPreHeaderLabel}
|
|
103
109
|
<iframe srcDoc={updateCopyContent} {...deviceAspectRatio.mobile} >
|
|
104
110
|
<p><FormattedMessage {...messages.noIframeSupport}/></p>
|
|
105
111
|
</iframe>
|
|
@@ -127,6 +133,7 @@ class EmailMobilePreview extends React.Component {
|
|
|
127
133
|
EmailMobilePreview.propTypes = {
|
|
128
134
|
templateContent: PropTypes.string,
|
|
129
135
|
templateSubject: PropTypes.string,
|
|
136
|
+
templatePreHeader: PropTypes.string,
|
|
130
137
|
templateData: PropTypes.object,
|
|
131
138
|
templateActions: PropTypes.object,
|
|
132
139
|
onClickShowInAllDevices: PropTypes.func,
|
|
@@ -44,10 +44,19 @@ $classPrefix: email-mobile-preview-container;
|
|
|
44
44
|
justify-content: center;
|
|
45
45
|
}
|
|
46
46
|
.email-subject-mobile {
|
|
47
|
-
width:
|
|
47
|
+
width: 14.1875rem;
|
|
48
48
|
text-align: left;
|
|
49
|
-
margin:
|
|
50
|
-
|
|
49
|
+
margin: 0.5rem 0.25rem 0;
|
|
50
|
+
padding-bottom: 0.5rem;
|
|
51
|
+
border-bottom: 0.0625rem solid #d9d9d9;
|
|
52
|
+
}
|
|
53
|
+
.email-preheader-mobile {
|
|
54
|
+
width: 14.1875rem;
|
|
55
|
+
text-align: left;
|
|
56
|
+
margin: 0.5rem 0.25rem 0;
|
|
57
|
+
padding-bottom: 0.5rem;
|
|
58
|
+
border-bottom: 0.0625rem solid #d9d9d9;
|
|
59
|
+
color: #8c8c8c;
|
|
51
60
|
}
|
|
52
61
|
// .mobile-message-container {
|
|
53
62
|
// position: absolute;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { shallow } from 'enzyme';
|
|
3
|
+
import { EmailMobilePreview } from '../index';
|
|
4
|
+
|
|
5
|
+
describe('<EmailMobilePreview />', () => {
|
|
6
|
+
const baseProps = {
|
|
7
|
+
templateContent: '<p>Body</p>',
|
|
8
|
+
templateActions: {
|
|
9
|
+
getTemplateDetails: jest.fn(),
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it('does not render a pre-header label when templatePreHeader is not provided', () => {
|
|
14
|
+
const wrapper = shallow(<EmailMobilePreview {...baseProps} templateSubject="Hello" />);
|
|
15
|
+
expect(wrapper.find('.email-preheader-mobile')).toHaveLength(0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('renders the pre-header label when templatePreHeader is provided', () => {
|
|
19
|
+
const wrapper = shallow(
|
|
20
|
+
<EmailMobilePreview {...baseProps} templateSubject="Hello" templatePreHeader="Enjoy 20% off" />
|
|
21
|
+
);
|
|
22
|
+
const label = wrapper.find('.email-preheader-mobile');
|
|
23
|
+
expect(label).toHaveLength(1);
|
|
24
|
+
expect(label.text()).toContain('Enjoy 20% off');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('renders the pre-header label even when subject is absent', () => {
|
|
28
|
+
const wrapper = shallow(<EmailMobilePreview {...baseProps} templatePreHeader="Enjoy 20% off" />);
|
|
29
|
+
expect(wrapper.find('.email-preheader-mobile')).toHaveLength(1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
@import '~@capillarytech/cap-ui-library/styles/_variables';
|
|
2
|
+
|
|
1
3
|
$classPrefix: email-preview-v2;
|
|
4
|
+
$preheaderMutedColor: #8c8c8c;
|
|
5
|
+
|
|
2
6
|
.#{$classPrefix}:not(.email-mobile-preview-container){
|
|
3
7
|
#emailPreivew-container {
|
|
4
8
|
position: relative;
|
|
@@ -54,19 +58,47 @@ $classPrefix: email-preview-v2;
|
|
|
54
58
|
.email-subject-mobile {
|
|
55
59
|
width: 310px;
|
|
56
60
|
text-align: left;
|
|
57
|
-
margin:
|
|
58
|
-
|
|
61
|
+
margin: 0.5rem 0.25rem 0;
|
|
62
|
+
padding-bottom: 0.5rem;
|
|
63
|
+
border-bottom: 0.0625rem solid $CAP_COLOR_16;
|
|
59
64
|
}
|
|
60
65
|
.email-subject-tablet {
|
|
61
66
|
width: 450px;
|
|
62
67
|
text-align: left;
|
|
63
|
-
margin:
|
|
64
|
-
|
|
68
|
+
margin: 0.5rem 0.25rem 0;
|
|
69
|
+
padding-bottom: 0.5rem;
|
|
70
|
+
border-bottom: 0.0625rem solid $CAP_COLOR_16;
|
|
65
71
|
}
|
|
66
72
|
.email-subject-desktop {
|
|
67
73
|
text-align: left;
|
|
68
74
|
margin-left: 6%;
|
|
69
|
-
margin-
|
|
75
|
+
margin-right: 6%;
|
|
76
|
+
padding-bottom: 0.5rem;
|
|
77
|
+
border-bottom: 0.0625rem solid $CAP_COLOR_16;
|
|
78
|
+
}
|
|
79
|
+
.email-preheader-mobile {
|
|
80
|
+
width: 19.375rem;
|
|
81
|
+
text-align: left;
|
|
82
|
+
margin: 0.5rem 0.25rem 0;
|
|
83
|
+
padding-bottom: 0.5rem;
|
|
84
|
+
border-bottom: 0.0625rem solid $CAP_COLOR_16;
|
|
85
|
+
color: $preheaderMutedColor;
|
|
86
|
+
}
|
|
87
|
+
.email-preheader-tablet {
|
|
88
|
+
width: 28.125rem;
|
|
89
|
+
text-align: left;
|
|
90
|
+
margin: 0.5rem 0.25rem 0;
|
|
91
|
+
padding-bottom: 0.5rem;
|
|
92
|
+
border-bottom: 0.0625rem solid $CAP_COLOR_16;
|
|
93
|
+
color: $preheaderMutedColor;
|
|
94
|
+
}
|
|
95
|
+
.email-preheader-desktop {
|
|
96
|
+
text-align: left;
|
|
97
|
+
margin-left: 6%;
|
|
98
|
+
margin-right: 6%;
|
|
99
|
+
padding-bottom: 0.5rem;
|
|
100
|
+
border-bottom: 0.0625rem solid $CAP_COLOR_16;
|
|
101
|
+
color: $preheaderMutedColor;
|
|
70
102
|
}
|
|
71
103
|
.tablet-message-container {
|
|
72
104
|
position: absolute;
|