@capillarytech/creatives-library 8.0.294 → 8.0.296

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.294",
4
+ "version": "8.0.296",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -75,7 +75,7 @@ const ModifyDeliverySettings = (props) => {
75
75
  const next = cloneDeep(prev);
76
76
  next[DELIVERY_SETTING_KEY_SOURCE_ACCOUNT_IDENTIFIER] = sourceAccountId;
77
77
  const domain = (senderDetailsOptions || []).find(
78
- (d) => d.sourceAccountIdentifier === sourceAccountId,
78
+ (senderDetails) => senderDetails.sourceAccountIdentifier === sourceAccountId,
79
79
  );
80
80
  const first = domain?.gsmSenders?.[0];
81
81
  if (first?.value != null) {
@@ -193,7 +193,7 @@ const ModifyDeliverySettings = (props) => {
193
193
  })));
194
194
  }
195
195
  const domainForAccount = (senderDetailsOptions || []).find(
196
- (senderDetails) => senderDetails.sourceAccountIdentifier === selectedAccId,
196
+ (senderDetails) => senderDetails?.sourceAccountIdentifier === selectedAccId,
197
197
  );
198
198
  return (domainForAccount?.gsmSenders || []).map((gsmSender) => ({
199
199
  label: gsmSender.value,
@@ -379,7 +379,7 @@ const ModifyDeliverySettings = (props) => {
379
379
  }
380
380
 
381
381
  return (
382
- <div className="modify-delivery-settings">
382
+ <CapRow className="modify-delivery-settings">
383
383
  {getChannelFields().map((field, idx) => renderSelectRow({
384
384
  ...field,
385
385
  rowKey: field.titleMessage?.id || `${ROW_KEY_PREFIX}${idx}`,
@@ -390,7 +390,7 @@ const ModifyDeliverySettings = (props) => {
390
390
  <FormattedMessage {...messages.done} />
391
391
  </CapButton>
392
392
  </CapRow>
393
- </div>
393
+ </CapRow>
394
394
  );
395
395
  };
396
396
 
@@ -5,6 +5,7 @@
5
5
  @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
6
6
 
7
7
  .modify-delivery-settings {
8
+ height: 100%;
8
9
  &__field-row {
9
10
  margin-bottom: $CAP_SPACE_24;
10
11
  }
@@ -17,7 +18,7 @@
17
18
  width: 22.85rem;
18
19
 
19
20
  &--disabled {
20
- background-color: #ebecf0;
21
+ background-color: $CAP_G08;
21
22
  pointer-events: none;
22
23
  }
23
24
  }
@@ -163,7 +163,7 @@ const DeliverySettings = (props) => {
163
163
  const summaryObject = getSummaryObject();
164
164
 
165
165
  return (
166
- <div className="delivery-settings">
166
+ <CapRow className="delivery-settings">
167
167
  <CapRow className="delivery-settings__heading-row">
168
168
  <CapHeading type={HEADING_TYPE_H10}>
169
169
  <FormattedMessage {...messages.deliverySettings} />
@@ -218,7 +218,7 @@ const DeliverySettings = (props) => {
218
218
  )}
219
219
  />
220
220
  )}
221
- </div>
221
+ </CapRow>
222
222
  );
223
223
  };
224
224
 
@@ -7,13 +7,13 @@
7
7
  * No cross-repo imports; logic aligned with campaigns domain structure.
8
8
  */
9
9
 
10
- import { get } from 'lodash';
10
+ import get from 'lodash/get';
11
11
  import { CHANNELS } from '../../constants';
12
12
 
13
13
  const getSenderOptions = (contactInfo, key) =>
14
14
  (contactInfo || [])
15
- .filter((c) => c.type === key && c.valid)
16
- .sort((a) => (a.default ? -1 : 0));
15
+ .filter((contactInfoItem) => contactInfoItem.type === key && contactInfoItem.valid)
16
+ .sort((contact) => (contact.default ? -1 : 0));
17
17
 
18
18
  /**
19
19
  * Parse raw API response for one channel into { domains: [...] }
@@ -179,6 +179,11 @@ export const UNSUBSCRIBE_TAG_NAME = 'unsubscribe';
179
179
  export const IN_APP_CHANNEL_NAME = 'in-app';
180
180
  export const MOBILE_PUSH_CHANNEL_NAME = 'mobile push';
181
181
  export const CHANNEL = 'channel';
182
+ export const PHONE_NUMBER = 'PHONE_NUMBER';
183
+ export const DYNAMIC_URL = 'DYNAMIC_URL';
184
+ export const IMAGE = 'IMAGE';
185
+ export const VIDEO = 'VIDEO';
186
+ export const URL = 'URL';
182
187
 
183
188
  // Initial Payload Template (for reference)
184
189
  export const INITIAL_PAYLOAD = {
@@ -65,6 +65,11 @@ import {
65
65
  IN_APP_CHANNEL_NAME,
66
66
  MOBILE_PUSH_CHANNEL_NAME,
67
67
  CHANNEL,
68
+ PHONE_NUMBER,
69
+ DYNAMIC_URL,
70
+ IMAGE,
71
+ VIDEO,
72
+ URL,
68
73
  } from './constants';
69
74
 
70
75
  // Import utilities
@@ -542,6 +547,50 @@ const CommonTestAndPreview = (props) => {
542
547
  * Prepare payload for test message sending based on channel
543
548
  * Updated to match API structure with ouId, sourceEntityId, module, deliverySettings, etc.
544
549
  */
550
+
551
+ const getCarouselMappedData = (carouselData = []) => carouselData.map((carousel) => {
552
+ const {
553
+ bodyText, imageUrl, videoUrl, videoPreviewImg, buttons, mediaType, cardVarMapped, bodyTemplate,
554
+ } = carousel || {};
555
+ const buttonData = buttons.map((button, index) => {
556
+ const {
557
+ type, text, phone_number, urlType, url,
558
+ } = button || {};
559
+ const buttonObj = {
560
+ type,
561
+ text,
562
+ index,
563
+ };
564
+ if (type === PHONE_NUMBER) {
565
+ buttonObj.phoneNumber = phone_number;
566
+ }
567
+ if (type === URL) {
568
+ buttonObj.url = url;
569
+ if (urlType === DYNAMIC_URL) {
570
+ const dynamicUrlPayload = url?.match(/{{(.*?)}}/g);
571
+ buttonObj.dynamicUrlPayload = dynamicUrlPayload?.length === 1 ? dynamicUrlPayload[0] : '';
572
+ }
573
+ }
574
+ return buttonObj;
575
+ });
576
+ return {
577
+ body: bodyText,
578
+ cardVarMapped,
579
+ bodyTemplate,
580
+ media: {
581
+ ...(mediaType?.toLowerCase() === IMAGE.toLowerCase() && {
582
+ url: imageUrl,
583
+ }),
584
+ ...(mediaType?.toLowerCase() === VIDEO.toLowerCase() && {
585
+ url: videoUrl,
586
+ previewUrl: videoPreviewImg,
587
+ }),
588
+ },
589
+ buttons: buttonData,
590
+ mediaType: mediaType?.toUpperCase(),
591
+ };
592
+ });
593
+
545
594
  const prepareTestMessagePayload = (channelType, formDataObj, contentStr, customValuesObj, recipientDetails, previewDataObj, deliverySettingsOverride) => {
546
595
  // Base payload structure common to all channels
547
596
  const basePayload = {
@@ -760,7 +809,7 @@ const CommonTestAndPreview = (props) => {
760
809
 
761
810
  // Add carousel data if mediaType is CAROUSEL
762
811
  if (mediaType === MEDIA_TYPE_CAROUSEL && formDataObj?.carouselData) {
763
- templateConfigs.cards = formDataObj.carouselData;
812
+ templateConfigs.cards = getCarouselMappedData(formDataObj.carouselData);
764
813
  templateConfigs.mediaType = formDataObj?.carouselMediaType?.toUpperCase() || MEDIA_TYPE_IMAGE;
765
814
  }
766
815
 
@@ -10,7 +10,7 @@ import PropTypes from 'prop-types';
10
10
  import { render, screen, waitFor } from '@testing-library/react';
11
11
  import { IntlProvider } from 'react-intl';
12
12
  import CommonTestAndPreview from '../index';
13
- import { CHANNELS } from '../constants';
13
+ import { CHANNELS, MEDIA_TYPE_CAROUSEL } from '../constants';
14
14
 
15
15
  // Mock html-to-text
16
16
  jest.mock('html-to-text', () => ({
@@ -524,6 +524,446 @@ describe('CommonTestAndPreview', () => {
524
524
  });
525
525
  });
526
526
 
527
+ describe('Carousel payload (getCarouselMappedData) - handleSendTestMessage', () => {
528
+ it('should map empty carouselData to empty cards when mediaType is CAROUSEL', async () => {
529
+ const props = {
530
+ ...defaultProps,
531
+ channel: CHANNELS.WHATSAPP,
532
+ formData: {
533
+ templateMsg: 'Message',
534
+ mediaType: MEDIA_TYPE_CAROUSEL,
535
+ carouselData: [],
536
+ },
537
+ };
538
+ render(
539
+ <TestWrapper>
540
+ <CommonTestAndPreview {...props} />
541
+ </TestWrapper>
542
+ );
543
+ await waitFor(() => {
544
+ expect(lastSendTestMessageProps).toBeDefined();
545
+ expect(typeof lastSendTestMessageProps.handleSendTestMessage).toBe('function');
546
+ });
547
+ lastSendTestMessageProps.handleSendTestMessage();
548
+ expect(mockActions.createMessageMetaRequested).toHaveBeenCalled();
549
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
550
+ expect(payload.whatsappMessageContent.templateConfigs.cards).toEqual([]);
551
+ expect(payload.whatsappMessageContent.templateConfigs.mediaType).toBe('IMAGE');
552
+ });
553
+
554
+ it('should map carousel with IMAGE media and bodyText to cards with media.url', async () => {
555
+ const props = {
556
+ ...defaultProps,
557
+ channel: CHANNELS.WHATSAPP,
558
+ formData: {
559
+ templateMsg: 'Message',
560
+ mediaType: MEDIA_TYPE_CAROUSEL,
561
+ carouselMediaType: 'IMAGE',
562
+ carouselData: [
563
+ {
564
+ bodyText: 'Card body',
565
+ imageUrl: 'https://image.example.com/1.jpg',
566
+ mediaType: 'image',
567
+ buttons: [],
568
+ },
569
+ ],
570
+ },
571
+ };
572
+ render(
573
+ <TestWrapper>
574
+ <CommonTestAndPreview {...props} />
575
+ </TestWrapper>
576
+ );
577
+ await waitFor(() => {
578
+ expect(lastSendTestMessageProps).toBeDefined();
579
+ });
580
+ lastSendTestMessageProps.handleSendTestMessage();
581
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
582
+ const cards = payload.whatsappMessageContent.templateConfigs.cards;
583
+ expect(cards).toHaveLength(1);
584
+ expect(cards[0].body).toBe('Card body');
585
+ expect(cards[0].media).toEqual({ url: 'https://image.example.com/1.jpg' });
586
+ expect(cards[0].mediaType).toBe('IMAGE');
587
+ expect(cards[0].buttons).toEqual([]);
588
+ expect(payload.whatsappMessageContent.templateConfigs.mediaType).toBe('IMAGE');
589
+ });
590
+
591
+ it('should map carousel with VIDEO media to cards with url and previewUrl', async () => {
592
+ const props = {
593
+ ...defaultProps,
594
+ channel: CHANNELS.WHATSAPP,
595
+ formData: {
596
+ templateMsg: 'Message',
597
+ mediaType: MEDIA_TYPE_CAROUSEL,
598
+ carouselMediaType: 'VIDEO',
599
+ carouselData: [
600
+ {
601
+ bodyText: 'Video card',
602
+ videoUrl: 'https://video.example.com/1.mp4',
603
+ videoPreviewImg: 'https://preview.example.com/1.jpg',
604
+ mediaType: 'video',
605
+ buttons: [],
606
+ },
607
+ ],
608
+ },
609
+ };
610
+ render(
611
+ <TestWrapper>
612
+ <CommonTestAndPreview {...props} />
613
+ </TestWrapper>
614
+ );
615
+ await waitFor(() => {
616
+ expect(lastSendTestMessageProps).toBeDefined();
617
+ });
618
+ lastSendTestMessageProps.handleSendTestMessage();
619
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
620
+ const cards = payload.whatsappMessageContent.templateConfigs.cards;
621
+ expect(cards).toHaveLength(1);
622
+ expect(cards[0].body).toBe('Video card');
623
+ expect(cards[0].media).toEqual({
624
+ url: 'https://video.example.com/1.mp4',
625
+ previewUrl: 'https://preview.example.com/1.jpg',
626
+ });
627
+ expect(cards[0].mediaType).toBe('VIDEO');
628
+ expect(payload.whatsappMessageContent.templateConfigs.mediaType).toBe('VIDEO');
629
+ });
630
+
631
+ it('should map button type PHONE_NUMBER to buttonObj with phoneNumber', async () => {
632
+ const props = {
633
+ ...defaultProps,
634
+ channel: CHANNELS.WHATSAPP,
635
+ formData: {
636
+ templateMsg: 'Message',
637
+ mediaType: MEDIA_TYPE_CAROUSEL,
638
+ carouselData: [
639
+ {
640
+ bodyText: 'Card',
641
+ mediaType: 'image',
642
+ imageUrl: 'https://img.url',
643
+ buttons: [
644
+ { type: 'PHONE_NUMBER', text: 'Call', phone_number: '+1234567890' },
645
+ ],
646
+ },
647
+ ],
648
+ },
649
+ };
650
+ render(
651
+ <TestWrapper>
652
+ <CommonTestAndPreview {...props} />
653
+ </TestWrapper>
654
+ );
655
+ await waitFor(() => {
656
+ expect(lastSendTestMessageProps).toBeDefined();
657
+ });
658
+ lastSendTestMessageProps.handleSendTestMessage();
659
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
660
+ const buttons = payload.whatsappMessageContent.templateConfigs.cards[0].buttons;
661
+ expect(buttons).toHaveLength(1);
662
+ expect(buttons[0].type).toBe('PHONE_NUMBER');
663
+ expect(buttons[0].text).toBe('Call');
664
+ expect(buttons[0].index).toBe(0);
665
+ expect(buttons[0].phoneNumber).toBe('+1234567890');
666
+ });
667
+
668
+ it('should map button type URL without DYNAMIC_URL to buttonObj with url only', async () => {
669
+ const props = {
670
+ ...defaultProps,
671
+ channel: CHANNELS.WHATSAPP,
672
+ formData: {
673
+ templateMsg: 'Message',
674
+ mediaType: MEDIA_TYPE_CAROUSEL,
675
+ carouselData: [
676
+ {
677
+ bodyText: 'Card',
678
+ mediaType: 'image',
679
+ imageUrl: 'https://img.url',
680
+ buttons: [
681
+ { type: 'URL', text: 'Open', urlType: 'STATIC', url: 'https://example.com' },
682
+ ],
683
+ },
684
+ ],
685
+ },
686
+ };
687
+ render(
688
+ <TestWrapper>
689
+ <CommonTestAndPreview {...props} />
690
+ </TestWrapper>
691
+ );
692
+ await waitFor(() => {
693
+ expect(lastSendTestMessageProps).toBeDefined();
694
+ });
695
+ lastSendTestMessageProps.handleSendTestMessage();
696
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
697
+ const buttons = payload.whatsappMessageContent.templateConfigs.cards[0].buttons;
698
+ expect(buttons[0].type).toBe('URL');
699
+ expect(buttons[0].url).toBe('https://example.com');
700
+ expect(buttons[0].dynamicUrlPayload).toBeUndefined();
701
+ });
702
+
703
+ it('should set dynamicUrlPayload when button type URL and urlType DYNAMIC_URL with single template', async () => {
704
+ const props = {
705
+ ...defaultProps,
706
+ channel: CHANNELS.WHATSAPP,
707
+ formData: {
708
+ templateMsg: 'Message',
709
+ mediaType: MEDIA_TYPE_CAROUSEL,
710
+ carouselData: [
711
+ {
712
+ bodyText: 'Card',
713
+ mediaType: 'image',
714
+ imageUrl: 'https://img.url',
715
+ buttons: [
716
+ { type: 'URL', text: 'Open', urlType: 'DYNAMIC_URL', url: 'https://example.com/{{id}}' },
717
+ ],
718
+ },
719
+ ],
720
+ },
721
+ };
722
+ render(
723
+ <TestWrapper>
724
+ <CommonTestAndPreview {...props} />
725
+ </TestWrapper>
726
+ );
727
+ await waitFor(() => {
728
+ expect(lastSendTestMessageProps).toBeDefined();
729
+ });
730
+ lastSendTestMessageProps.handleSendTestMessage();
731
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
732
+ const buttons = payload.whatsappMessageContent.templateConfigs.cards[0].buttons;
733
+ expect(buttons[0].url).toBe('https://example.com/{{id}}');
734
+ expect(buttons[0].dynamicUrlPayload).toBe('{{id}}');
735
+ });
736
+
737
+ it('should set dynamicUrlPayload to empty when urlType DYNAMIC_URL but multiple or zero matches', async () => {
738
+ const props = {
739
+ ...defaultProps,
740
+ channel: CHANNELS.WHATSAPP,
741
+ formData: {
742
+ templateMsg: 'Message',
743
+ mediaType: MEDIA_TYPE_CAROUSEL,
744
+ carouselData: [
745
+ {
746
+ bodyText: 'Card',
747
+ mediaType: 'image',
748
+ imageUrl: 'https://img.url',
749
+ buttons: [
750
+ { type: 'URL', text: 'Open', urlType: 'DYNAMIC_URL', url: 'https://example.com/{{a}}{{b}}' },
751
+ ],
752
+ },
753
+ ],
754
+ },
755
+ };
756
+ render(
757
+ <TestWrapper>
758
+ <CommonTestAndPreview {...props} />
759
+ </TestWrapper>
760
+ );
761
+ await waitFor(() => {
762
+ expect(lastSendTestMessageProps).toBeDefined();
763
+ });
764
+ lastSendTestMessageProps.handleSendTestMessage();
765
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
766
+ const buttons = payload.whatsappMessageContent.templateConfigs.cards[0].buttons;
767
+ expect(buttons[0].dynamicUrlPayload).toBe('');
768
+ });
769
+
770
+ it('should set dynamicUrlPayload to empty when urlType DYNAMIC_URL and url is null/undefined', async () => {
771
+ const props = {
772
+ ...defaultProps,
773
+ channel: CHANNELS.WHATSAPP,
774
+ formData: {
775
+ templateMsg: 'Message',
776
+ mediaType: MEDIA_TYPE_CAROUSEL,
777
+ carouselData: [
778
+ {
779
+ bodyText: 'Card',
780
+ mediaType: 'image',
781
+ imageUrl: 'https://img.url',
782
+ buttons: [
783
+ { type: 'URL', text: 'Open', urlType: 'DYNAMIC_URL', url: null },
784
+ ],
785
+ },
786
+ ],
787
+ },
788
+ };
789
+ render(
790
+ <TestWrapper>
791
+ <CommonTestAndPreview {...props} />
792
+ </TestWrapper>
793
+ );
794
+ await waitFor(() => {
795
+ expect(lastSendTestMessageProps).toBeDefined();
796
+ });
797
+ lastSendTestMessageProps.handleSendTestMessage();
798
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
799
+ const buttons = payload.whatsappMessageContent.templateConfigs.cards[0].buttons;
800
+ expect(buttons[0].dynamicUrlPayload).toBe('');
801
+ });
802
+
803
+ it('should pass through cardVarMapped and bodyTemplate on each card', async () => {
804
+ const props = {
805
+ ...defaultProps,
806
+ channel: CHANNELS.WHATSAPP,
807
+ formData: {
808
+ templateMsg: 'Message',
809
+ mediaType: MEDIA_TYPE_CAROUSEL,
810
+ carouselData: [
811
+ {
812
+ bodyText: 'Body',
813
+ cardVarMapped: { foo: 'bar' },
814
+ bodyTemplate: 'Body {{foo}}',
815
+ mediaType: 'image',
816
+ imageUrl: 'https://img.url',
817
+ buttons: [],
818
+ },
819
+ ],
820
+ },
821
+ };
822
+ render(
823
+ <TestWrapper>
824
+ <CommonTestAndPreview {...props} />
825
+ </TestWrapper>
826
+ );
827
+ await waitFor(() => {
828
+ expect(lastSendTestMessageProps).toBeDefined();
829
+ });
830
+ lastSendTestMessageProps.handleSendTestMessage();
831
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
832
+ const card = payload.whatsappMessageContent.templateConfigs.cards[0];
833
+ expect(card.cardVarMapped).toEqual({ foo: 'bar' });
834
+ expect(card.bodyTemplate).toBe('Body {{foo}}');
835
+ });
836
+
837
+ it('should not add media url when mediaType is neither image nor video', async () => {
838
+ const props = {
839
+ ...defaultProps,
840
+ channel: CHANNELS.WHATSAPP,
841
+ formData: {
842
+ templateMsg: 'Message',
843
+ mediaType: MEDIA_TYPE_CAROUSEL,
844
+ carouselData: [
845
+ {
846
+ bodyText: 'Card',
847
+ mediaType: 'DOCUMENT',
848
+ imageUrl: 'https://img.url',
849
+ videoUrl: 'https://video.url',
850
+ buttons: [],
851
+ },
852
+ ],
853
+ },
854
+ };
855
+ render(
856
+ <TestWrapper>
857
+ <CommonTestAndPreview {...props} />
858
+ </TestWrapper>
859
+ );
860
+ await waitFor(() => {
861
+ expect(lastSendTestMessageProps).toBeDefined();
862
+ });
863
+ lastSendTestMessageProps.handleSendTestMessage();
864
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
865
+ const card = payload.whatsappMessageContent.templateConfigs.cards[0];
866
+ expect(card.media).toEqual({});
867
+ expect(card.mediaType).toBe('DOCUMENT');
868
+ });
869
+
870
+ it('should handle multiple carousel cards with mixed button types', async () => {
871
+ const props = {
872
+ ...defaultProps,
873
+ channel: CHANNELS.WHATSAPP,
874
+ formData: {
875
+ templateMsg: 'Message',
876
+ mediaType: MEDIA_TYPE_CAROUSEL,
877
+ carouselData: [
878
+ {
879
+ bodyText: 'First',
880
+ mediaType: 'image',
881
+ imageUrl: 'https://first.jpg',
882
+ buttons: [{ type: 'PHONE_NUMBER', text: 'Call', phone_number: '+111' }],
883
+ },
884
+ {
885
+ bodyText: 'Second',
886
+ mediaType: 'video',
887
+ videoUrl: 'https://second.mp4',
888
+ videoPreviewImg: 'https://second-prev.jpg',
889
+ buttons: [{ type: 'URL', text: 'Link', urlType: 'DYNAMIC_URL', url: 'https://x/{{only}}' }],
890
+ },
891
+ ],
892
+ },
893
+ };
894
+ render(
895
+ <TestWrapper>
896
+ <CommonTestAndPreview {...props} />
897
+ </TestWrapper>
898
+ );
899
+ await waitFor(() => {
900
+ expect(lastSendTestMessageProps).toBeDefined();
901
+ });
902
+ lastSendTestMessageProps.handleSendTestMessage();
903
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
904
+ const cards = payload.whatsappMessageContent.templateConfigs.cards;
905
+ expect(cards).toHaveLength(2);
906
+ expect(cards[0].body).toBe('First');
907
+ expect(cards[0].media.url).toBe('https://first.jpg');
908
+ expect(cards[0].buttons[0].phoneNumber).toBe('+111');
909
+ expect(cards[1].body).toBe('Second');
910
+ expect(cards[1].media.url).toBe('https://second.mp4');
911
+ expect(cards[1].media.previewUrl).toBe('https://second-prev.jpg');
912
+ expect(cards[1].buttons[0].dynamicUrlPayload).toBe('{{only}}');
913
+ });
914
+
915
+ it('should use carouselMediaType for templateConfigs.mediaType when provided', async () => {
916
+ const props = {
917
+ ...defaultProps,
918
+ channel: CHANNELS.WHATSAPP,
919
+ formData: {
920
+ templateMsg: 'Message',
921
+ mediaType: MEDIA_TYPE_CAROUSEL,
922
+ carouselMediaType: 'VIDEO',
923
+ carouselData: [
924
+ { bodyText: 'Card', mediaType: 'image', imageUrl: 'https://img.url', buttons: [] },
925
+ ],
926
+ },
927
+ };
928
+ render(
929
+ <TestWrapper>
930
+ <CommonTestAndPreview {...props} />
931
+ </TestWrapper>
932
+ );
933
+ await waitFor(() => {
934
+ expect(lastSendTestMessageProps).toBeDefined();
935
+ });
936
+ lastSendTestMessageProps.handleSendTestMessage();
937
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
938
+ expect(payload.whatsappMessageContent.templateConfigs.mediaType).toBe('VIDEO');
939
+ });
940
+
941
+ it('should default templateConfigs.mediaType to IMAGE when carouselMediaType is missing', async () => {
942
+ const props = {
943
+ ...defaultProps,
944
+ channel: CHANNELS.WHATSAPP,
945
+ formData: {
946
+ templateMsg: 'Message',
947
+ mediaType: MEDIA_TYPE_CAROUSEL,
948
+ carouselData: [
949
+ { bodyText: 'Card', mediaType: 'image', imageUrl: 'https://img.url', buttons: [] },
950
+ ],
951
+ },
952
+ };
953
+ render(
954
+ <TestWrapper>
955
+ <CommonTestAndPreview {...props} />
956
+ </TestWrapper>
957
+ );
958
+ await waitFor(() => {
959
+ expect(lastSendTestMessageProps).toBeDefined();
960
+ });
961
+ lastSendTestMessageProps.handleSendTestMessage();
962
+ const [payload] = mockActions.createMessageMetaRequested.mock.calls[0];
963
+ expect(payload.whatsappMessageContent.templateConfigs.mediaType).toBe('IMAGE');
964
+ });
965
+ });
966
+
527
967
  describe('Component Rendering', () => {
528
968
  it('should render when show is true', async () => {
529
969
  render(