@capillarytech/creatives-library 8.0.271 → 8.0.272

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 (149) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +34 -0
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
  9. package/tests/integration/TemplateCreation/api-response.js +31 -1
  10. package/tests/integration/TemplateCreation/msw-handler.js +2 -0
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +28 -5
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/v2Components/CapDeviceContent/index.js +61 -56
  16. package/v2Components/CapTagList/index.js +6 -1
  17. package/v2Components/CapTagListWithInput/index.js +5 -1
  18. package/v2Components/CapTagListWithInput/messages.js +1 -1
  19. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  20. package/v2Components/ErrorInfoNote/constants.js +1 -0
  21. package/v2Components/ErrorInfoNote/index.js +402 -72
  22. package/v2Components/ErrorInfoNote/messages.js +32 -6
  23. package/v2Components/ErrorInfoNote/style.scss +278 -6
  24. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  25. package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
  26. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
  27. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
  28. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  29. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  30. package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
  31. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
  32. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
  33. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  34. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  35. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
  36. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
  37. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  43. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
  44. package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
  45. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  46. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
  47. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
  48. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
  49. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
  50. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
  51. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
  52. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
  53. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  54. package/v2Components/HtmlEditor/constants.js +45 -20
  55. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  56. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
  57. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  58. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  59. package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
  60. package/v2Components/HtmlEditor/index.js +1 -1
  61. package/v2Components/HtmlEditor/messages.js +102 -94
  62. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
  63. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
  64. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  65. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  66. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
  67. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  68. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  69. package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
  70. package/v2Components/MobilePushPreviewV2/constants.js +6 -0
  71. package/v2Components/MobilePushPreviewV2/index.js +33 -7
  72. package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
  73. package/v2Components/TemplatePreview/index.js +47 -32
  74. package/v2Components/TemplatePreview/messages.js +4 -0
  75. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  76. package/v2Containers/BeeEditor/index.js +172 -90
  77. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
  78. package/v2Containers/BeePopupEditor/constants.js +10 -0
  79. package/v2Containers/BeePopupEditor/index.js +194 -0
  80. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  81. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  82. package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
  83. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  84. package/v2Containers/CreativesContainer/constants.js +1 -0
  85. package/v2Containers/CreativesContainer/index.js +251 -47
  86. package/v2Containers/CreativesContainer/messages.js +8 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  88. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
  90. package/v2Containers/Email/actions.js +7 -0
  91. package/v2Containers/Email/constants.js +5 -1
  92. package/v2Containers/Email/index.js +234 -29
  93. package/v2Containers/Email/messages.js +32 -0
  94. package/v2Containers/Email/reducer.js +12 -1
  95. package/v2Containers/Email/sagas.js +61 -7
  96. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  97. package/v2Containers/Email/tests/reducer.test.js +46 -0
  98. package/v2Containers/Email/tests/sagas.test.js +320 -29
  99. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
  100. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
  101. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  102. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2472 -0
  103. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  104. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  105. package/v2Containers/EmailWrapper/constants.js +2 -0
  106. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
  107. package/v2Containers/EmailWrapper/index.js +103 -23
  108. package/v2Containers/EmailWrapper/messages.js +65 -1
  109. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
  110. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
  111. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  112. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  113. package/v2Containers/InApp/actions.js +7 -0
  114. package/v2Containers/InApp/constants.js +20 -4
  115. package/v2Containers/InApp/index.js +802 -360
  116. package/v2Containers/InApp/index.scss +4 -3
  117. package/v2Containers/InApp/messages.js +7 -3
  118. package/v2Containers/InApp/reducer.js +21 -3
  119. package/v2Containers/InApp/sagas.js +29 -9
  120. package/v2Containers/InApp/selectors.js +25 -5
  121. package/v2Containers/InApp/tests/index.test.js +154 -50
  122. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  123. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  124. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  125. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
  126. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  127. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
  128. package/v2Containers/InAppWrapper/constants.js +16 -0
  129. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  130. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  131. package/v2Containers/InAppWrapper/index.js +148 -0
  132. package/v2Containers/InAppWrapper/messages.js +49 -0
  133. package/v2Containers/InappAdvance/index.js +1099 -0
  134. package/v2Containers/InappAdvance/index.scss +10 -0
  135. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  136. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  137. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  138. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  139. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  140. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  141. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  142. package/v2Containers/TagList/index.js +62 -19
  143. package/v2Containers/Templates/_templates.scss +60 -1
  144. package/v2Containers/Templates/index.js +89 -4
  145. package/v2Containers/Templates/messages.js +4 -0
  146. package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  148. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  149. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -75,14 +75,16 @@ describe('Email Templates Sagas', () => {
75
75
  const fakeResponse = {
76
76
  success: true,
77
77
  response: { id: 1, name: 'New Template', status: 'created' },
78
- status: { code: 200 }
78
+ status: { code: 200 },
79
79
  };
80
80
 
81
81
  return expectSaga(sagas.createTemplate, template)
82
82
  .provide([
83
83
  [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
84
84
  ])
85
- .put({ type: types.CREATE_TEMPLATE_SUCCESS, data: fakeResponse.response, statusCode: 200, errorMsg: undefined })
85
+ .put({
86
+ type: types.CREATE_TEMPLATE_SUCCESS, data: fakeResponse.response, statusCode: 200, errorMsg: undefined,
87
+ })
86
88
  .run();
87
89
  });
88
90
 
@@ -94,7 +96,7 @@ describe('Email Templates Sagas', () => {
94
96
  .provide([
95
97
  [matchers.call.fn(Api.createEmailTemplate), throwError(fakeError)],
96
98
  ])
97
- .put({ type: types.CREATE_TEMPLATE_FAILURE, error: fakeError, errorMsg: undefined })
99
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, error: fakeError, errorMsg: fakeError.message })
98
100
  .run();
99
101
  });
