@capillarytech/creatives-library 8.0.200 → 8.0.201

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.
@@ -44,7 +44,6 @@ export const REGISTRATION_CUSTOM_FIELD = 'Registration custom fields';
44
44
  export const GIFT_CARDS = 'GIFT_CARDS';
45
45
  export const PROMO_ENGINE = 'PROMO_ENGINE';
46
46
  export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
47
- export const ENABLE_NEW_MPUSH = 'ENABLE_NEW_MPUSH';
48
47
  export const CUSTOM_TAG = 'CustomTagMessage';
49
48
  export const CUSTOMER_EXTENDED_FIELD = 'Customer extended fields';
50
49
  export const EXTENDED_TAG = 'ExtendedTagMessage';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.200",
4
+ "version": "8.0.201",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/utils/common.js CHANGED
@@ -21,8 +21,7 @@ import {
21
21
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
22
22
  BADGES_ISSUE,
23
23
  ENABLE_WECHAT,
24
- LIQUID_SUPPORT,
25
- ENABLE_NEW_MPUSH
24
+ LIQUID_SUPPORT
26
25
  } from '../constants/unified';
27
26
  import { apiMessageFormatHandler } from './commonUtils';
28
27
 
@@ -126,11 +125,6 @@ export const isEmailUnsubscribeTagMandatory = Auth.hasFeatureAccess.bind(
126
125
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
127
126
  );
128
127
 
129
- export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
130
- null,
131
- ENABLE_NEW_MPUSH,
132
- );
133
-
134
128
  //filtering tags based on scope
135
129
  export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
136
130
  (tag) => !tagsToFilter?.includes(tag?.definition?.value)
