@capillarytech/creatives-library 8.0.126 → 8.0.127-alpha.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.
Files changed (78) hide show
  1. package/containers/App/constants.js +1 -0
  2. package/index.html +3 -1
  3. package/package.json +1 -1
  4. package/services/api.js +4 -4
  5. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  6. package/tests/integration/TemplateCreation/api-response.js +5 -0
  7. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  8. package/utils/common.js +7 -0
  9. package/utils/commonUtils.js +2 -6
  10. package/utils/createPayload.js +272 -0
  11. package/utils/tests/createPayload.test.js +761 -0
  12. package/v2Components/CapImageUpload/index.js +59 -46
  13. package/v2Components/CapInAppCTA/index.js +1 -0
  14. package/v2Components/CapMpushCTA/constants.js +25 -0
  15. package/v2Components/CapMpushCTA/index.js +332 -0
  16. package/v2Components/CapMpushCTA/index.scss +95 -0
  17. package/v2Components/CapMpushCTA/messages.js +89 -0
  18. package/v2Components/CapTagList/index.js +177 -120
  19. package/v2Components/CapVideoUpload/constants.js +3 -0
  20. package/v2Components/CapVideoUpload/index.js +167 -110
  21. package/v2Components/CapVideoUpload/messages.js +16 -0
  22. package/v2Components/Carousel/index.js +15 -13
  23. package/v2Components/CustomerSearchSection/index.js +12 -7
  24. package/v2Components/ErrorInfoNote/style.scss +1 -0
  25. package/v2Components/MobilePushPreviewV2/index.js +37 -5
  26. package/v2Components/TemplatePreview/_templatePreview.scss +114 -72
  27. package/v2Components/TemplatePreview/assets/images/Android _ With date and time.svg +29 -0
  28. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  29. package/v2Components/TemplatePreview/assets/images/iOS _ With date and time.svg +26 -0
  30. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  31. package/v2Components/TemplatePreview/index.js +178 -50
  32. package/v2Components/TemplatePreview/messages.js +4 -0
  33. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +169 -0
  34. package/v2Components/TestAndPreviewSlidebox/LeftPanelContent.js +95 -0
  35. package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +69 -0
  36. package/v2Components/TestAndPreviewSlidebox/SendTestMessage.js +68 -0
  37. package/v2Components/TestAndPreviewSlidebox/index.js +67 -246
  38. package/v2Components/TestAndPreviewSlidebox/tests/CustomValuesEditor.test.js +425 -0
  39. package/v2Components/TestAndPreviewSlidebox/tests/LeftPanelContent.test.js +400 -0
  40. package/v2Components/TestAndPreviewSlidebox/tests/SendTestMessage.test.js +448 -0
  41. package/v2Containers/CreativesContainer/SlideBoxContent.js +9 -9
  42. package/v2Containers/CreativesContainer/index.js +191 -136
  43. package/v2Containers/Email/index.js +15 -2
  44. package/v2Containers/InApp/constants.js +1 -0
  45. package/v2Containers/InApp/index.js +13 -13
  46. package/v2Containers/MobilePush/Create/index.js +1 -0
  47. package/v2Containers/MobilePush/commonMethods.js +7 -14
  48. package/v2Containers/MobilePushNew/actions.js +116 -0
  49. package/v2Containers/MobilePushNew/components/CtaButtons.js +170 -0
  50. package/v2Containers/MobilePushNew/components/MediaUploaders.js +754 -0
  51. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +279 -0
  52. package/v2Containers/MobilePushNew/components/index.js +5 -0
  53. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +779 -0
  54. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +2114 -0
  55. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +343 -0
  56. package/v2Containers/MobilePushNew/constants.js +115 -0
  57. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1299 -0
  58. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1223 -0
  59. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +246 -0
  60. package/v2Containers/MobilePushNew/hooks/useUpload.js +726 -0
  61. package/v2Containers/MobilePushNew/index.js +2280 -0
  62. package/v2Containers/MobilePushNew/index.scss +308 -0
  63. package/v2Containers/MobilePushNew/messages.js +226 -0
  64. package/v2Containers/MobilePushNew/reducer.js +160 -0
  65. package/v2Containers/MobilePushNew/sagas.js +198 -0
  66. package/v2Containers/MobilePushNew/selectors.js +55 -0
  67. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  68. package/v2Containers/MobilePushNew/tests/sagas.test.js +863 -0
  69. package/v2Containers/MobilePushNew/tests/selectors.test.js +425 -0
  70. package/v2Containers/MobilePushNew/tests/utils.test.js +322 -0
  71. package/v2Containers/MobilePushNew/utils.js +33 -0
  72. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +5 -5
  73. package/v2Containers/TagList/index.js +56 -10
  74. package/v2Containers/Templates/_templates.scss +101 -1
  75. package/v2Containers/Templates/index.js +147 -35
  76. package/v2Containers/Templates/messages.js +8 -0
  77. package/v2Containers/Templates/sagas.js +2 -0
  78. package/v2Containers/Whatsapp/constants.js +1 -0
