@capillarytech/creatives-library 8.0.87-alpha.21 → 8.0.87-alpha.22

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 (55) hide show
  1. package/containers/Templates/constants.js +6 -0
  2. package/containers/Templates/index.js +44 -24
  3. package/package.json +1 -1
  4. package/services/api.js +20 -10
  5. package/services/tests/api.test.js +5 -1
  6. package/utils/commonUtils.js +64 -10
  7. package/utils/tests/commonUtil.test.js +108 -3
  8. package/utils/tests/transformerUtils.test.js +2127 -0
  9. package/v2Components/CapImageUpload/index.js +13 -10
  10. package/v2Components/CapVideoUpload/index.js +12 -9
  11. package/v2Components/CapWhatsappCTA/messages.js +4 -0
  12. package/v2Components/CapWhatsappCarouselButton/constant.js +56 -0
  13. package/v2Components/CapWhatsappCarouselButton/index.js +446 -0
  14. package/v2Components/CapWhatsappCarouselButton/index.scss +39 -0
  15. package/v2Components/CapWhatsappCarouselButton/tests/index.test.js +237 -0
  16. package/v2Components/FormBuilder/constants.js +8 -0
  17. package/v2Components/FormBuilder/index.js +2 -2
  18. package/v2Components/TemplatePreview/_templatePreview.scss +20 -0
  19. package/v2Components/TemplatePreview/assets/images/empty_image_preview.svg +4 -0
  20. package/v2Components/TemplatePreview/assets/images/empty_video_preview.svg +4 -0
  21. package/v2Components/TemplatePreview/index.js +160 -105
  22. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +6 -6
  23. package/v2Containers/CreativesContainer/SlideBoxContent.js +0 -6
  24. package/v2Containers/CreativesContainer/index.js +100 -7
  25. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +3 -0
  26. package/v2Containers/Email/index.js +0 -1
  27. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +192 -0
  28. package/v2Containers/EmailWrapper/constants.js +11 -1
  29. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +343 -0
  30. package/v2Containers/EmailWrapper/index.js +116 -300
  31. package/v2Containers/EmailWrapper/mockdata/mockdata.js +119 -0
  32. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
  33. package/v2Containers/EmailWrapper/tests/index.test.js +101 -0
  34. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +601 -0
  35. package/v2Containers/MobilePush/Edit/index.js +0 -1
  36. package/v2Containers/MobilepushWrapper/index.js +1 -2
  37. package/v2Containers/Sms/Create/index.js +0 -1
  38. package/v2Containers/SmsWrapper/index.js +0 -2
  39. package/v2Containers/Templates/_templates.scss +47 -0
  40. package/v2Containers/Templates/index.js +55 -5
  41. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +177 -156
  42. package/v2Containers/Whatsapp/constants.js +87 -1
  43. package/v2Containers/Whatsapp/index.js +715 -190
  44. package/v2Containers/Whatsapp/index.scss +52 -1
  45. package/v2Containers/Whatsapp/messages.js +38 -2
  46. package/v2Containers/Whatsapp/styles.scss +5 -0
  47. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +27722 -90751
  48. package/v2Containers/Whatsapp/tests/__snapshots__/utils.test.js.snap +6 -0
  49. package/v2Containers/Whatsapp/tests/mockData.js +3 -7
  50. package/v2Containers/Whatsapp/tests/utils.test.js +178 -1
  51. package/v2Containers/Whatsapp/utils.js +52 -0
  52. package/v2Containers/Zalo/index.js +47 -15
  53. package/v2Containers/Zalo/index.scss +8 -0
  54. package/v2Containers/Zalo/messages.js +4 -0
  55. package/v2Containers/mockdata.js +2 -0