@@ -62,12 +62,33 @@ const createMobilePushPayload = ({
62
62
  throw new Error(intl.formatMessage(messages.templateNameEmptyError));
63
63
  }
64
64
 
65
- // Validate content
66
- if (isAndroidSupported && (!androidContent?.title || !androidContent?.message)) {
67
- throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
68
- }
69
- if (isIosSupported && (!iosContent?.title || !iosContent?.message)) {
70
- throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
65
+ // Validate content - allow single platform if explicitly allowed
66
+ const allowSinglePlatform = options?.allowSinglePlatform || false;
67
+
68
+ if (!allowSinglePlatform) {
69
+ // Normal validation: require all supported platforms to have content
70
+ if (isAndroidSupported && (!androidContent?.title || !androidContent?.message)) {
71
+ throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
72
+ }
73
+ if (isIosSupported && (!iosContent?.title || !iosContent?.message)) {
74
+ throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
75
+ }
76
+ } else {
77
+ // Single platform mode: require at least one platform to have content
78
+ const hasAndroidContent = isAndroidSupported && androidContent?.title?.trim() && androidContent?.message?.trim();
79
+ const hasIosContent = isIosSupported && iosContent?.title?.trim() && iosContent?.message?.trim();
80
+
81
+ if (!hasAndroidContent && !hasIosContent) {
82
+ throw new Error(intl.formatMessage(messages.singlePlatformContentMissing));
83
+ }
84
+
85
+ // Validate individual platforms that are supported but have incomplete content
86
+ if (isAndroidSupported && !hasAndroidContent && androidContent && (androidContent.title || androidContent.message)) {
87
+ throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
88
+ }
89
+ if (isIosSupported && !hasIosContent && iosContent && (iosContent.title || iosContent.message)) {
90
+ throw new Error(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
91
+ }
71
92
  }
72
93
 
73
94
  // Ensure imageSrc has the required properties
@@ -99,7 +120,7 @@ const createMobilePushPayload = ({
99
120
  };
100
121
 
101
122
  // Build Android content
102
- if (isAndroidSupported) {
123
+ if (isAndroidSupported && androidContent) {
103
124
  payload.versions.base.ANDROID = buildPlatformContent(
104
125
  androidContent,
105
126
  safeImageSrc.androidImageSrc,
@@ -109,7 +130,7 @@ const createMobilePushPayload = ({
109
130
  }
110
131
 
111
132
  // Build iOS content
112
- if (isIosSupported) {
133
+ if (isIosSupported && iosContent) {
113
134
  payload.versions.base.IOS = buildPlatformContent(
114
135
  iosContent,
115
136
  safeImageSrc.iosImageSrc,
@@ -261,6 +261,59 @@ describe('createMobilePushPayload', () => {
261
261
  accountData: unsupportedAccountData,
262
262
  })).not.toThrow();
263
263
  });
264
+
265
+ // Single Platform Mode Tests
266
+ it('should allow single platform when allowSinglePlatform is true - Android only', () => {
267
+ expect(() => callWithIntl({
268
+ templateName: 'Test',
269
+ androidContent: { title: 'Title', message: 'Message' },
270
+ iosContent: null,
271
+ accountData: mockAccountData,
272
+ options: { allowSinglePlatform: true },
273
+ })).not.toThrow();
274
+ });
275
+
276
+ it('should allow single platform when allowSinglePlatform is true - iOS only', () => {
277
+ expect(() => callWithIntl({
278
+ templateName: 'Test',
279
+ androidContent: null,
280
+ iosContent: { title: 'Title', message: 'Message' },
281
+ accountData: mockAccountData,
282
+ options: { allowSinglePlatform: true },
283
+ })).not.toThrow();
284
+ });
285
+
286
+ it('should throw error when no platforms have content even with allowSinglePlatform', () => {
287
+ expect(() => callWithIntl({
288
+ templateName: 'Test',
289
+ androidContent: null,
290
+ iosContent: null,
291
+ accountData: mockAccountData,
292
+ options: { allowSinglePlatform: true },
293
+ })).toThrow(intl.formatMessage(messages.singlePlatformContentMissing));
294
+ });
295
+
296
+ it('should validate individual platforms in single platform mode - Android incomplete', () => {
297
+ // Test case where iOS has valid content but Android has incomplete content
298
+ expect(() => callWithIntl({
299
+ templateName: 'Test',
300
+ androidContent: { title: 'Valid Title' }, // Missing message property
301
+ iosContent: { title: 'Valid Title', message: 'Valid Message' }, // Complete content
302
+ accountData: mockAccountData,
303
+ options: { allowSinglePlatform: true },
304
+ })).toThrow(intl.formatMessage(messages.contentValidationError, { platform: 'Android' }));
305
+ });
306
+
307
+ it('should validate individual platforms in single platform mode - iOS incomplete', () => {
308
+ // Test case where Android has valid content but iOS has incomplete content
309
+ expect(() => callWithIntl({
310
+ templateName: 'Test',
311
+ androidContent: { title: 'Valid Title', message: 'Valid Message' }, // Complete content
312
+ iosContent: { title: 'Valid Title' }, // Missing message property
313
+ accountData: mockAccountData,
314
+ options: { allowSinglePlatform: true },
315
+ })).toThrow(intl.formatMessage(messages.contentValidationError, { platform: 'iOS' }));
316
+ });
264
317
  });
265
318
 
266
319
  // Account Data Validation Tests
@@ -599,9 +599,8 @@
599
599
  font-size: 10px;
600
600
  .body-image{
601
601
  img{
602
- max-width: 18.75rem;
602
+ max-width: 16.75rem;
603
603
  max-height: 13.75rem;
604
- margin-left: 1.571rem;
605
604
  display: block;
606
605
  }
607
606
  }
@@ -620,7 +619,7 @@
620
619
  box-shadow: 0 0 0.625rem 0 rgba(0,0,0,0.08); // 10px
621
620
 
622
621
  .carousel-image {
623
- width: 100%;
622
+ width: 95%;
624
623
  height: 11.25rem; // 180px
625
624
  object-fit: contain;
626
625
  border-radius: 0.5rem 0.5rem 0 0; // 8px
@@ -696,7 +695,7 @@
696
695
  padding: 0 $CAP_SPACE_08 0 $CAP_SPACE_08;
697
696
  }
698
697
  .body-image{
699
- padding: 0 $CAP_SPACE_08 $CAP_SPACE_08 $CAP_SPACE_08;
698
+ padding: $CAP_SPACE_08 $CAP_SPACE_08 $CAP_SPACE_08 $CAP_SPACE_08;
700
699
  img{
701
700
  border-radius: 3%;
702
701
  }
@@ -816,7 +816,7 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
816
816
  <video
817
817
  key={`android-video-${content.bodyVideo.videoSrc}`}
818
818
  controls
819
- style={{ maxWidth: '100%', maxHeight: '120px', marginLeft: '24px' }}
819
+ style={{ maxWidth: '100%', maxHeight: '150px' }}
820
820
  poster={content?.bodyVideo?.videoPreview || undefined}
821
821
  >
822
822
  <source src={content.bodyVideo.videoSrc} type="video/mp4" />
@@ -880,7 +880,7 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
880
880
  <video
881
881
  key={`ios-video-${content?.bodyVideo?.videoSrc}`}
882
882
  controls
883
- style={{ maxWidth: '100%', maxHeight: '120px', marginLeft: '24px' }}
883
+ style={{ maxWidth: '100%', maxHeight: '150px' }}
884
884
  poster={content?.bodyVideo?.videoPreview || undefined}
885
885
  >
886
886
  <source src={content?.bodyVideo?.videoSrc} type="video/mp4" />
@@ -668,9 +668,7 @@ export function SlideBoxContent(props) {
668
668
  />
669
669
  )}
670
670
  {isEditMPush && (
671
- (isFullMode && !commonUtil.hasNewMobilePushFeatureEnabled()) ||
672
- (!isFullMode && isLoyaltyModule) ||
673
- (!isFullMode && !isLoyaltyModule && !commonUtil.hasNewMobilePushFeatureEnabled()) ? (
671
+ (!isFullMode && isLoyaltyModule) ? (
674
672
  <MobliPushEdit
675
673
  getFormLibraryData={getFormData}
676
674
  setIsLoadingContent={setIsLoadingContent}
@@ -726,9 +724,7 @@ export function SlideBoxContent(props) {
726
724
  )
727
725
  )}
728
726
  {isCreateMPush && (
729
- (isFullMode && !commonUtil.hasNewMobilePushFeatureEnabled()) ||
730
- (!isFullMode && isLoyaltyModule) ||
731
- (!isFullMode && !isLoyaltyModule && !commonUtil.hasNewMobilePushFeatureEnabled()) ? (
727
+ (!isFullMode && isLoyaltyModule) ? (
732
728
  <MobilepushWrapper
733
729
  key="creatives-mobilepush-wrapper"
734
730
  date={new Date().getMilliseconds()}
@@ -5,6 +5,7 @@ import { SlideBoxContent } from '../SlideBoxContent';
5
5
  import mockdata from '../../mockdata';
6
6
  import { templateDetailsImage, templateDetailsVideo, templateDetailsText, templateDetails_ } from '../../Viber/tests/mockData';
7
7
  import { CREATE, RICH_MEDIA } from '../../WeChat/Wrapper/constants';
8
+ import * as commonUtils from '../../../utils/common';
8
9
 
9
10
  const {
10
11
  whatsappEditTemplateData,
@@ -50,6 +51,11 @@ describe('Test SlideBoxContent container', () => {
50
51
  global.Date = originalDate;
51
52
  });
52
53
 
54
+ afterEach(() => {
55
+ // Clean up all mocks after each test
56
+ jest.restoreAllMocks();
57
+ });
58
+
53
59
  const renderFunction = (channel, mode, templateData, additionalProps = {}) => {
54
60
  renderedComponent = shallowWithIntl(
55
61
  <SlideBoxContent
@@ -700,4 +706,315 @@ describe('Test SlideBoxContent container', () => {
700
706
  expect(renderedComponent).toMatchSnapshot();
701
707
  });
702
708
  });
709
+
710
+ // Mobile Push Loyalty Module Tests
711
+ describe('Mobile Push with Loyalty Module', () => {
712
+ it('Should render MobliPushEdit for loyalty module in library mode (edit)', () => {
713
+ renderFunction('MOBILEPUSH', 'editTemplate', {
714
+ _id: 'test-id',
715
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
716
+ }, {
717
+ isFullMode: false,
718
+ isLoyaltyModule: true
719
+ });
720
+ expect(renderedComponent).toMatchSnapshot();
721
+ });
722
+
723
+ it('Should render MobilePushNew for loyalty module in full mode (edit)', () => {
724
+ renderFunction('MOBILEPUSH', 'editTemplate', {
725
+ _id: 'test-id',
726
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
727
+ }, {
728
+ isFullMode: true,
729
+ isLoyaltyModule: true
730
+ });
731
+ expect(renderedComponent).toMatchSnapshot();
732
+ });
733
+
734
+ it('Should render MobilepushWrapper for loyalty module in library mode (create)', () => {
735
+ renderFunction('MOBILEPUSH', 'createTemplate', {
736
+ mode: 'create',
737
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
738
+ }, {
739
+ isFullMode: false,
740
+ isLoyaltyModule: true
741
+ });
742
+ expect(renderedComponent).toMatchSnapshot();
743
+ });
744
+
745
+ it('Should render MobilePushNew for loyalty module in full mode (create)', () => {
746
+ renderFunction('MOBILEPUSH', 'createTemplate', {
747
+ mode: 'create',
748
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
749
+ }, {
750
+ isFullMode: true,
751
+ isLoyaltyModule: true
752
+ });
753
+ expect(renderedComponent).toMatchSnapshot();
754
+ });
755
+
756
+ it('Should render MobilePushNew for non-loyalty module in library mode (edit)', () => {
757
+ renderFunction('MOBILEPUSH', 'editTemplate', {
758
+ _id: 'test-id',
759
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
760
+ }, {
761
+ isFullMode: false,
762
+ isLoyaltyModule: false
763
+ });
764
+ expect(renderedComponent).toMatchSnapshot();
765
+ });
766
+
767
+ it('Should render MobilePushNew for non-loyalty module in library mode (create)', () => {
768
+ renderFunction('MOBILEPUSH', 'createTemplate', {
769
+ mode: 'create',
770
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
771
+ }, {
772
+ isFullMode: false,
773
+ isLoyaltyModule: false
774
+ });
775
+ expect(renderedComponent).toMatchSnapshot();
776
+ });
777
+
778
+ it('Should render MobilePushNew for non-loyalty module in full mode (edit)', () => {
779
+ renderFunction('MOBILEPUSH', 'editTemplate', {
780
+ _id: 'test-id',
781
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
782
+ }, {
783
+ isFullMode: true,
784
+ isLoyaltyModule: false
785
+ });
786
+ expect(renderedComponent).toMatchSnapshot();
787
+ });
788
+
789
+ it('Should render MobilePushNew for non-loyalty module in full mode (create)', () => {
790
+ renderFunction('MOBILEPUSH', 'createTemplate', {
791
+ mode: 'create',
792
+ type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
793
+ }, {
794
+ isFullMode: true,
795
+ isLoyaltyModule: false
796
+ });
797
+ expect(renderedComponent).toMatchSnapshot();
798
+ });
799
+ });
800
+
801
+ // Additional edge case tests to cover uncovered lines
802
+ describe('Edge Cases for Uncovered Lines', () => {
803
+ it('Should handle getLineType with LINE channel and empty templateData', () => {
804
+ // This should cover lines 72-74 in getLineType function
805
+ // LINE channel, not full mode, templateData exists but has no _id, no messageBody, and not isDefault
806
+ const emptyLineTemplateData = {
807
+ type: 'LINE',
808
+ isDefault: false,
809
+ // No _id and no versions.base.content.messages[0]
810
+ };
811
+ renderFunction('LINE', 'editTemplate', emptyLineTemplateData, {
812
+ isFullMode: false
813
+ });
814
+ expect(renderedComponent).toMatchSnapshot();
815
+ });
816
+
817
+ it('Should handle SMS channel with DLT content processing', () => {
818
+ // This should cover line 251 in SMS content processing
819
+ // Need isDltEnabled = true and updatedSmsEditor as array
820
+ // Mock hasTraiDltFeature to return true
821
+ jest.spyOn(commonUtils, 'hasTraiDltFeature').mockReturnValue(true);
822
+
823
+ const smsTemplateData = {
824
+ type: 'SMS',
825
+ versions: {
826
+ base: {
827
+ 'updated-sms-editor': ['Line 1', 'Line 2', 'Line 3'], // Array to trigger join()
828
+ 'sms-editor': 'Fallback SMS content'
829
+ }
830
+ }
831
+ };
832
+ renderFunction('SMS', 'preview', smsTemplateData, {
833
+ smsRegister: 'DLT', // Enable DLT for library mode
834
+ isFullMode: false
835
+ });
836
+
837
+ expect(renderedComponent).toMatchSnapshot();
838
+ });
839
+
840
+ it('Should handle LINE channel with video content', () => {
841
+ // This should cover lines 314-315 for video detection
842
+ // Need a LINE message with video property but no text, previewImageUrl, selectedSticker, baseUrl, template, or contents
843
+ const lineTemplateDataWithVideo = {
844
+ type: 'LINE',
845
+ versions: {
846
+ base: {
847
+ content: {
848
+ messages: [{
849
+ video: {
850
+ originalContentUrl: 'https://example.com/video.mp4',
851
+ externalLink: {
852
+ linkUri: 'https://example.com/link'
853
+ }
854
+ }
855
+ // No text, previewImageUrl, selectedSticker, baseUrl, template, or contents
856
+ }]
857
+ }
858
+ }
859
+ }
860
+ };
861
+ renderFunction('LINE', 'preview', lineTemplateDataWithVideo);
862
+ expect(renderedComponent).toMatchSnapshot();
863
+ });
864
+
865
+ it('Should handle default case in getChannelPreviewContent', () => {
866
+ // This should cover line 395 - default case return
867
+ const unknownChannelData = {
868
+ versions: {
869
+ base: {
870
+ content: {
871
+ customField: 'custom content'
872
+ }
873
+ }
874
+ }
875
+ };
876
+ renderFunction('UNKNOWN_CHANNEL', 'preview', unknownChannelData);
877
+ expect(renderedComponent).toMatchSnapshot();
878
+ });
879
+
880
+ it('Should handle WeChat channel in different modes', () => {
881
+ const wechatTemplateData = {
882
+ type: 'WECHAT',
883
+ versions: {
884
+ base: {
885
+ content: {
886
+ msgcontent: 'RICH_MEDIA_WECHAT'
887
+ }
888
+ }
889
+ }
890
+ };
891
+ renderFunction('WECHAT', 'editTemplate', wechatTemplateData);
892
+ expect(renderedComponent).toMatchSnapshot();
893
+ });
894
+
895
+ it('Should handle InApp channel preview', () => {
896
+ const inappTemplateData = {
897
+ type: 'INAPP',
898
+ versions: {
899
+ base: {
900
+ content: {
901
+ message: 'Test InApp message'
902
+ }
903
+ }
904
+ }
905
+ };
906
+ renderFunction('INAPP', 'preview', inappTemplateData);
907
+ expect(renderedComponent).toMatchSnapshot();
908
+ });
909
+
910
+ it('Should handle Email channel with different modes', () => {
911
+ const emailTemplateData = {
912
+ type: 'EMAIL',
913
+ _id: 'email-123',
914
+ versions: {
915
+ base: {
916
+ content: {
917
+ subject: 'Test Email',
918
+ html_content: '<p>Test content</p>'
919
+ }
920
+ }
921
+ }
922
+ };
923
+ renderFunction('EMAIL', 'editTemplate', emailTemplateData, {
924
+ isFullMode: true
925
+ });
926
+ expect(renderedComponent).toMatchSnapshot();
927
+ });
928
+
929
+ it('Should handle Call Task channel', () => {
930
+ const callTaskData = {
931
+ type: 'CALL_TASK',
932
+ versions: {
933
+ base: {
934
+ content: {
935
+ message: 'Call task message'
936
+ }
937
+ }
938
+ }
939
+ };
940
+ renderFunction('CALL_TASK', 'editTemplate', callTaskData);
941
+ expect(renderedComponent).toMatchSnapshot();
942
+ });
943
+
944
+ it('Should handle FTP channel', () => {
945
+ const ftpData = {
946
+ type: 'FTP',
947
+ versions: {
948
+ base: {
949
+ content: {
950
+ message: 'FTP message'
951
+ }
952
+ }
953
+ }
954
+ };
955
+ renderFunction('FTP', 'editTemplate', ftpData);
956
+ expect(renderedComponent).toMatchSnapshot();
957
+ });
958
+ });
959
+
960
+ // Additional comprehensive test cases
961
+ describe('Additional Channel Coverage', () => {
962
+ it('Should handle Zalo channel', () => {
963
+ const zaloData = {
964
+ type: 'ZALO',
965
+ versions: {
966
+ base: {
967
+ content: {
968
+ message: 'Zalo message'
969
+ }
970
+ }
971
+ }
972
+ };
973
+ renderFunction('ZALO', 'editTemplate', zaloData);
974
+ expect(renderedComponent).toMatchSnapshot();
975
+ });
976
+
977
+ it('Should handle templates mode for various channels', () => {
978
+ renderFunction('EMAIL', 'templates', {});
979
+ expect(renderedComponent).toMatchSnapshot();
980
+ });
981
+
982
+ it('Should handle createTemplate mode with different options', () => {
983
+ renderFunction('SMS', 'createTemplate', { mode: 'create' }, {
984
+ isFullMode: true,
985
+ smsRegister: 'DLT'
986
+ });
987
+ expect(renderedComponent).toMatchSnapshot();
988
+ });
989
+
990
+ it('Should handle complex LINE template with multiple message types', () => {
991
+ const complexLineData = {
992
+ type: 'LINE',
993
+ versions: {
994
+ base: {
995
+ content: {
996
+ messages: [
997
+ {
998
+ text: 'Hello',
999
+ previewImageUrl: 'https://example.com/image.jpg'
1000
+ },
1001
+ {
1002
+ selectedSticker: {
1003
+ stickerUrl: 'https://example.com/sticker.png',
1004
+ stickerId: '123',
1005
+ packageId: '456'
1006
+ }
1007
+ },
1008
+ {
1009
+ baseUrl: 'https://example.com/map'
1010
+ }
1011
+ ]
1012
+ }
1013
+ }
1014
+ }
1015
+ };
1016
+ renderFunction('LINE', 'preview', complexLineData);
1017
+ expect(renderedComponent).toMatchSnapshot();
1018
+ });
1019
+ });
703
1020
  });