@@ -0,0 +1,1223 @@
1
+ import { renderHook, act } from '@testing-library/react';
2
+ import { getCdnUrl } from '../../../../utils/cdnTransformation';
3
+ import useUpload from '../useUpload';
4
+ import {
5
+ MOBILE_PUSH_CHANNEL,
6
+ MPUSH_IMG_SIZE,
7
+ MPUSH_VIDEO_SIZE,
8
+ MPUSH_GIF_SIZE,
9
+ ANDROID,
10
+ IMAGE,
11
+ VIDEO,
12
+ GIF,
13
+ CAROUSEL,
14
+ } from '../../constants';
15
+
16
+ // Mock the CDN transformation utility
17
+ jest.mock('../../../../utils/cdnTransformation', () => ({
18
+ getCdnUrl: jest.fn(),
19
+ }));
20
+
21
+ describe('useUpload Hook', () => {
22
+ // Mock data and functions
23
+ const mockMobilePushActions = {
24
+ uploadAsset: jest.fn(),
25
+ clearAsset: jest.fn(),
26
+ };
27
+
28
+ const mockSetAndroidContent = jest.fn();
29
+ const mockSetIosContent = jest.fn();
30
+
31
+ const defaultProps = {
32
+ mobilePushActions: mockMobilePushActions,
33
+ editData: {},
34
+ uploadedAssetData0: {},
35
+ uploadedAssetData1: {},
36
+ uploadAssetSuccess: false,
37
+ sameContent: false,
38
+ setAndroidContent: mockSetAndroidContent,
39
+ setIosContent: mockSetIosContent,
40
+ activeTab: ANDROID,
41
+ androidContent: { title: 'Test Android', message: 'Test message' },
42
+ iosContent: { title: 'Test iOS', message: 'Test message' },
43
+ };
44
+
45
+ beforeEach(() => {
46
+ jest.clearAllMocks();
47
+ getCdnUrl.mockImplementation(({ url }) => `cdn_${url}`);
48
+ });
49
+
50
+ describe('Hook Initialization', () => {
51
+ it('should initialize with default states', () => {
52
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
53
+
54
+ expect(result.current.androidAssetList).toEqual([]);
55
+ expect(result.current.iosAssetList).toEqual([]);
56
+ expect(result.current.mpushVideoSrcAndPreview).toEqual({
57
+ mpushVideoSrc: "",
58
+ mpushVideoPreviewImg: "",
59
+ duration: 0,
60
+ });
61
+ expect(result.current.imageSrc).toEqual({
62
+ androidImageSrc: "",
63
+ iosImageSrc: "",
64
+ });
65
+ expect(result.current.uploadErrors).toEqual({});
66
+ });
67
+
68
+ it('should initialize from edit data when provided', () => {
69
+ const editData = {
70
+ templateDetails: {
71
+ versions: {
72
+ base: {
73
+ content: {
74
+ ANDROID: {
75
+ expandableDetails: {
76
+ image: 'test-android-image.jpg',
77
+ video: 'test-video.mp4',
78
+ videoPreview: 'test-preview.jpg',
79
+ },
80
+ },
81
+ IOS: {
82
+ expandableDetails: {
83
+ image: 'test-ios-image.jpg',
84
+ },
85
+ },
86
+ },
87
+ },
88
+ },
89
+ },
90
+ };
91
+
92
+ const { result } = renderHook(() => useUpload(
93
+ mockMobilePushActions,
94
+ editData,
95
+ {},
96
+ {},
97
+ false,
98
+ false,
99
+ mockSetAndroidContent,
100
+ mockSetIosContent,
101
+ ANDROID,
102
+ defaultProps.androidContent,
103
+ defaultProps.iosContent
104
+ ));
105
+
106
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-android-image.jpg');
107
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-ios-image.jpg');
108
+ expect(result.current.mpushVideoSrcAndPreview.mpushVideoSrc).toBe('test-video.mp4');
109
+ expect(result.current.mpushVideoSrcAndPreview.mpushVideoPreviewImg).toBe('test-preview.jpg');
110
+ });
111
+ });
112
+
113
+ describe('File Validation Functions', () => {
114
+ describe('validateImageFile', () => {
115
+ it('should pass validation for valid image file', () => {
116
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
117
+
118
+ const validFile = new File(['test'], 'test.jpg', {
119
+ type: 'image/jpeg',
120
+ size: MPUSH_IMG_SIZE - 1000, // Under the limit
121
+ });
122
+
123
+ const errors = result.current.validateImageFile(validFile);
124
+ expect(errors).toEqual([]);
125
+ });
126
+
127
+ it('should fail validation for oversized image file', () => {
128
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
129
+
130
+ // Create a file with data that exceeds the limit
131
+ const oversizedData = new Array(MPUSH_IMG_SIZE + 1000).fill('a').join('');
132
+ const oversizedFile = new File([oversizedData], 'test.jpg', {
133
+ type: 'image/jpeg',
134
+ });
135
+
136
+ const errors = result.current.validateImageFile(oversizedFile);
137
+ expect(errors).toContain('File size should be less than 2MB');
138
+ });
139
+
140
+ it('should fail validation for invalid file type', () => {
141
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
142
+
143
+ const invalidFile = new File(['test'], 'test.pdf', {
144
+ type: 'application/pdf',
145
+ size: 1000,
146
+ });
147
+
148
+ const errors = result.current.validateImageFile(invalidFile);
149
+ expect(errors).toContain('Only JPEG and PNG files are allowed');
150
+ });
151
+
152
+ it('should fail validation for both size and type errors', () => {
153
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
154
+
155
+ // Create a file with data that exceeds the limit
156
+ const oversizedData = new Array(MPUSH_IMG_SIZE + 1000).fill('a').join('');
157
+ const invalidFile = new File([oversizedData], 'test.pdf', {
158
+ type: 'application/pdf',
159
+ });
160
+
161
+ const errors = result.current.validateImageFile(invalidFile);
162
+ expect(errors).toHaveLength(2);
163
+ expect(errors).toContain('File size should be less than 2MB');
164
+ expect(errors).toContain('Only JPEG and PNG files are allowed');
165
+ });
166
+ });
167
+
168
+ describe('validateVideoFile', () => {
169
+ it('should pass validation for valid video file', () => {
170
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
171
+
172
+ const validFile = new File(['test'], 'test.mp4', {
173
+ type: 'video/mp4',
174
+ size: MPUSH_VIDEO_SIZE - 1000,
175
+ });
176
+
177
+ const errors = result.current.validateVideoFile(validFile);
178
+ expect(errors).toEqual([]);
179
+ });
180
+
181
+ it('should fail validation for oversized video file', () => {
182
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
183
+
184
+ // Mock a file with oversized property
185
+ const oversizedFile = new File(['test'], 'test.mp4', {
186
+ type: 'video/mp4',
187
+ });
188
+ // Override the size property to simulate an oversized file
189
+ Object.defineProperty(oversizedFile, 'size', {
190
+ value: MPUSH_VIDEO_SIZE + 1000,
191
+ writable: false,
192
+ });
193
+
194
+ const errors = result.current.validateVideoFile(oversizedFile);
195
+ expect(errors).toContain('File size should be less than 209.7152MB');
196
+ });
197
+
198
+ it('should fail validation for invalid video file type', () => {
199
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
200
+
201
+ const invalidFile = new File(['test'], 'test.avi', {
202
+ type: 'video/avi',
203
+ size: 1000,
204
+ });
205
+
206
+ const errors = result.current.validateVideoFile(invalidFile);
207
+ expect(errors).toContain('Only 3GP, MP4, MOV, M4V files are allowed');
208
+ });
209
+ });
210
+
211
+ describe('validateGifFile', () => {
212
+ it('should pass validation for valid GIF file', () => {
213
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
214
+
215
+ const validFile = new File(['test'], 'test.gif', {
216
+ type: 'image/gif',
217
+ size: MPUSH_GIF_SIZE - 1000,
218
+ });
219
+
220
+ const errors = result.current.validateGifFile(validFile);
221
+ expect(errors).toEqual([]);
222
+ });
223
+
224
+ it('should fail validation for oversized GIF file', () => {
225
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
226
+
227
+ // Mock a file with oversized property
228
+ const oversizedFile = new File(['test'], 'test.gif', {
229
+ type: 'image/gif',
230
+ });
231
+ // Override the size property to simulate an oversized file
232
+ Object.defineProperty(oversizedFile, 'size', {
233
+ value: MPUSH_GIF_SIZE + 1000,
234
+ writable: false,
235
+ });
236
+
237
+ const errors = result.current.validateGifFile(oversizedFile);
238
+ expect(errors).toContain('File size should be less than 209.7152MB');
239
+ });
240
+
241
+ it('should fail validation for non-GIF file', () => {
242
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
243
+
244
+ const invalidFile = new File(['test'], 'test.jpg', {
245
+ type: 'image/jpeg',
246
+ size: 1000,
247
+ });
248
+
249
+ const errors = result.current.validateGifFile(invalidFile);
250
+ expect(errors).toContain('Only GIF files are allowed');
251
+ });
252
+ });
253
+ });
254
+
255
+ describe('Upload Asset Function', () => {
256
+ it('should upload valid image file', () => {
257
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
258
+
259
+ const validFile = new File(['test'], 'test.jpg', {
260
+ type: 'image/jpeg',
261
+ size: 1000,
262
+ });
263
+
264
+ act(() => {
265
+ result.current.uploadMpushAsset(validFile, IMAGE.toLowerCase(), {});
266
+ });
267
+
268
+ expect(mockMobilePushActions.uploadAsset).toHaveBeenCalledWith(
269
+ validFile,
270
+ IMAGE.toLowerCase(),
271
+ {},
272
+ 0, // Android tab
273
+ {
274
+ source: MOBILE_PUSH_CHANNEL.toLowerCase(),
275
+ channel: MOBILE_PUSH_CHANNEL,
276
+ }
277
+ );
278
+ });
279
+
280
+ it('should upload valid video file', () => {
281
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
282
+
283
+ const validFile = new File(['test'], 'test.mp4', {
284
+ type: 'video/mp4',
285
+ size: 1000,
286
+ });
287
+
288
+ act(() => {
289
+ result.current.uploadMpushAsset(validFile, VIDEO.toLowerCase(), {});
290
+ });
291
+
292
+ expect(mockMobilePushActions.uploadAsset).toHaveBeenCalledWith(
293
+ validFile,
294
+ VIDEO.toLowerCase(),
295
+ {},
296
+ 0,
297
+ {
298
+ source: MOBILE_PUSH_CHANNEL.toLowerCase(),
299
+ channel: MOBILE_PUSH_CHANNEL,
300
+ }
301
+ );
302
+ });
303
+
304
+ it('should upload GIF file as video type', () => {
305
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
306
+
307
+ const validFile = new File(['test'], 'test.gif', {
308
+ type: 'image/gif',
309
+ size: 1000,
310
+ });
311
+
312
+ act(() => {
313
+ result.current.uploadMpushAsset(validFile, GIF.toLowerCase(), {});
314
+ });
315
+
316
+ expect(mockMobilePushActions.uploadAsset).toHaveBeenCalledWith(
317
+ validFile,
318
+ VIDEO.toLowerCase(), // GIF uploads use video type
319
+ {},
320
+ 0,
321
+ {
322
+ source: MOBILE_PUSH_CHANNEL.toLowerCase(),
323
+ channel: MOBILE_PUSH_CHANNEL,
324
+ }
325
+ );
326
+ });
327
+
328
+ it('should not upload invalid file and set upload errors', () => {
329
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
330
+
331
+ // Create a file with data that exceeds the limit
332
+ const oversizedData = new Array(MPUSH_IMG_SIZE + 1000).fill('a').join('');
333
+ const invalidFile = new File([oversizedData], 'test.pdf', {
334
+ type: 'application/pdf',
335
+ });
336
+
337
+ act(() => {
338
+ result.current.uploadMpushAsset(invalidFile, IMAGE.toLowerCase(), {});
339
+ });
340
+
341
+ expect(mockMobilePushActions.uploadAsset).not.toHaveBeenCalled();
342
+ expect(result.current.uploadErrors[0]).toHaveLength(2);
343
+ expect(result.current.uploadErrors[0]).toContain('File size should be less than 2MB');
344
+ expect(result.current.uploadErrors[0]).toContain('Only JPEG and PNG files are allowed');
345
+ });
346
+
347
+ it('should handle iOS tab correctly', () => {
348
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
349
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
350
+
351
+ const validFile = new File(['test'], 'test.jpg', {
352
+ type: 'image/jpeg',
353
+ size: 1000,
354
+ });
355
+
356
+ act(() => {
357
+ result.current.uploadMpushAsset(validFile, IMAGE.toLowerCase(), {});
358
+ });
359
+
360
+ expect(mockMobilePushActions.uploadAsset).toHaveBeenCalledWith(
361
+ validFile,
362
+ IMAGE.toLowerCase(),
363
+ {},
364
+ 1, // iOS tab
365
+ expect.any(Object)
366
+ );
367
+ });
368
+ });
369
+
370
+ describe('Image Source Management', () => {
371
+ describe('setUpdateMpushImageSrc', () => {
372
+ it('should update Android image source when sync is disabled', () => {
373
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
374
+
375
+ act(() => {
376
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
377
+ });
378
+
379
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
380
+ expect(result.current.imageSrc.iosImageSrc).toBe('');
381
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(0);
382
+ });
383
+
384
+ it('should update both platforms when sync is enabled', () => {
385
+ const syncProps = { ...defaultProps, sameContent: true };
386
+ const { result } = renderHook(() => useUpload(...Object.values(syncProps)));
387
+
388
+ act(() => {
389
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
390
+ });
391
+
392
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
393
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-image.jpg');
394
+ });
395
+
396
+ it('should update iOS image source when iOS tab is active', () => {
397
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
398
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
399
+
400
+ act(() => {
401
+ result.current.setUpdateMpushImageSrc('test-ios-image.jpg', 1, IMAGE);
402
+ });
403
+
404
+ expect(result.current.imageSrc.androidImageSrc).toBe('');
405
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-ios-image.jpg');
406
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(1);
407
+ });
408
+
409
+ it('should not handle carousel media type', () => {
410
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
411
+
412
+ act(() => {
413
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, CAROUSEL);
414
+ });
415
+
416
+ expect(result.current.imageSrc.androidImageSrc).toBe('');
417
+ expect(result.current.imageSrc.iosImageSrc).toBe('');
418
+ expect(mockMobilePushActions.clearAsset).not.toHaveBeenCalled();
419
+ });
420
+
421
+ it('should default to IMAGE media type when mediaType parameter is not provided', () => {
422
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
423
+
424
+ act(() => {
425
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0); // No mediaType parameter
426
+ });
427
+
428
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
429
+ expect(result.current.imageSrc.iosImageSrc).toBe('');
430
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(0);
431
+ });
432
+
433
+ it('should default to IMAGE media type for iOS platform when mediaType parameter is not provided', () => {
434
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
435
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
436
+
437
+ act(() => {
438
+ result.current.setUpdateMpushImageSrc('test-ios-image.jpg', 1); // No mediaType parameter
439
+ });
440
+
441
+ expect(result.current.imageSrc.androidImageSrc).toBe('');
442
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-ios-image.jpg');
443
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(1);
444
+ });
445
+
446
+ it('should default to IMAGE media type when sync is enabled and mediaType parameter is not provided', () => {
447
+ const syncProps = { ...defaultProps, sameContent: true };
448
+ const { result } = renderHook(() => useUpload(...Object.values(syncProps)));
449
+
450
+ act(() => {
451
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0); // No mediaType parameter
452
+ });
453
+
454
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
455
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-image.jpg');
456
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(0);
457
+ });
458
+ });
459
+
460
+ describe('updateOnMpushImageReUpload', () => {
461
+ it('should clear Android platform image when sync is disabled and Android tab is active', () => {
462
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
463
+
464
+ // Set initial images for both platforms
465
+ act(() => {
466
+ result.current.setImageSrc({
467
+ androidImageSrc: 'test-android-image.jpg',
468
+ iosImageSrc: 'test-ios-image.jpg',
469
+ });
470
+ });
471
+
472
+ // Clear image (should only clear Android since activeTab is ANDROID)
473
+ act(() => {
474
+ result.current.updateOnMpushImageReUpload();
475
+ });
476
+
477
+ expect(result.current.imageSrc.androidImageSrc).toBe('');
478
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-ios-image.jpg'); // iOS should remain
479
+ });
480
+
481
+ it('should clear iOS platform image when sync is disabled and iOS tab is active', () => {
482
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
483
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
484
+
485
+ // Set initial images for both platforms
486
+ act(() => {
487
+ result.current.setImageSrc({
488
+ androidImageSrc: 'test-android-image.jpg',
489
+ iosImageSrc: 'test-ios-image.jpg',
490
+ });
491
+ });
492
+
493
+ // Clear image (should only clear iOS since activeTab is IOS)
494
+ act(() => {
495
+ result.current.updateOnMpushImageReUpload();
496
+ });
497
+
498
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-android-image.jpg'); // Android should remain
499
+ expect(result.current.imageSrc.iosImageSrc).toBe(''); // iOS should be cleared
500
+ });
501
+
502
+ it('should clear both platforms when sync is enabled', () => {
503
+ const syncProps = { ...defaultProps, sameContent: true };
504
+ const { result } = renderHook(() => useUpload(...Object.values(syncProps)));
505
+
506
+ // Set initial images
507
+ act(() => {
508
+ result.current.setUpdateMpushImageSrc('test-image.jpg', IMAGE);
509
+ });
510
+
511
+ // Clear images
512
+ act(() => {
513
+ result.current.updateOnMpushImageReUpload();
514
+ });
515
+
516
+ expect(result.current.imageSrc.androidImageSrc).toBe('');
517
+ expect(result.current.imageSrc.iosImageSrc).toBe('');
518
+ });
519
+ });
520
+ });
521
+
522
+ describe('Video Source Management', () => {
523
+ describe('setUpdateMpushVideoSrc', () => {
524
+ it('should update video source and asset list', () => {
525
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
526
+
527
+ const videoData = {
528
+ videoSrc: 'test-video.mp4',
529
+ previewUrl: 'test-preview.jpg',
530
+ videoDuration: 120,
531
+ videoName: 'test-video.mp4',
532
+ fileHandle: 'file-handle-123',
533
+ };
534
+
535
+ act(() => {
536
+ result.current.setUpdateMpushVideoSrc(0, videoData);
537
+ });
538
+
539
+ expect(result.current.mpushVideoSrcAndPreview).toEqual({
540
+ mpushVideoSrc: 'test-video.mp4',
541
+ mpushVideoPreviewImg: 'test-preview.jpg',
542
+ duration: 120,
543
+ });
544
+
545
+ expect(result.current.assetList).toEqual(videoData);
546
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(0);
547
+ });
548
+
549
+ it('should handle partial video data', () => {
550
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
551
+
552
+ const partialVideoData = {
553
+ videoSrc: 'test-video.mp4',
554
+ };
555
+
556
+ act(() => {
557
+ result.current.setUpdateMpushVideoSrc(0, partialVideoData);
558
+ });
559
+
560
+ expect(result.current.mpushVideoSrcAndPreview).toEqual({
561
+ mpushVideoSrc: 'test-video.mp4',
562
+ mpushVideoPreviewImg: '',
563
+ duration: 0,
564
+ });
565
+ });
566
+ });
567
+
568
+ describe('updateOnMpushVideoReUpload', () => {
569
+ it('should clear all video-related state', () => {
570
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
571
+
572
+ // Set initial video data
573
+ act(() => {
574
+ result.current.setUpdateMpushVideoSrc(0, {
575
+ videoSrc: 'test-video.mp4',
576
+ previewUrl: 'test-preview.jpg',
577
+ videoDuration: 120,
578
+ });
579
+ });
580
+
581
+ // Clear video data
582
+ act(() => {
583
+ result.current.updateOnMpushVideoReUpload();
584
+ });
585
+
586
+ expect(result.current.mpushVideoSrcAndPreview).toEqual({
587
+ mpushVideoSrc: '',
588
+ mpushVideoPreviewImg: '',
589
+ duration: 0,
590
+ });
591
+
592
+ expect(result.current.assetList).toEqual([]);
593
+ expect(mockSetAndroidContent).toHaveBeenCalledWith(expect.any(Function));
594
+ expect(mockSetIosContent).toHaveBeenCalledWith(expect.any(Function));
595
+ expect(mockMobilePushActions.clearAsset).toHaveBeenCalledWith(0);
596
+ });
597
+ });
598
+ });
599
+
600
+ describe('Media Payload Generation', () => {
601
+ it('should generate correct payload for IMAGE media type', () => {
602
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
603
+
604
+ // Set image source
605
+ act(() => {
606
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
607
+ });
608
+
609
+ const payload = result.current.getMediaPayload(IMAGE, ANDROID);
610
+
611
+ expect(payload).toEqual({
612
+ imageUrl: 'cdn_test-image.jpg',
613
+ });
614
+ });
615
+
616
+ it('should generate correct payload for VIDEO media type', () => {
617
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
618
+
619
+ // Set video source with preview
620
+ act(() => {
621
+ result.current.setUpdateMpushVideoSrc(0, {
622
+ videoSrc: 'test-video.mp4',
623
+ previewUrl: 'test-preview.jpg',
624
+ videoDuration: 30,
625
+ });
626
+ });
627
+
628
+ const payload = result.current.getMediaPayload(VIDEO, ANDROID);
629
+
630
+ expect(payload).toEqual({
631
+ videoUrl: 'cdn_test-video.mp4',
632
+ videoPreviewImg: 'cdn_test-preview.jpg',
633
+ duration: 30,
634
+ });
635
+ });
636
+
637
+ it('should generate correct payload for GIF media type with preview image', () => {
638
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
639
+
640
+ // Set GIF source with preview
641
+ act(() => {
642
+ result.current.setUpdateMpushVideoSrc(0, {
643
+ videoSrc: 'test-gif.gif',
644
+ previewUrl: 'test-preview.jpg',
645
+ videoDuration: 5,
646
+ });
647
+ });
648
+
649
+ const payload = result.current.getMediaPayload(GIF, ANDROID);
650
+
651
+ expect(payload).toEqual({
652
+ gifUrl: 'cdn_test-gif.gif',
653
+ gifPreviewImg: 'cdn_test-preview.jpg',
654
+ });
655
+ });
656
+
657
+ it('should generate correct payload for GIF media type without preview image (covers line 328)', () => {
658
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
659
+
660
+ // Set GIF source WITHOUT preview to cover the falsy condition on line 328
661
+ act(() => {
662
+ result.current.setUpdateMpushVideoSrc(0, {
663
+ videoSrc: 'test-gif.gif',
664
+ previewUrl: '', // Empty preview URL to trigger the falsy condition
665
+ videoDuration: 5,
666
+ });
667
+ });
668
+
669
+ const payload = result.current.getMediaPayload(GIF, ANDROID);
670
+
671
+ expect(payload).toEqual({
672
+ gifUrl: 'cdn_test-gif.gif',
673
+ gifPreviewImg: '', // This should be empty string due to the falsy condition
674
+ });
675
+ });
676
+
677
+ it('should generate correct payload for GIF media type with null preview image (covers line 328)', () => {
678
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
679
+
680
+ // Set GIF source with null preview to cover the falsy condition on line 328
681
+ act(() => {
682
+ result.current.setUpdateMpushVideoSrc(0, {
683
+ videoSrc: 'test-gif.gif',
684
+ previewUrl: null, // Null preview URL to trigger the falsy condition
685
+ videoDuration: 5,
686
+ });
687
+ });
688
+
689
+ const payload = result.current.getMediaPayload(GIF, ANDROID);
690
+
691
+ expect(payload).toEqual({
692
+ gifUrl: 'cdn_test-gif.gif',
693
+ gifPreviewImg: '', // This should be empty string due to the falsy condition
694
+ });
695
+ });
696
+
697
+ it('should generate correct payload for GIF media type with undefined preview image (covers line 328)', () => {
698
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
699
+
700
+ // Set GIF source with undefined preview to cover the falsy condition on line 328
701
+ act(() => {
702
+ result.current.setUpdateMpushVideoSrc(0, {
703
+ videoSrc: 'test-gif.gif',
704
+ previewUrl: undefined, // Undefined preview URL to trigger the falsy condition
705
+ videoDuration: 5,
706
+ });
707
+ });
708
+
709
+ const payload = result.current.getMediaPayload(GIF, ANDROID);
710
+
711
+ expect(payload).toEqual({
712
+ gifUrl: 'cdn_test-gif.gif',
713
+ gifPreviewImg: '', // This should be empty string due to the falsy condition
714
+ });
715
+ });
716
+
717
+ it('should return empty object for unknown media type', () => {
718
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
719
+
720
+ const payload = result.current.getMediaPayload('UNKNOWN_TYPE', ANDROID);
721
+
722
+ expect(payload).toEqual({});
723
+ });
724
+
725
+ it('should return empty object when no media is set', () => {
726
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
727
+
728
+ // Test with IMAGE but no image source set
729
+ const imagePayload = result.current.getMediaPayload(IMAGE, ANDROID);
730
+ expect(imagePayload).toEqual({});
731
+
732
+ // Test with VIDEO but no video source set
733
+ const videoPayload = result.current.getMediaPayload(VIDEO, ANDROID);
734
+ expect(videoPayload).toEqual({});
735
+
736
+ // Test with GIF but no GIF source set
737
+ const gifPayload = result.current.getMediaPayload(GIF, ANDROID);
738
+ expect(gifPayload).toEqual({});
739
+ });
740
+ });
741
+
742
+ describe('Preview Data Generation', () => {
743
+ describe('getPreviewData', () => {
744
+ it('should generate image preview data', () => {
745
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
746
+
747
+ act(() => {
748
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
749
+ });
750
+
751
+ const previewData = result.current.getPreviewData(IMAGE, ANDROID);
752
+
753
+ expect(previewData).toEqual({
754
+ imageSrc: 'test-image.jpg',
755
+ });
756
+ });
757
+
758
+ it('should generate video preview data', () => {
759
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
760
+
761
+ act(() => {
762
+ result.current.setUpdateMpushVideoSrc(0, {
763
+ videoSrc: 'test-video.mp4',
764
+ previewUrl: 'test-preview.jpg',
765
+ videoDuration: 120,
766
+ });
767
+ });
768
+
769
+ const previewData = result.current.getPreviewData(VIDEO, ANDROID);
770
+
771
+ expect(previewData).toEqual({
772
+ videoSrc: 'test-video.mp4',
773
+ videoPreviewImg: 'test-preview.jpg',
774
+ duration: 120,
775
+ });
776
+ });
777
+
778
+ it('should generate GIF preview data', () => {
779
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
780
+
781
+ act(() => {
782
+ result.current.setUpdateMpushVideoSrc(0, {
783
+ videoSrc: 'test-gif.gif',
784
+ previewUrl: 'test-preview.jpg',
785
+ });
786
+ });
787
+
788
+ const previewData = result.current.getPreviewData(GIF, ANDROID);
789
+
790
+ expect(previewData).toEqual({
791
+ gifSrc: 'test-gif.gif',
792
+ gifPreviewImg: 'test-preview.jpg',
793
+ });
794
+ });
795
+
796
+ it('should generate image preview data for iOS platform', () => {
797
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
798
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
799
+
800
+ act(() => {
801
+ result.current.setUpdateMpushImageSrc('test-ios-image.jpg', 1, IMAGE);
802
+ });
803
+
804
+ const previewData = result.current.getPreviewData(IMAGE, 'IOS');
805
+
806
+ expect(previewData).toEqual({
807
+ imageSrc: 'test-ios-image.jpg',
808
+ });
809
+ });
810
+
811
+ it('should generate video preview data for iOS platform', () => {
812
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
813
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
814
+
815
+ act(() => {
816
+ result.current.setUpdateMpushVideoSrc(1, {
817
+ videoSrc: 'test-ios-video.mp4',
818
+ previewUrl: 'test-ios-preview.jpg',
819
+ videoDuration: 90,
820
+ });
821
+ });
822
+
823
+ const previewData = result.current.getPreviewData(VIDEO, 'IOS');
824
+
825
+ expect(previewData).toEqual({
826
+ videoSrc: 'test-ios-video.mp4',
827
+ videoPreviewImg: 'test-ios-preview.jpg',
828
+ duration: 90,
829
+ });
830
+ });
831
+
832
+ it('should generate GIF preview data for iOS platform', () => {
833
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
834
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
835
+
836
+ act(() => {
837
+ result.current.setUpdateMpushVideoSrc(1, {
838
+ videoSrc: 'test-ios-gif.gif',
839
+ previewUrl: 'test-ios-preview.jpg',
840
+ });
841
+ });
842
+
843
+ const previewData = result.current.getPreviewData(GIF, 'IOS');
844
+
845
+ expect(previewData).toEqual({
846
+ gifSrc: 'test-ios-gif.gif',
847
+ gifPreviewImg: 'test-ios-preview.jpg',
848
+ });
849
+ });
850
+ });
851
+ });
852
+
853
+ describe('Media Upload Validation', () => {
854
+ describe('isMediaUploaded', () => {
855
+ it('should return true for uploaded image', () => {
856
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
857
+
858
+ act(() => {
859
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
860
+ });
861
+
862
+ expect(result.current.isMediaUploaded(IMAGE, ANDROID)).toBe(true);
863
+ });
864
+
865
+ it('should return false for non-uploaded image', () => {
866
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
867
+
868
+ expect(result.current.isMediaUploaded(IMAGE, ANDROID)).toBe(false);
869
+ });
870
+
871
+ it('should return true for uploaded video', () => {
872
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
873
+
874
+ act(() => {
875
+ result.current.setUpdateMpushVideoSrc(0, {
876
+ videoSrc: 'test-video.mp4',
877
+ });
878
+ });
879
+
880
+ expect(result.current.isMediaUploaded(VIDEO, ANDROID)).toBe(true);
881
+ expect(result.current.isMediaUploaded(GIF, ANDROID)).toBe(true);
882
+ });
883
+
884
+ it('should return true for text media types', () => {
885
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
886
+
887
+ expect(result.current.isMediaUploaded('TEXT', ANDROID)).toBe(true);
888
+ expect(result.current.isMediaUploaded('NONE', ANDROID)).toBe(true);
889
+ });
890
+
891
+ it('should return true for uploaded image on iOS platform', () => {
892
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
893
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
894
+
895
+ act(() => {
896
+ result.current.setUpdateMpushImageSrc('test-ios-image.jpg', IMAGE);
897
+ });
898
+
899
+ expect(result.current.isMediaUploaded(IMAGE, 'IOS')).toBe(true);
900
+ });
901
+
902
+ it('should return false for non-uploaded image on iOS platform', () => {
903
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
904
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
905
+
906
+ expect(result.current.isMediaUploaded(IMAGE, 'IOS')).toBe(false);
907
+ });
908
+
909
+ it('should return true for uploaded video on iOS platform', () => {
910
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
911
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
912
+
913
+ act(() => {
914
+ result.current.setUpdateMpushVideoSrc(1, {
915
+ videoSrc: 'test-ios-video.mp4',
916
+ });
917
+ });
918
+
919
+ expect(result.current.isMediaUploaded(VIDEO, 'IOS')).toBe(true);
920
+ expect(result.current.isMediaUploaded(GIF, 'IOS')).toBe(true);
921
+ });
922
+
923
+ it('should return true for text media types on iOS platform', () => {
924
+ const iosProps = { ...defaultProps, activeTab: 'IOS' };
925
+ const { result } = renderHook(() => useUpload(...Object.values(iosProps)));
926
+
927
+ expect(result.current.isMediaUploaded('TEXT', 'IOS')).toBe(true);
928
+ expect(result.current.isMediaUploaded('NONE', 'IOS')).toBe(true);
929
+ });
930
+ });
931
+ });
932
+
933
+ describe('Utility Functions', () => {
934
+ describe('clearImageDataByMediaType', () => {
935
+ it('should clear image data for IMAGE media type', () => {
936
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
937
+
938
+ // Set initial image on both platforms
939
+ act(() => {
940
+ result.current.setImageSrc({
941
+ androidImageSrc: 'test-android.jpg',
942
+ iosImageSrc: 'test-ios.jpg',
943
+ });
944
+ });
945
+
946
+ // Clear image data (should only clear Android since activeTab is ANDROID and sameContent is false)
947
+ act(() => {
948
+ result.current.clearImageDataByMediaType(IMAGE);
949
+ });
950
+
951
+ expect(result.current.imageSrc).toEqual({
952
+ androidImageSrc: '',
953
+ iosImageSrc: 'test-ios.jpg', // iOS should remain unchanged
954
+ });
955
+ });
956
+
957
+ it('should not clear data for non-IMAGE media types', () => {
958
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
959
+
960
+ // Set initial image
961
+ act(() => {
962
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
963
+ });
964
+
965
+ // Try to clear with different media type
966
+ act(() => {
967
+ result.current.clearImageDataByMediaType(VIDEO);
968
+ });
969
+
970
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
971
+ });
972
+ });
973
+
974
+ describe('resetUploadStates', () => {
975
+ it('should reset all upload states', () => {
976
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
977
+
978
+ // Set some data
979
+ act(() => {
980
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
981
+ result.current.setUpdateMpushVideoSrc(0, {
982
+ videoSrc: 'test-video.mp4',
983
+ previewUrl: 'test-preview.jpg',
984
+ });
985
+ });
986
+
987
+ // Reset all states
988
+ act(() => {
989
+ result.current.resetUploadStates();
990
+ });
991
+
992
+ expect(result.current.imageSrc).toEqual({
993
+ androidImageSrc: '',
994
+ iosImageSrc: '',
995
+ });
996
+ expect(result.current.mpushVideoSrcAndPreview).toEqual({
997
+ mpushVideoSrc: '',
998
+ mpushVideoPreviewImg: '',
999
+ duration: 0,
1000
+ });
1001
+ expect(result.current.assetList).toEqual([]);
1002
+ });
1003
+ });
1004
+
1005
+ describe('CDN URL functions', () => {
1006
+ it('should generate CDN image URL', () => {
1007
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1008
+
1009
+ const cdnUrl = result.current.getCdnImageUrl('test-image.jpg');
1010
+
1011
+ expect(cdnUrl).toBe('cdn_test-image.jpg');
1012
+ expect(getCdnUrl).toHaveBeenCalledWith({
1013
+ url: 'test-image.jpg',
1014
+ channelName: MOBILE_PUSH_CHANNEL,
1015
+ });
1016
+ });
1017
+
1018
+ it('should return empty string for empty image URL', () => {
1019
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1020
+
1021
+ const cdnUrl = result.current.getCdnImageUrl('');
1022
+
1023
+ expect(cdnUrl).toBe('');
1024
+ });
1025
+
1026
+ it('should generate CDN video URL', () => {
1027
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1028
+
1029
+ const cdnUrl = result.current.getCdnVideoUrl('test-video.mp4');
1030
+
1031
+ expect(cdnUrl).toBe('cdn_test-video.mp4');
1032
+ expect(getCdnUrl).toHaveBeenCalledWith({
1033
+ url: 'test-video.mp4',
1034
+ channelName: MOBILE_PUSH_CHANNEL,
1035
+ });
1036
+ });
1037
+
1038
+ it('should return empty string for empty video URL', () => {
1039
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1040
+
1041
+ const cdnUrl = result.current.getCdnVideoUrl('');
1042
+
1043
+ expect(cdnUrl).toBe('');
1044
+ });
1045
+ });
1046
+ });
1047
+
1048
+ describe('useEffect behaviors', () => {
1049
+ it('should call setAndroidContent and setIosContent when imageSrc changes', () => {
1050
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1051
+
1052
+ act(() => {
1053
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
1054
+ });
1055
+
1056
+ expect(mockSetAndroidContent).toHaveBeenCalledWith(expect.any(Function));
1057
+ expect(mockSetIosContent).toHaveBeenCalledWith(expect.any(Function));
1058
+ });
1059
+
1060
+ it('should call setAndroidContent and setIosContent when video src changes', () => {
1061
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1062
+
1063
+ act(() => {
1064
+ result.current.setUpdateMpushVideoSrc(0, {
1065
+ videoSrc: 'test-video.mp4',
1066
+ previewUrl: 'test-preview.jpg',
1067
+ });
1068
+ });
1069
+
1070
+ expect(mockSetAndroidContent).toHaveBeenCalledWith(expect.any(Function));
1071
+ expect(mockSetIosContent).toHaveBeenCalledWith(expect.any(Function));
1072
+ });
1073
+ });
1074
+
1075
+ describe('Edge Cases', () => {
1076
+ it('should handle undefined file in validation functions', () => {
1077
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1078
+
1079
+ expect(() => {
1080
+ result.current.validateImageFile(undefined);
1081
+ }).toThrow();
1082
+ });
1083
+
1084
+ it('should handle missing file properties gracefully', () => {
1085
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1086
+
1087
+ const fileWithoutSize = { name: 'test.jpg' };
1088
+
1089
+ expect(() => {
1090
+ result.current.validateImageFile(fileWithoutSize);
1091
+ }).not.toThrow();
1092
+ });
1093
+
1094
+ it('should handle empty edit data gracefully', () => {
1095
+ const { result } = renderHook(() => useUpload(
1096
+ mockMobilePushActions,
1097
+ { templateDetails: null },
1098
+ {},
1099
+ {},
1100
+ false,
1101
+ false,
1102
+ mockSetAndroidContent,
1103
+ mockSetIosContent,
1104
+ ANDROID,
1105
+ defaultProps.androidContent,
1106
+ defaultProps.iosContent
1107
+ ));
1108
+
1109
+ expect(result.current.imageSrc).toEqual({
1110
+ androidImageSrc: '',
1111
+ iosImageSrc: '',
1112
+ });
1113
+ });
1114
+
1115
+ it('should handle malformed edit data structure', () => {
1116
+ const malformedEditData = {
1117
+ templateDetails: {
1118
+ versions: {
1119
+ base: {
1120
+ content: null,
1121
+ },
1122
+ },
1123
+ },
1124
+ };
1125
+
1126
+ const { result } = renderHook(() => useUpload(
1127
+ mockMobilePushActions,
1128
+ malformedEditData,
1129
+ {},
1130
+ {},
1131
+ false,
1132
+ false,
1133
+ mockSetAndroidContent,
1134
+ mockSetIosContent,
1135
+ ANDROID,
1136
+ defaultProps.androidContent,
1137
+ defaultProps.iosContent
1138
+ ));
1139
+
1140
+ expect(result.current.imageSrc).toEqual({
1141
+ androidImageSrc: '',
1142
+ iosImageSrc: '',
1143
+ });
1144
+ });
1145
+ });
1146
+
1147
+ describe('Additional Edge Cases for Complete Coverage', () => {
1148
+ it('should handle setUpdateMpushVideoSrc with minimal data', () => {
1149
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1150
+
1151
+ // Test with minimal data object
1152
+ act(() => {
1153
+ result.current.setUpdateMpushVideoSrc(0, {
1154
+ videoSrc: 'test.mp4',
1155
+ });
1156
+ });
1157
+
1158
+ expect(result.current.mpushVideoSrcAndPreview.mpushVideoSrc).toBe('test.mp4');
1159
+ expect(result.current.mpushVideoSrcAndPreview.mpushVideoPreviewImg).toBe('');
1160
+ expect(result.current.mpushVideoSrcAndPreview.duration).toBe(0);
1161
+ });
1162
+
1163
+ it('should handle setUpdateMpushVideoSrc with extra data spread', () => {
1164
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1165
+
1166
+ const extraData = {
1167
+ videoSrc: 'test.mp4',
1168
+ previewUrl: 'preview.jpg',
1169
+ videoDuration: 30,
1170
+ extraField: 'extra-value',
1171
+ anotherField: 123,
1172
+ };
1173
+
1174
+ act(() => {
1175
+ result.current.setUpdateMpushVideoSrc(0, extraData);
1176
+ });
1177
+
1178
+ expect(result.current.assetList).toEqual(expect.objectContaining({
1179
+ videoSrc: 'test.mp4',
1180
+ previewUrl: 'preview.jpg',
1181
+ videoDuration: 30,
1182
+ extraField: 'extra-value',
1183
+ anotherField: 123,
1184
+ }));
1185
+ });
1186
+
1187
+ it('should handle clearImageDataByMediaType for non-IMAGE types', () => {
1188
+ const { result } = renderHook(() => useUpload(...Object.values(defaultProps)));
1189
+
1190
+ // Set some image data first
1191
+ act(() => {
1192
+ result.current.setUpdateMpushImageSrc('test-image.jpg', 0, IMAGE);
1193
+ });
1194
+
1195
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
1196
+
1197
+ // Clear using CAROUSEL type (should not affect image data)
1198
+ act(() => {
1199
+ result.current.clearImageDataByMediaType(CAROUSEL);
1200
+ });
1201
+
1202
+ // Image data should remain unchanged for CAROUSEL
1203
+ expect(result.current.imageSrc.androidImageSrc).toBe('test-image.jpg');
1204
+
1205
+ // Set data for both platforms to test clearing behavior
1206
+ act(() => {
1207
+ result.current.setImageSrc({
1208
+ androidImageSrc: 'test-android.jpg',
1209
+ iosImageSrc: 'test-ios.jpg',
1210
+ });
1211
+ });
1212
+
1213
+ // Clear using IMAGE type (should clear only Android since activeTab is ANDROID and sameContent is false)
1214
+ act(() => {
1215
+ result.current.clearImageDataByMediaType(IMAGE);
1216
+ });
1217
+
1218
+ // Only Android should be cleared when sameContent=false
1219
+ expect(result.current.imageSrc.androidImageSrc).toBe('');
1220
+ expect(result.current.imageSrc.iosImageSrc).toBe('test-ios.jpg');
1221
+ });
1222
+ });
1223
+ });