@@ -0,0 +1,601 @@
1
+ import React from 'react';
2
+ import useEmailWrapper from '../hooks/useEmailWrapper';
3
+ import { EmailWrapperMockData } from '../mockdata/mockdata';
4
+ import _, { isEmpty, find } from 'lodash';
5
+ import { renderHook, waitFor, act as reactAct } from '@testing-library/react';
6
+ import { EMAIL_CREATE_MODES, STEPS } from '../constants';
7
+ import { GA } from '@capillarytech/cap-ui-utils';
8
+ import { gtmPush } from '../../../utils/gtmTrackers';
9
+
10
+ // Mock dependencies
11
+ jest.mock('lodash', () => ({
12
+ ...jest.requireActual('lodash'),
13
+ isEmpty: jest.fn().mockImplementation(val => {
14
+ if (val === null || val === undefined) return true;
15
+ if (typeof val === 'object') return Object.keys(val).length === 0;
16
+ if (typeof val === 'string') return val.trim().length === 0;
17
+ return !val;
18
+ }),
19
+ find: jest.fn(),
20
+ }));
21
+
22
+ let mockCapNotificationError;
23
+ jest.mock('@capillarytech/cap-ui-library/CapNotification', () => {
24
+ mockCapNotificationError = jest.fn();
25
+ return {
26
+ error: mockCapNotificationError,
27
+ };
28
+ });
29
+
30
+ jest.mock('../../../utils/gtmTrackers', () => ({
31
+ gtmPush: jest.fn(),
32
+ }));
33
+
34
+ const mockStopTimer = jest.fn();
35
+
36
+ jest.mock('@capillarytech/cap-ui-utils', () => ({
37
+ ...jest.requireActual('@capillarytech/cap-ui-utils'),
38
+ GA: {
39
+ timeTracker: {
40
+ startTimer: jest.fn(),
41
+ stopTimer: jest.fn(),
42
+ }
43
+ }
44
+ }));
45
+
46
+ describe('useEmailWrapper', () => {
47
+ let mockProps;
48
+
49
+ beforeEach(() => {
50
+ jest.clearAllMocks();
51
+ isEmpty.mockClear();
52
+ find.mockClear();
53
+ mockCapNotificationError.mockClear();
54
+ gtmPush.mockClear();
55
+ mockStopTimer.mockClear();
56
+
57
+ isEmpty.mockImplementation(val => {
58
+ if (val === null || val === undefined) return true;
59
+ if (typeof val === 'object') return Object.keys(val).length === 0;
60
+ if (typeof val === 'string') return val.trim().length === 0;
61
+ return !val;
62
+ });
63
+
64
+ mockProps = {
65
+ ...EmailWrapperMockData,
66
+ intl: { formatMessage: jest.fn((msg) => msg.defaultMessage || msg.id || '') },
67
+ onEmailModeChange: jest.fn(),
68
+ showNextStep: jest.fn(),
69
+ onResetStep: jest.fn(),
70
+ onEnterTemplateName: jest.fn(),
71
+ onRemoveTemplateName: jest.fn(),
72
+ templatesActions: {
73
+ resetTemplateData: jest.fn(),
74
+ getDefaultBeeTemplates: jest.fn(),
75
+ handleZipUpload: jest.fn(),
76
+ handleHtmlUpload: jest.fn(),
77
+ setEdmTemplate: jest.fn(),
78
+ setBEETemplate: jest.fn(),
79
+ },
80
+ EmailLayout: null,
81
+ CmsTemplates: null,
82
+ SelectedEdmDefaultTemplate: null,
83
+ isUploading: false,
84
+ getCmsTemplatesInProgress: false,
85
+ step: STEPS.MODE_SELECTION,
86
+ emailCreateMode: '',
87
+ setIsLoadingContent: jest.fn(),
88
+ isGetFormData: false,
89
+ getFormdata: jest.fn(),
90
+ type: 'someType',
91
+ isFullMode: false,
92
+ cap: { currentOrgDetails: { id: 'org1' } },
93
+ showTemplateName: true,
94
+ showLiquidErrorInFooter: false,
95
+ onValidationFail: jest.fn(),
96
+ forwardedTags: [],
97
+ selectedOfferDetails: null,
98
+ onPreviewContentClicked: jest.fn(),
99
+ onTestContentClicked: jest.fn(),
100
+ editor: null,
101
+ moduleType: '',
102
+ eventContextTags: [],
103
+ isLoyaltyModule: false,
104
+ cmsTemplatesLoader: false,
105
+ currentOrgDetails: { id: 'org1' },
106
+ };
107
+ });
108
+
109
+ it('is a function', () => {
110
+ expect(typeof useEmailWrapper).toBe('function');
111
+ });
112
+
113
+ it('initializes with correct default state', () => {
114
+ const { result } = renderHook(() => useEmailWrapper(mockProps));
115
+
116
+ expect(result.current.templateName).toBeDefined();
117
+ expect(result.current.modes).toBeDefined();
118
+ expect(result.current.onTemplateNameChange).toBeInstanceOf(Function);
119
+ });
120
+
121
+ it('handles template name change correctly', () => {
122
+ const { result } = renderHook(() => useEmailWrapper(mockProps));
123
+
124
+ reactAct(() => {
125
+ result.current.onTemplateNameChange({ target: { value: 'New Template' } });
126
+ });
127
+
128
+ expect(result.current.templateName).toBe('New Template');
129
+ expect(result.current.isTemplateNameEmpty).toBe(false);
130
+ expect(mockProps.onEnterTemplateName).toHaveBeenCalled();
131
+ });
132
+
133
+ it('detects empty template name correctly', () => {
134
+ const { result } = renderHook(() => useEmailWrapper(mockProps));
135
+
136
+ reactAct(() => {
137
+ result.current.onTemplateNameChange({ target: { value: '' } });
138
+ });
139
+
140
+ expect(result.current.templateName).toBe('');
141
+ expect(result.current.isTemplateNameEmpty).toBe(true);
142
+ expect(mockProps.onRemoveTemplateName).toHaveBeenCalled();
143
+ });
144
+
145
+ it('handles mode change correctly', () => {
146
+ const { result } = renderHook(() => useEmailWrapper(mockProps));
147
+
148
+ reactAct(() => {
149
+ result.current.onChange({ target: { value: EMAIL_CREATE_MODES.UPLOAD } });
150
+ });
151
+
152
+ expect(mockProps.onEmailModeChange).toHaveBeenCalledWith(EMAIL_CREATE_MODES.UPLOAD);
153
+ });
154
+
155
+ it('handles zip file upload correctly and calls success callbacks', () => {
156
+ const successCallback = mockProps.showNextStep;
157
+ const errorCallback = jest.fn();
158
+
159
+ mockProps.templatesActions.handleZipUpload.mockImplementation((file, onSuccess, onError) => {
160
+ onSuccess();
161
+ });
162
+
163
+ const { result } = renderHook(() => useEmailWrapper({
164
+ ...mockProps,
165
+ isUploading: false,
166
+ }));
167
+
168
+ const mockFile = {
169
+ name: 'test.zip',
170
+ size: 1024 * 1024,
171
+ originFileObj: new Blob(['test data'], { type: 'application/zip' })
172
+ };
173
+
174
+ reactAct(() => {
175
+ result.current.useFileUpload({ file: mockFile });
176
+ });
177
+
178
+ expect(mockProps.templatesActions.handleZipUpload).toHaveBeenCalledWith(
179
+ mockFile.originFileObj,
180
+ expect.any(Function),
181
+ expect.any(Function)
182
+ );
183
+
184
+ // expect(mockStopTimer).toHaveBeenCalled();
185
+ expect(gtmPush).toHaveBeenCalled();
186
+ expect(successCallback).toHaveBeenCalled();
187
+ expect(mockCapNotificationError).not.toHaveBeenCalled();
188
+ expect(result.current.modeContent).toEqual({ file: mockFile });
189
+ });
190
+
191
+ it('handles zip file upload error correctly', () => {
192
+ mockProps.templatesActions.handleZipUpload.mockImplementation((file, onSuccess, onError) => {
193
+ onError();
194
+ });
195
+
196
+ const { result } = renderHook(() => useEmailWrapper({
197
+ ...mockProps,
198
+ isUploading: false,
199
+ }));
200
+
201
+ const mockFile = {
202
+ name: 'test.zip',
203
+ size: 1024 * 1024,
204
+ originFileObj: new Blob(['test data'], { type: 'application/zip' })
205
+ };
206
+
207
+ reactAct(() => {
208
+ result.current.useFileUpload({ file: mockFile });
209
+ });
210
+
211
+ expect(mockProps.templatesActions.handleZipUpload).toHaveBeenCalled();
212
+ expect(mockCapNotificationError).toHaveBeenCalled();
213
+ expect(mockProps.showNextStep).not.toHaveBeenCalled();
214
+ });
215
+
216
+ it('handles HTML file upload correctly', async () => {
217
+ const mockHtmlContent = '<html><body>Test</body></html>';
218
+ const mockFile = {
219
+ name: 'test.html',
220
+ size: 1024,
221
+ originFileObj: new Blob([mockHtmlContent], { type: 'text/html' })
222
+ };
223
+
224
+ const mockReader = {
225
+ onload: null,
226
+ onerror: null,
227
+ readAsText: jest.fn(function() {
228
+ this.result = mockHtmlContent;
229
+ if (this.onload) {
230
+ this.onload();
231
+ }
232
+ }),
233
+ result: '',
234
+ };
235
+ const fileReaderSpy = jest.spyOn(global, 'FileReader').mockImplementation(() => mockReader);
236
+
237
+ const { result } = renderHook(() => useEmailWrapper({
238
+ ...mockProps,
239
+ isUploading: false,
240
+ }));
241
+
242
+ reactAct(() => {
243
+ result.current.useFileUpload({ file: mockFile });
244
+ });
245
+
246
+ await waitFor(() => {
247
+ expect(mockReader.readAsText).toHaveBeenCalledWith(mockFile.originFileObj);
248
+ expect(mockProps.templatesActions.handleHtmlUpload).toHaveBeenCalledWith(mockHtmlContent);
249
+ });
250
+
251
+ expect(result.current.modeContent).toEqual({ file: mockFile });
252
+ expect(mockCapNotificationError).not.toHaveBeenCalled();
253
+
254
+ fileReaderSpy.mockRestore();
255
+ });
256
+
257
+ it('shows error for invalid file type during upload', () => {
258
+ const { result } = renderHook(() => useEmailWrapper({
259
+ ...mockProps,
260
+ isUploading: false
261
+ }));
262
+
263
+ const mockFile = {
264
+ name: 'test.txt',
265
+ size: 1024,
266
+ originFileObj: new Blob(['test data'], { type: 'text/plain' })
267
+ };
268
+
269
+ reactAct(() => {
270
+ result.current.useFileUpload({ file: mockFile });
271
+ });
272
+
273
+ expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({
274
+ key: "email-upload-error",
275
+ }));
276
+ expect(mockProps.templatesActions.handleZipUpload).not.toHaveBeenCalled();
277
+ expect(mockProps.templatesActions.handleHtmlUpload).not.toHaveBeenCalled();
278
+ });
279
+
280
+ it('shows error for oversized file during upload', () => {
281
+ const { result } = renderHook(() => useEmailWrapper({
282
+ ...mockProps,
283
+ isUploading: false
284
+ }));
285
+
286
+ const mockFile = {
287
+ name: 'large_file.zip',
288
+ size: 6 * 1024 * 1024,
289
+ originFileObj: new Blob(['large data'], { type: 'application/zip' })
290
+ };
291
+
292
+ reactAct(() => {
293
+ result.current.useFileUpload({ file: mockFile });
294
+ });
295
+
296
+ expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({
297
+ key: "email-upload-error",
298
+ }));
299
+ expect(mockProps.templatesActions.handleZipUpload).not.toHaveBeenCalled();
300
+ });
301
+
302
+ it('shows error if no file or originFileObj is provided for upload', () => {
303
+ const { result } = renderHook(() => useEmailWrapper({
304
+ ...mockProps,
305
+ isUploading: false
306
+ }));
307
+
308
+ reactAct(() => {
309
+ result.current.useFileUpload({ file: null });
310
+ });
311
+ expect(mockCapNotificationError).toHaveBeenCalledTimes(1);
312
+ expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({ key: "email-upload-error"}));
313
+
314
+ mockCapNotificationError.mockClear();
315
+ reactAct(() => {
316
+ result.current.useFileUpload({ file: { name: 'test.zip', size: 100 } });
317
+ });
318
+ expect(mockCapNotificationError).toHaveBeenCalledTimes(1);
319
+ expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({ key: "email-upload-error"}));
320
+
321
+ expect(mockProps.templatesActions.handleZipUpload).not.toHaveBeenCalled();
322
+ expect(mockProps.templatesActions.handleHtmlUpload).not.toHaveBeenCalled();
323
+ });
324
+
325
+ it('calls showNextStep when useEditor is invoked', () => {
326
+ const { result } = renderHook(() => useEmailWrapper(mockProps));
327
+ const templateId = 'template123';
328
+
329
+ reactAct(() => {
330
+ result.current.cmsTemplatesProps.handleEdmDefaultTemplateSelection(templateId);
331
+ });
332
+
333
+ expect(result.current.modeContent).toEqual({ id: templateId });
334
+ expect(mockProps.showNextStep).toHaveBeenCalled();
335
+ });
336
+
337
+ it('calls getDefaultBeeTemplates in useEffect when in EDITOR mode, TEMPLATE_SELECTION step, and no templates', () => {
338
+ renderHook(() => useEmailWrapper({
339
+ ...mockProps,
340
+ step: STEPS.TEMPLATE_SELECTION,
341
+ emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
342
+ CmsTemplates: null,
343
+ getCmsTemplatesInProgress: false,
344
+ }));
345
+
346
+ expect(mockProps.templatesActions.getDefaultBeeTemplates).toHaveBeenCalledTimes(1);
347
+ });
348
+
349
+ it('does NOT call getDefaultBeeTemplates if templates already exist or are loading', () => {
350
+ renderHook(() => useEmailWrapper({
351
+ ...mockProps,
352
+ step: STEPS.TEMPLATE_SELECTION,
353
+ emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
354
+ CmsTemplates: [{ _id: '1', name: 'Test Template' }],
355
+ getCmsTemplatesInProgress: false,
356
+ }));
357
+ expect(mockProps.templatesActions.getDefaultBeeTemplates).not.toHaveBeenCalled();
358
+
359
+ renderHook(() => useEmailWrapper({
360
+ ...mockProps,
361
+ step: STEPS.TEMPLATE_SELECTION,
362
+ emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
363
+ CmsTemplates: null,
364
+ getCmsTemplatesInProgress: true,
365
+ }));
366
+ expect(mockProps.templatesActions.getDefaultBeeTemplates).not.toHaveBeenCalled();
367
+ });
368
+
369
+ it('calls setEdmTemplate/setBEETemplate in useEffect when in EDITOR mode and CREATE_TEMPLATE_CONTENT step', async () => {
370
+ // 1. Setup
371
+ const templateId = 'editorTemplate456';
372
+ const mockTemplateData = { _id: templateId, name: 'Selected Editor Template' };
373
+
374
+ // Ensure mocks are clean from previous tests
375
+ find.mockClear();
376
+ isEmpty.mockClear();
377
+ mockProps.templatesActions.setEdmTemplate.mockClear();
378
+ mockProps.templatesActions.setBEETemplate.mockClear();
379
+ mockProps.showNextStep.mockClear();
380
+
381
+ // 2. Mock find (this will be called by the *internal* handleEdmDefaultTemplateSelection)
382
+ find.mockImplementation((array, criteria) => {
383
+ // console.log('DEBUG: find called with', JSON.stringify(array), JSON.stringify(criteria)); // Optional debug
384
+ if (array && criteria && criteria._id === templateId && array[0]?._id === templateId) {
385
+ return mockTemplateData;
386
+ }
387
+ return null;
388
+ });
389
+
390
+ // 3. Initial Render (Template Selection Step)
391
+ const initialProps = {
392
+ ...mockProps,
393
+ step: STEPS.TEMPLATE_SELECTION,
394
+ emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
395
+ CmsTemplates: [mockTemplateData],
396
+ SelectedEdmDefaultTemplate: null,
397
+ // selectedCreateMode is initially '' from hook's useState
398
+ };
399
+ const { result, rerender } = renderHook((props) => useEmailWrapper(props), {
400
+ initialProps: initialProps
401
+ });
402
+
403
+ // 4. Simulate user selecting a template ID via the exposed callback (useEditor)
404
+ reactAct(() => {
405
+ // This maps to useEditor which calls setModeContent and showNextStep
406
+ result.current.cmsTemplatesProps.handleEdmDefaultTemplateSelection(templateId);
407
+ });
408
+ // Verify internal state updated and mock called
409
+ expect(result.current.modeContent).toEqual({ id: templateId }); // State updated
410
+ expect(mockProps.showNextStep).toHaveBeenCalledTimes(1); // User action effect
411
+
412
+ // 5. Rerender with step changed to CREATE_TEMPLATE_CONTENT
413
+ const createContentProps = {
414
+ ...initialProps, // Base props
415
+ step: STEPS.CREATE_TEMPLATE_CONTENT, // *** Change step ***
416
+ SelectedEdmDefaultTemplate: null // *** Ensure this is null ***
417
+ // The internal state `modeContent = { id: templateId }` persists
418
+ // The internal state `selectedCreateMode = ''` also persists (not changed yet)
419
+ };
420
+
421
+ // Mock isEmpty just before rerender for the specific check inside the effect
422
+ isEmpty.mockImplementation(val => {
423
+ // console.log('DEBUG: isEmpty called with', JSON.stringify(val)); // Optional debug
424
+ // Return true ONLY for the 'SelectedEdmDefaultTemplate' check (which is null)
425
+ if (val === null) return true;
426
+ // Provide a fallback for other potential isEmpty calls if needed
427
+ if (typeof val === 'object' && !val) return true;
428
+ if (typeof val === 'object' && Object.keys(val).length === 0) return true;
429
+ if (typeof val === 'string' && val.trim().length === 0) return true;
430
+ return !val;
431
+ });
432
+
433
+ rerender(createContentProps);
434
+
435
+ // 6. Wait for effects triggered by the rerender
436
+ await reactAct(async () => {
437
+ // Short pause allows React's scheduler to run effects
438
+ await new Promise(resolve => setTimeout(resolve, 0));
439
+ });
440
+
441
+
442
+ // Assert that the template setting actions were called as a result of the effect
443
+ expect(mockProps.templatesActions.setEdmTemplate).toHaveBeenCalledTimes(1);
444
+ expect(mockProps.templatesActions.setEdmTemplate).toHaveBeenCalledWith(mockTemplateData);
445
+ expect(mockProps.templatesActions.setBEETemplate).toHaveBeenCalledTimes(1);
446
+ expect(mockProps.templatesActions.setBEETemplate).toHaveBeenCalledWith(mockTemplateData);
447
+ });
448
+
449
+ it('sets selectedCreateMode in useEffect when in UPLOAD mode and CREATE_TEMPLATE_CONTENT step with EmailLayout', async () => {
450
+ const initialRenderProps = {
451
+ ...mockProps,
452
+ step: STEPS.MODE_SELECTION,
453
+ emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
454
+ EmailLayout: null,
455
+ };
456
+
457
+ const { result, rerender } = renderHook((props) => useEmailWrapper(props), {
458
+ initialProps: initialRenderProps
459
+ });
460
+
461
+ const rerenderProps = {
462
+ ...mockProps,
463
+ step: STEPS.CREATE_TEMPLATE_CONTENT,
464
+ emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
465
+ EmailLayout: { html: '<p>Test Layout</p>' },
466
+ };
467
+
468
+ // Reset isEmpty to default before setting the specific mock for the effect
469
+ isEmpty.mockImplementation((val) => {
470
+ if (val === null || val === undefined) return true;
471
+ if (typeof val === 'object') return Object.keys(val).length === 0;
472
+ if (typeof val === 'string') return val.trim().length === 0;
473
+ return !val;
474
+ });
475
+ // Mock isEmpty specifically for the EmailLayout check inside the useEffect
476
+ isEmpty.mockImplementationOnce(() => false); // Mock !isEmpty(EmailLayout) to be true
477
+
478
+ rerender(rerenderProps);
479
+
480
+ // Wait for the effect to run, set selectedCreateMode, and isShowEmailCreate to update
481
+ await waitFor(() => {
482
+ expect(result.current.isShowEmailCreate).toBe(true);
483
+ });
484
+
485
+ // Restore default isEmpty behavior after the specific mock is used
486
+ isEmpty.mockImplementation((val) => {
487
+ if (val === null || val === undefined) return true;
488
+ if (typeof val === 'object') return Object.keys(val).length === 0;
489
+ if (typeof val === 'string') return val.trim().length === 0;
490
+ return !val;
491
+ });
492
+ isEmpty.mockClear(); // Clear any remaining mock state if needed
493
+
494
+ expect(mockProps.templatesActions.setEdmTemplate).not.toHaveBeenCalled();
495
+ expect(mockProps.templatesActions.setBEETemplate).not.toHaveBeenCalled();
496
+ });
497
+
498
+ it('calls cleanup functions on unmount', () => {
499
+ const { unmount } = renderHook(() => useEmailWrapper(mockProps));
500
+
501
+ unmount();
502
+
503
+ expect(mockProps.onResetStep).toHaveBeenCalled();
504
+ expect(mockProps.templatesActions.resetTemplateData).toHaveBeenCalled();
505
+ });
506
+
507
+ it('correctly computes isShowEmailCreate based on state transitions', async () => {
508
+ // --- Case 1: Initial state -> false ---
509
+ const initialPropsCase1 = {
510
+ ...mockProps,
511
+ step: STEPS.MODE_SELECTION,
512
+ emailCreateMode: '',
513
+ EmailLayout: null,
514
+ SelectedEdmDefaultTemplate: null,
515
+ };
516
+ // Mock isEmpty for initial render checks if needed (though selectedCreateMode is the main driver)
517
+ isEmpty.mockReturnValue(true); // General mock for empty things
518
+
519
+ const { result, rerender } = renderHook((props) => useEmailWrapper(props), {
520
+ initialProps: initialPropsCase1,
521
+ });
522
+ expect(result.current.isShowEmailCreate).toBe(false);
523
+ isEmpty.mockClear(); // Clear general mock
524
+
525
+ // --- Case 2: UPLOAD mode selected, Layout available -> true ---
526
+ const uploadProps = {
527
+ ...mockProps,
528
+ step: STEPS.CREATE_TEMPLATE_CONTENT,
529
+ emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
530
+ EmailLayout: { html: '<p>Test</p>' }, // Layout is present
531
+ SelectedEdmDefaultTemplate: null,
532
+ };
533
+
534
+ // Reset isEmpty to default before setting the specific mock for the effect
535
+ isEmpty.mockImplementation((val) => {
536
+ if (val === null || val === undefined) return true;
537
+ if (typeof val === 'object') return Object.keys(val).length === 0;
538
+ if (typeof val === 'string') return val.trim().length === 0;
539
+ return !val;
540
+ });
541
+ // Mock isEmpty specifically for the EmailLayout check inside the useEffect
542
+ isEmpty.mockImplementationOnce(() => false); // Mock !isEmpty(EmailLayout) to be true
543
+
544
+ rerender(uploadProps);
545
+
546
+ // Wait for the effect to run, set selectedCreateMode, and isShowEmailCreate to update
547
+ await waitFor(() => {
548
+ expect(result.current.isShowEmailCreate).toBe(true);
549
+ });
550
+
551
+ // Restore default isEmpty behavior after the specific mock is used
552
+ isEmpty.mockImplementation((val) => {
553
+ if (val === null || val === undefined) return true;
554
+ if (typeof val === 'object') return Object.keys(val).length === 0;
555
+ if (typeof val === 'string') return val.trim().length === 0;
556
+ return !val;
557
+ });
558
+ isEmpty.mockClear(); // Clear any remaining mock state if needed
559
+
560
+ // --- Case 3: EDITOR mode selected, Template becomes available -> true (via effect) ---
561
+ const templateId = 't1';
562
+ const mockTemplate = { _id: templateId };
563
+ find.mockReturnValue(mockTemplate); // Ensure find is mocked for this case
564
+
565
+ const editorPropsInitial = {
566
+ ...mockProps,
567
+ step: STEPS.TEMPLATE_SELECTION,
568
+ emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
569
+ CmsTemplates: [mockTemplate],
570
+ SelectedEdmDefaultTemplate: null,
571
+ };
572
+ rerender(editorPropsInitial);
573
+
574
+ // Simulate selecting the template via the exposed handler
575
+ reactAct(() => {
576
+ result.current.cmsTemplatesProps.handleEdmDefaultTemplateSelection(templateId);
577
+ });
578
+ // Verify modeContent was updated internally by the act above
579
+ expect(result.current.modeContent).toEqual({ id: templateId });
580
+
581
+ const editorPropsFinal = {
582
+ ...mockProps, // Use fresh props base
583
+ step: STEPS.CREATE_TEMPLATE_CONTENT, // Move to the create step
584
+ emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
585
+ CmsTemplates: [mockTemplate],
586
+ SelectedEdmDefaultTemplate: null, // Template not yet selected in props
587
+ // modeContent state ({id: templateId}) should persist internally from the reactAct call
588
+ };
589
+
590
+ // Mock isEmpty specifically for the check within the useEffect triggered by rerender
591
+ // It should return true for SelectedEdmDefaultTemplate to proceed
592
+ isEmpty.mockImplementation((arg) => arg === null || arg === undefined || Object.keys(arg).length === 0); // General implementation first
593
+ isEmpty.mockImplementationOnce(() => true); // Mock the specific check for SelectedEdmDefaultTemplate
594
+
595
+ rerender(editorPropsFinal);
596
+
597
+ // Cleanup mocks if necessary for subsequent tests (though beforeEach should handle)
598
+ find.mockClear();
599
+ isEmpty.mockClear();
600
+ });
601
+ });
@@ -1991,7 +1991,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
1991
1991
  hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