100
102
  });
@@ -107,7 +109,7 @@ describe('Email Templates Sagas', () => {
107
109
 
108
110
  return expectSaga(sagas.getTemplateDetails, id)
109
111
  .provide([
110
- [matchers.call.fn(Api.getTemplateDetails), { response: detailsResponse }]
112
+ [matchers.call.fn(Api.getTemplateDetails), { response: detailsResponse }],
111
113
  ])
112
114
  .put({ type: types.GET_TEMPLATE_DETAILS_SUCCESS, data: detailsResponse })
113
115
  .run();
@@ -118,7 +120,7 @@ describe('Email Templates Sagas', () => {
118
120
 
119
121
  return expectSaga(sagas.getTemplateDetails, id)
120
122
  .provide([
121
- [matchers.call.fn(Api.getTemplateDetails), throwError(error)]
123
+ [matchers.call.fn(Api.getTemplateDetails), throwError(error)],
122
124
  ])
123
125
  .put({ type: types.GET_TEMPLATE_DETAILS_FAILURE, error })
124
126
  .run();
@@ -133,17 +135,17 @@ describe('Email Templates Sagas', () => {
133
135
  it('should handle successful asset upload', () => {
134
136
  const uploadResponse = {
135
137
  response: { asset: { id: 1, url: 'http://example.com/asset.jpg', metaInfo: { secure_file_path: 'path', file_size: 1024 } } },
136
- status: { code: '200' }
138
+ status: { code: '200' },
137
139
  };
138
140
 
139
141
  return expectSaga(sagas.uploadAsset, file, assetType, fileParams)
140
142
  .provide([
141
- [matchers.call.fn(Api.uploadFile), uploadResponse]
143
+ [matchers.call.fn(Api.uploadFile), uploadResponse],
142
144
  ])
143
145
  .put({
144
146
  type: types.UPLOAD_ASSET_SUCCESS,
145
147
  data: uploadResponse.response.asset,
146
- statusCode: '200'
148
+ statusCode: '200',
147
149
  })
148
150
  .run();
149
151
  });
@@ -153,7 +155,7 @@ describe('Email Templates Sagas', () => {
153
155
 
154
156
  return expectSaga(sagas.uploadAsset, file, assetType, fileParams)
155
157
  .provide([
156
- [matchers.call.fn(Api.uploadFile), throwError(error)]
158
+ [matchers.call.fn(Api.uploadFile), throwError(error)],
157
159
  ])
158
160
  .put({ type: types.UPLOAD_ASSET_FAILURE, error })
159
161
  .run();
@@ -162,17 +164,12 @@ describe('Email Templates Sagas', () => {
162
164
  });
163
165
 
164
166
 
165
-
166
167
  describe('Combined Sagas', () => {
167
- it('v2EmailSagas should run specified sagas', () => {
168
- return expectSaga(v2EmailSagas)
169
- .run();
170
- });
168
+ it('v2EmailSagas should run specified sagas', () => expectSaga(v2EmailSagas)
169
+ .run());
171
170
 
172
- it('v2EmailDuplicateTemplateSaga should run the duplicate template sagas', () => {
173
- return expectSaga(v2EmailDuplicateTemplateSaga)
174
- .run();
175
- });
171
+ it('v2EmailDuplicateTemplateSaga should run the duplicate template sagas', () => expectSaga(v2EmailDuplicateTemplateSaga)
172
+ .run());
176
173
  });
177
174
 
178
175
 
@@ -180,7 +177,7 @@ describe('duplicateTemplate saga', () => {
180
177
  it('handles successful duplication with callback', () => {
181
178
  const fakeResponse = {
182
179
  status: { code: 200 },
183
- response: { id: 2, name: 'Duplicated Template' }
180
+ response: { id: 2, name: 'Duplicated Template' },
184
181
  };
185
182
  const callback = jest.fn();
186
183
  const payload = { id: 1, channel: 'email', callback };
@@ -193,7 +190,7 @@ describe('duplicateTemplate saga', () => {
193
190
  type: types.DUPLICATE_TEMPLATE_SUCCESS,
194
191
  data: fakeResponse.response,
195
192
  statusCode: fakeResponse.status.code,
196
- errorMsg: undefined
193
+ errorMsg: undefined,
197
194
  })
198
195
  .run()
199
196
  .then(() => {
@@ -212,7 +209,7 @@ describe('duplicateTemplate saga', () => {
212
209
  .put({
213
210
  type: types.DUPLICATE_TEMPLATE_FAILURE,
214
211
  error,
215
- errorMsg: undefined
212
+ errorMsg: undefined,
216
213
  })
217
214
  .run();
218
215
  });
@@ -220,7 +217,7 @@ describe('duplicateTemplate saga', () => {
220
217
  it('handles server-side error status', () => {
221
218
  const fakeResponse = {
222
219
  status: { code: 500 },
223
- message: 'Internal server error'
220
+ message: 'Internal server error',
224
221
  };
225
222
  const payload = { id: 1, channel: 'email' };
226
223
 
@@ -232,7 +229,7 @@ describe('duplicateTemplate saga', () => {
232
229
  type: types.DUPLICATE_TEMPLATE_SUCCESS,
233
230
  data: undefined,
234
231
  statusCode: fakeResponse.status.code,
235
- errorMsg: 'Internal server error'
232
+ errorMsg: 'Internal server error',
236
233
  })
237
234
  .run();
238
235
  });
@@ -242,7 +239,7 @@ describe('getAllAssets saga', () => {
242
239
  it('handles successful duplication with callback', () => {
243
240
  const fakeResponse = {
244
241
  status: { code: 200 },
245
- response: { id: 2, name: 'Duplicated Template' }
242
+ response: { id: 2, name: 'Duplicated Template' },
246
243
  };
247
244
  const callback = jest.fn();
248
245
  const payload = {assetType: 'image', queryParams: { page: 1 }, callback };
@@ -254,15 +251,15 @@ describe('getAllAssets saga', () => {
254
251
  .put({
255
252
  type: types.GET_ALL_ASSETS_SUCCESS,
256
253
  data: fakeResponse.response,
257
- isReset: true
254
+ isReset: true,
258
255
  })
259
- .run()
256
+ .run();
260
257
  });
261
258
 
262
259
  it('handles server-side error status', () => {
263
260
  const fakeResponse = {
264
261
  status: { code: 500 },
265
- message: 'Internal server error'
262
+ message: 'Internal server error',
266
263
  };
267
264
  const payload = {assetType: 'image', queryParams: { page: 1 } };
268
265
 
@@ -273,8 +270,302 @@ describe('getAllAssets saga', () => {
273
270
  .put({
274
271
  type: types.GET_ALL_ASSETS_SUCCESS,
275
272
  data: undefined,
276
- isReset: true
273
+ isReset: true,
277
274
  })
278
275
  .run();
279
276
  });
280
- });
277
+
278
+ describe('getCmsAccounts saga', () => {
279
+ it('should handle successful CMS accounts fetch with matching type', () => {
280
+ const cmsType = 'bee';
281
+ const fakeResponse = {
282
+ data: {
283
+ response: {
284
+ cmsAccounts: {
285
+ type: 'bee',
286
+ },
287
+ },
288
+ },
289
+ };
290
+
291
+ return expectSaga(sagas.getCmsAccounts, { cmsType })
292
+ .provide([
293
+ [matchers.call.fn(Api.getCmsAccounts), fakeResponse],
294
+ ])
295
+ .put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: true })
296
+ .run();
297
+ });
298
+
299
+ it('should handle CMS accounts fetch when type does not match', () => {
300
+ const cmsType = 'bee';
301
+ const fakeResponse = {
302
+ data: {
303
+ response: {
304
+ cmsAccounts: {
305
+ type: 'other',
306
+ },
307
+ },
308
+ },
309
+ };
310
+
311
+ return expectSaga(sagas.getCmsAccounts, { cmsType })
312
+ .provide([
313
+ [matchers.call.fn(Api.getCmsAccounts), fakeResponse],
314
+ ])
315
+ .put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: false })
316
+ .run();
317
+ });
318
+
319
+ it('should handle CMS accounts fetch when response structure is different', () => {
320
+ const cmsType = 'bee';
321
+ const fakeResponse = {
322
+ data: {
323
+ response: {},
324
+ },
325
+ };
326
+
327
+ return expectSaga(sagas.getCmsAccounts, { cmsType })
328
+ .provide([
329
+ [matchers.call.fn(Api.getCmsAccounts), fakeResponse],
330
+ ])
331
+ .put({ type: types.GET_CMS_ACCOUNTS_SUCCESS, isBeeEnabled: false })
332
+ .run();
333
+ });
334
+
335
+ it('should handle error when fetching CMS accounts fails', () => {
336
+ const cmsType = 'bee';
337
+ const fakeError = new Error('Failed to fetch CMS accounts');
338
+
339
+ return expectSaga(sagas.getCmsAccounts, { cmsType })
340
+ .provide([
341
+ [matchers.call.fn(Api.getCmsAccounts), throwError(fakeError)],
342
+ ])
343
+ .put({ type: types.GET_CMS_ACCOUNTS_FAILURE, error: fakeError })
344
+ .run();
345
+ });
346
+ });
347
+
348
+ describe('createTemplate saga error handling paths', () => {
349
+ it('should handle API error with status code >= 400', () => {
350
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
351
+ const fakeResponse = {
352
+ success: false,
353
+ status: { code: 400 },
354
+ message: 'Bad Request',
355
+ };
356
+
357
+ return expectSaga(sagas.createTemplate, template)
358
+ .provide([
359
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
360
+ ])
361
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Bad Request' })
362
+ .run()
363
+ .then(() => {
364
+ expect(template.onCreateTemplateComplete).toHaveBeenCalledWith({
365
+ error: true,
366
+ message: 'Bad Request',
367
+ statusCode: 400,
368
+ });
369
+ });
370
+ });
371
+
372
+ it('should handle API error with errorMessage field', () => {
373
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
374
+ const fakeResponse = {
375
+ success: false,
376
+ status: { code: 500 },
377
+ errorMessage: 'Server Error',
378
+ };
379
+
380
+ return expectSaga(sagas.createTemplate, template)
381
+ .provide([
382
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
383
+ ])
384
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Server Error' })
385
+ .run();
386
+ });
387
+
388
+ it('should handle API error with error.message field', () => {
389
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
390
+ const fakeResponse = {
391
+ success: false,
392
+ status: { code: 500 },
393
+ error: { message: 'Error from error object' },
394
+ };
395
+
396
+ return expectSaga(sagas.createTemplate, template)
397
+ .provide([
398
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
399
+ ])
400
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Error from error object' })
401
+ .run();
402
+ });
403
+
404
+ it('should handle API error with response.message field', () => {
405
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
406
+ const fakeResponse = {
407
+ success: false,
408
+ status: { code: 500 },
409
+ response: { message: 'Error from response' },
410
+ };
411
+
412
+ return expectSaga(sagas.createTemplate, template)
413
+ .provide([
414
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
415
+ ])
416
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Error from response' })
417
+ .run();
418
+ });
419
+
420
+ it('should handle API error with status.message field', () => {
421
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
422
+ const fakeResponse = {
423
+ success: false,
424
+ status: { code: 500, message: 'Error from status' },
425
+ };
426
+
427
+ return expectSaga(sagas.createTemplate, template)
428
+ .provide([
429
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
430
+ ])
431
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Error from status' })
432
+ .run();
433
+ });
434
+
435
+ it('should handle API error with data.message field', () => {
436
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
437
+ const fakeResponse = {
438
+ success: false,
439
+ status: { code: 500 },
440
+ data: { message: 'Error from data' },
441
+ };
442
+
443
+ return expectSaga(sagas.createTemplate, template)
444
+ .provide([
445
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
446
+ ])
447
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Error from data' })
448
+ .run();
449
+ });
450
+
451
+ it('should handle API error with unknown status code', () => {
452
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
453
+ const fakeResponse = {
454
+ success: false,
455
+ status: 500,
456
+ };
457
+
458
+ return expectSaga(sagas.createTemplate, template)
459
+ .provide([
460
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
461
+ ])
462
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'API Error: 500' })
463
+ .run();
464
+ });
465
+
466
+ it('should handle callback error gracefully', () => {
467
+ const template = {
468
+ id: 1,
469
+ name: 'New Template',
470
+ onCreateTemplateComplete: jest.fn(() => {
471
+ throw new Error('Callback error');
472
+ }),
473
+ };
474
+ const fakeResponse = {
475
+ success: false,
476
+ status: { code: 400 },
477
+ message: 'Bad Request',
478
+ };
479
+
480
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
481
+
482
+ return expectSaga(sagas.createTemplate, template)
483
+ .provide([
484
+ [matchers.call.fn(Api.createEmailTemplate), fakeResponse],
485
+ ])
486
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, errorMsg: 'Bad Request' })
487
+ .run()
488
+ .then(() => {
489
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
490
+ '[Email Saga] Error executing callback:',
491
+ expect.any(Error)
492
+ );
493
+ consoleErrorSpy.mockRestore();
494
+ });
495
+ });
496
+
497
+ it('should handle catch block with error message', () => {
498
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
499
+ const fakeError = { message: 'Error message' };
500
+
501
+ return expectSaga(sagas.createTemplate, template)
502
+ .provide([
503
+ [matchers.call.fn(Api.createEmailTemplate), throwError(fakeError)],
504
+ ])
505
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, error: fakeError, errorMsg: 'Error message' })
506
+ .run()
507
+ .then(() => {
508
+ expect(template.onCreateTemplateComplete).toHaveBeenCalledWith({
509
+ error: true,
510
+ message: 'Error message',
511
+ });
512
+ });
513
+ });
514
+
515
+ it('should handle catch block with error toString', () => {
516
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
517
+ const fakeError = { toString: () => 'Error string' };
518
+
519
+ return expectSaga(sagas.createTemplate, template)
520
+ .provide([
521
+ [matchers.call.fn(Api.createEmailTemplate), throwError(fakeError)],
522
+ ])
523
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, error: fakeError, errorMsg: 'Error string' })
524
+ .run()
525
+ .then(() => {
526
+ expect(template.onCreateTemplateComplete).toHaveBeenCalledWith({
527
+ error: true,
528
+ message: 'Error string',
529
+ });
530
+ });
531
+ });
532
+
533
+ it('should handle catch block with unknown error', () => {
534
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
535
+ // Create an error object without message
536
+ // error.toString() on {} returns '[object Object]', which is truthy, so it will be used
537
+ const fakeError = {};
538
+
539
+ return expectSaga(sagas.createTemplate, template)
540
+ .provide([
541
+ [matchers.call.fn(Api.createEmailTemplate), throwError(fakeError)],
542
+ ])
543
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, error: fakeError, errorMsg: '[object Object]' })
544
+ .run()
545
+ .then(() => {
546
+ expect(template.onCreateTemplateComplete).toHaveBeenCalledWith({
547
+ error: true,
548
+ message: '[object Object]',
549
+ });
550
+ });
551
+ });
552
+
553
+ it('should handle catch block when toString returns empty string', () => {
554
+ const template = { id: 1, name: 'New Template', onCreateTemplateComplete: jest.fn() };
555
+ const fakeError = { toString: () => '' };
556
+
557
+ return expectSaga(sagas.createTemplate, template)
558
+ .provide([
559
+ [matchers.call.fn(Api.createEmailTemplate), throwError(fakeError)],
560
+ ])
561
+ .put({ type: types.CREATE_TEMPLATE_FAILURE, error: fakeError, errorMsg: 'An unexpected error occurred' })
562
+ .run()
563
+ .then(() => {
564
+ expect(template.onCreateTemplateComplete).toHaveBeenCalledWith({
565
+ error: true,
566
+ message: 'An unexpected error occurred',
567
+ });
568
+ });
569
+ });
570
+ });
571
+ });