1992
1992
  isFullMode={this.props.isFullMode}
1993
1993
  eventContextTags={this.props?.eventContextTags}
1994
- messageDetails={this.props?.messageDetails}
1995
1994
  />}
1996
1995
  </CapColumn>
1997
1996
  {this.props.iosCtasData && this.state.showIosCtaTable &&
@@ -72,7 +72,7 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
72
72
  }
73
73
 
74
74
  render() {
75
- const {mobilePushCreateMode, step, getFormData, setIsLoadingContent, isGetFormData, query, isFullMode, showTemplateName, type, onValidationFail, onPreviewContentClicked, onTestContentClicked, templateData, eventContextTags = [], messageDetails = {}} = this.props;
75
+ const {mobilePushCreateMode, step, getFormData, setIsLoadingContent, isGetFormData, query, isFullMode, showTemplateName, type, onValidationFail, onPreviewContentClicked, onTestContentClicked, templateData, eventContextTags = []} = this.props;
76
76
  const {templateName} = this.state;
77
77
  const isShowMobilepushCreate = !isEmpty(mobilePushCreateMode);
78
78
  return (
@@ -120,7 +120,6 @@ export class MobilepushWrapper extends React.Component { // eslint-disable-line
120
120
  templateData={templateData}
121
121
  hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
122
122
  eventContextTags={eventContextTags}
123
- messageDetails={messageDetails}
124
123
  />
125
124
 
126
125
 
@@ -994,7 +994,6 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
994
994
  onTestContentClicked={this.props.onTestContentClicked}
995
995
  onPreviewContentClicked={this.props.onPreviewContentClicked}
996
996
  eventContextTags={this.props?.eventContextTags}
997
- messageDetails={this.props?.messageDetails}
998
997
  />
999
998
  </CapColumn>
1000
999
  </CapRow>
@@ -30,7 +30,6 @@ const SmsWrapper = (props) => {
30
30
  smsRegister,
31
31
  onShowTemplates,
32
32
  eventContextTags,
33
- messageDetails = {},
34
33
  } = props;
35
34
 
36
35
  const smsProps = {
@@ -47,7 +46,6 @@ const SmsWrapper = (props) => {
47
46
  onPreviewContentClicked,
48
47
  onTestContentClicked,
49
48
  eventContextTags,
50
- messageDetails,
51
49
  };
52
50
  const isTraiDlt = isTraiDLTEnable(isFullMode, smsRegister);
53
51
  return <>