@capillarytech/creatives-library 8.0.345-alpha.12 → 8.0.345-alpha.13

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 (138) hide show
  1. package/constants/unified.js +0 -29
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -13
  5. package/utils/commonUtils.js +1 -19
  6. package/v2Components/CapActionButton/constants.js +0 -7
  7. package/v2Components/CapActionButton/index.js +109 -167
  8. package/v2Components/CapActionButton/index.scss +6 -157
  9. package/v2Components/CapActionButton/messages.js +3 -19
  10. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  11. package/v2Components/CapCustomSkeleton/index.js +1 -1
  12. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  13. package/v2Components/CapTagList/index.js +0 -10
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -160
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -341
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  25. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  26. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  27. package/v2Components/CommonTestAndPreview/index.js +186 -676
  28. package/v2Components/CommonTestAndPreview/messages.js +3 -49
  29. package/v2Components/CommonTestAndPreview/sagas.js +6 -15
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  34. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  35. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  36. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  37. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  38. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
  39. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  40. package/v2Components/FormBuilder/index.js +10 -8
  41. package/v2Components/TemplatePreview/_templatePreview.scss +23 -33
  42. package/v2Components/TemplatePreview/index.js +28 -143
  43. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  44. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  45. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  46. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  47. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  48. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  49. package/v2Containers/CreativesContainer/SlideBoxFooter.js +4 -11
  50. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  51. package/v2Containers/CreativesContainer/constants.js +0 -9
  52. package/v2Containers/CreativesContainer/index.js +108 -300
  53. package/v2Containers/CreativesContainer/index.scss +1 -51
  54. package/v2Containers/CreativesContainer/messages.js +4 -0
  55. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  56. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  57. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  58. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  59. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +18 -20
  60. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  61. package/v2Containers/Rcs/constants.js +8 -119
  62. package/v2Containers/Rcs/index.js +812 -2375
  63. package/v2Containers/Rcs/index.scss +6 -276
  64. package/v2Containers/Rcs/messages.js +3 -38
  65. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70345 -98302
  66. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  67. package/v2Containers/Rcs/tests/index.test.js +121 -152
  68. package/v2Containers/Rcs/tests/mockData.js +0 -38
  69. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  70. package/v2Containers/Rcs/utils.js +11 -478
  71. package/v2Containers/Sms/Create/index.js +40 -100
  72. package/v2Containers/SmsTrai/Create/index.js +4 -9
  73. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  74. package/v2Containers/SmsTrai/Edit/index.js +130 -636
  75. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  76. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
  77. package/v2Containers/SmsWrapper/index.js +8 -37
  78. package/v2Containers/TagList/index.js +0 -6
  79. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  80. package/v2Containers/Templates/_templates.scss +126 -181
  81. package/v2Containers/Templates/actions.js +36 -11
  82. package/v2Containers/Templates/constants.js +23 -2
  83. package/v2Containers/Templates/index.js +333 -142
  84. package/v2Containers/Templates/messages.js +68 -0
  85. package/v2Containers/Templates/reducer.js +68 -0
  86. package/v2Containers/Templates/sagas.js +98 -55
  87. package/v2Containers/Templates/selectors.js +12 -0
  88. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  89. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1256 -1042
  90. package/v2Containers/Templates/tests/index.test.js +6 -0
  91. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  92. package/v2Containers/Templates/tests/sagas.test.js +436 -200
  93. package/v2Containers/Templates/tests/selector.test.js +32 -0
  94. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  95. package/v2Containers/TemplatesV2/index.js +23 -86
  96. package/v2Containers/Whatsapp/index.js +20 -3
  97. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  98. package/utils/rcsPayloadUtils.js +0 -92
  99. package/utils/templateVarUtils.js +0 -201
  100. package/utils/tests/templateVarUtils.test.js +0 -204
  101. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
  102. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  103. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  104. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
  105. package/v2Components/SmsFallback/constants.js +0 -73
  106. package/v2Components/SmsFallback/index.js +0 -955
  107. package/v2Components/SmsFallback/index.scss +0 -265
  108. package/v2Components/SmsFallback/messages.js +0 -78
  109. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
  110. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  111. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  112. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  113. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
  114. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
  115. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  116. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  117. package/v2Components/TemplatePreview/constants.js +0 -2
  118. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  119. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  120. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  121. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  122. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
  123. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  124. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  125. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  126. package/v2Containers/Rcs/index.js.rej +0 -1336
  127. package/v2Containers/Rcs/index.scss.rej +0 -74
  128. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  129. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
  130. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  131. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  132. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  133. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  134. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  135. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  136. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  137. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  138. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,22 +1,20 @@
1
1
  import { expectSaga } from 'redux-saga-test-plan';
2
- import {
3
- take, call, takeLatest, takeEvery, put,
4
- } from 'redux-saga/effects';
5
2
  import * as matchers from 'redux-saga-test-plan/matchers';
3
+ import { throwError } from 'redux-saga-test-plan/providers';
4
+ import { call, takeLatest, put } from 'redux-saga/effects';
6
5
  import * as api from '../../../services/api';
7
6
  import * as types from '../constants';
8
7
  import * as cdnUtils from '../../../utils/cdnTransformation';
9
-
10
8
  import {
11
9
  getCdnTransformationConfig,
12
10
  getAllTemplates,
13
- getLocalSmsTemplates,
14
11
  watchGetCdnTransformationConfig,
15
12
  watchGetAllTemplates,
16
13
  getSenderDetails,
17
14
  fetchWeCrmAccounts,
18
15
  sendZippedFile,
19
16
  fetchUserList,
17
+ watchGetUserList,
20
18
  deleteRcsTemplate,
21
19
  watchDeleteRcsTemplate,
22
20
  watchForGetTemplateInfoById,
@@ -30,14 +28,30 @@ import {
30
28
  watchGetOrgLevelCampaignSettings,
31
29
  watchSendingFile,
32
30
  watchFetchWeCrmAccounts,
31
+ watchGetSenderDetails,
32
+ v2TemplateSaga,
33
+ v2TemplateSagaWatchGetDefaultBeeTemplates,
34
+ archiveTemplateSaga,
35
+ unarchiveTemplateSaga,
36
+ bulkArchiveTemplatesSaga,
37
+ bulkUnarchiveTemplatesSaga,
38
+ watchArchiveTemplate,
39
+ watchUnarchiveTemplate,
40
+ watchBulkArchive,
41
+ watchBulkUnarchive,
33
42
  } from '../sagas';
34
-
35
- import * as mockData from './mockData';
36
- import { fetchSmsTemplatesFromQuery } from '../utils/smsTemplatesListApi';
37
- import { ZALO } from '../../CreativesContainer/constants';
38
- import { VIET_GUYS, ZALO_TEMPLATE_INFO_REQUEST } from '../../Zalo/constants';
39
- import { throwError } from 'redux-saga-test-plan/providers';
40
43
  import { getTemplateInfoById } from '../../Zalo/saga';
44
+ import { ZALO_TEMPLATE_INFO_REQUEST } from '../../Zalo/constants';
45
+ import * as mockData from './mockData';
46
+
47
+ jest.mock('@capillarytech/cap-ui-library', () => ({
48
+ CapNotification: {
49
+ success: jest.fn(),
50
+ error: jest.fn(),
51
+ warning: jest.fn(),
52
+ info: jest.fn(),
53
+ },
54
+ }));
41
55
 
42
56
  describe('getCdnTransformationConfig saga', () => {
43
57
  it("handle valid response from api", () => {
@@ -136,8 +150,8 @@ describe('templateList saga', () => {
136
150
  ],
137
151
  ]).put({
138
152
  type: types.GET_ALL_TEMPLATES_SUCCESS,
139
- data: mockData.getAllTemplatesListSuccess.response,
140
- weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
153
+ data: mockData.getAllTemplatesListSuccess,
154
+ weCRMTemplate: mockData.getAllTemplatesListSuccess,
141
155
  isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
142
156
  })
143
157
  .run();
@@ -153,8 +167,8 @@ describe('templateList saga', () => {
153
167
  ],
154
168
  ]).put({
155
169
  type: types.GET_ALL_TEMPLATES_SUCCESS,
156
- data: mockData.getAllTemplatesListSuccess.response,
157
- weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
170
+ data: mockData.getAllTemplatesListSuccess,
171
+ weCRMTemplate: mockData.getAllTemplatesListSuccess,
158
172
  isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
159
173
  })
160
174
  .run();
@@ -173,163 +187,6 @@ describe('templateList saga', () => {
173
187
  })
174
188
  .run();
175
189
  });
176
-
177
- it('SMS channel uses fetchSmsTemplatesFromQuery', () => {
178
- const smsAction = {
179
- type: types.GET_ALL_TEMPLATES_REQUEST,
180
- channel: 'Sms',
181
- queryParams: { page: 1, perPage: 25, name: '', sortBy: 'Most Recent' },
182
- intlCopyOf: 'Kopie',
183
- };
184
- const fetched = {
185
- channelTemplates: { templates: [{ name: 'Kopie Foo' }], totalCount: 1 },
186
- weCRMTemplate: undefined,
187
- raw: {},
188
- };
189
- return expectSaga(getAllTemplates, smsAction)
190
- .provide([
191
- [call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), fetched],
192
- ])
193
- .put({
194
- type: types.GET_ALL_TEMPLATES_SUCCESS,
195
- data: fetched.channelTemplates,
196
- weCRMTemplate: fetched.weCRMTemplate,
197
- isReset: true,
198
- })
199
- .run();
200
- });
201
-
202
- it('SMS channel propagates fetchSmsTemplatesFromQuery errors', () => {
203
- const smsAction = {
204
- type: types.GET_ALL_TEMPLATES_REQUEST,
205
- channel: 'SMS',
206
- queryParams: { page: 2, perPage: 25 },
207
- intlCopyOf: '',
208
- };
209
- const error = new Error('sms list fail');
210
- return expectSaga(getAllTemplates, smsAction)
211
- .provide([
212
- [call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), throwError(error)],
213
- ])
214
- .put({
215
- type: types.GET_ALL_TEMPLATES_FAILURE,
216
- error,
217
- })
218
- .run();
219
- });
220
-
221
- it('wechat channel merges mapped and richmedia and applies Most Recent sort', () => {
222
- const wechatApiResponse = {
223
- response: {
224
- mapped: [
225
- { name: 'B', updatedAt: '2024-01-02T00:00:00Z' },
226
- { name: 'A', updatedAt: '2024-01-01T00:00:00Z' },
227
- ],
228
- richmedia: [{ name: 'C', updatedAt: '2024-01-03T00:00:00Z' }],
229
- unMapped: [],
230
- },
231
- };
232
- const action = {
233
- channel: 'wechat',
234
- queryParams: { sortBy: 'Most Recent', page: 1 },
235
- intlCopyOf: '',
236
- };
237
- return expectSaga(getAllTemplates, action)
238
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
239
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
240
- .run();
241
- });
242
-
243
- it('wechat channel applies Alphabetically sort', () => {
244
- const wechatApiResponse = {
245
- response: {
246
- mapped: [{ name: 'Zebra', updatedAt: '2024-01-02T00:00:00Z' }],
247
- richmedia: [{ name: 'Apple', updatedAt: '2024-01-01T00:00:00Z' }],
248
- unMapped: [],
249
- },
250
- };
251
- const action = {
252
- channel: 'wechat',
253
- queryParams: { sortBy: 'Alphabetically', page: 1 },
254
- intlCopyOf: '',
255
- };
256
- return expectSaga(getAllTemplates, action)
257
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
258
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
259
- .run();
260
- });
261
-
262
- it('wechat channel replaces Copy-of prefix when intlCopyOf is set', () => {
263
- const wechatApiResponse = {
264
- response: {
265
- mapped: [{ name: 'Copy of Promo', updatedAt: '2024-01-01T00:00:00Z' }],
266
- richmedia: [],
267
- unMapped: [],
268
- },
269
- };
270
- const action = {
271
- channel: 'wechat',
272
- queryParams: { page: 1 },
273
- intlCopyOf: 'Kopia av ',
274
- };
275
- return expectSaga(getAllTemplates, action)
276
- .provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
277
- .put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
278
- .run();
279
- });
280
- });
281
-
282
- describe('getLocalSmsTemplates saga', () => {
283
- it('invokes onSuccess with fetched data', () => {
284
- const onSuccess = jest.fn();
285
- const action = {
286
- queryParams: { page: 1 },
287
- intlCopyOf: '',
288
- onSuccess,
289
- };
290
- const fetched = { channelTemplates: { templates: [] }, raw: {} };
291
- return expectSaga(getLocalSmsTemplates, action)
292
- .provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
293
- .call(onSuccess, fetched)
294
- .run();
295
- });
296
-
297
- it('invokes onFailure when fetch throws', () => {
298
- const onFailure = jest.fn();
299
- const error = new Error('local list fail');
300
- const action = {
301
- queryParams: { page: 1 },
302
- intlCopyOf: 'Kopie',
303
- onFailure,
304
- };
305
- return expectSaga(getLocalSmsTemplates, action)
306
- .provide([
307
- [call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(error)],
308
- ])
309
- .call(onFailure, error)
310
- .run();
311
- });
312
-
313
- it('does not throw when callbacks are omitted', () => {
314
- const action = { queryParams: { page: 1 }, intlCopyOf: '' };
315
- return expectSaga(getLocalSmsTemplates, action)
316
- .provide([
317
- [call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(new Error('x'))],
318
- ])
319
- .run();
320
- });
321
-
322
- it('does not call onSuccess when it is not a function', () => {
323
- const action = {
324
- queryParams: { page: 1 },
325
- intlCopyOf: '',
326
- onSuccess: 'not-a-fn',
327
- };
328
- const fetched = { channelTemplates: { templates: [] } };
329
- return expectSaga(getLocalSmsTemplates, action)
330
- .provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
331
- .run();
332
- });
333
190
  });
334
191
 
335
192
  describe('watchForTemplates saga', () => {
@@ -523,7 +380,7 @@ describe('getSenderDetails Saga', () => {
523
380
 
524
381
  describe('fetchWeCrmAccounts Saga', () => {
525
382
  const action = { source: 'CRM' };
526
- it('handles successful fetching of WeCRM accounts', () => {
383
+ test.concurrent('handles successful fetching of WeCRM accounts', () => {
527
384
  const fakeResponse = {
528
385
  response: [{ id: 1, name: 'Account One' }]
529
386
  };
@@ -536,11 +393,14 @@ describe('fetchWeCrmAccounts Saga', () => {
536
393
  })
537
394
  );
538
395
  });
539
- it('handles error thrown from api', () => {
396
+ test.concurrent('handles error thrown from api', () => {
540
397
  const error = new Error('Fetch failed');
541
- expectSaga(fetchWeCrmAccounts, action)
398
+ expectSaga(fetchWeCrmAccounts, action.source)
542
399
  .provide([
543
- [call(api.fetchWeCrmAccounts, action.source), throwError(error)],
400
+ [
401
+ call(api.fetchWeCrmAccounts),
402
+ throwError(error)
403
+ ],
544
404
  ])
545
405
  .put({
546
406
  type: types.GET_WECRM_ACCOUNTS_FAILURE,
@@ -582,29 +442,6 @@ describe('sendZippedFile Saga', () => {
582
442
  expect(generator.next().value).toEqual(call(api.sendZippedFile, action.selectedFile));
583
443
  });
584
444
 
585
- it('calls errorHandler with message and returns early when isError is true', () => {
586
- const errorHandler = jest.fn();
587
- const successHandler = jest.fn();
588
- const errorAction = {
589
- selectedFile: 'upload.zip',
590
- errorHandler,
591
- successHandler,
592
- };
593
- const fakeErrorResponse = {
594
- status: { isError: true },
595
- message: 'Upload failed',
596
- };
597
- const generator = sendZippedFile(errorAction);
598
- expect(generator.next().value).toEqual(call(api.sendZippedFile, errorAction.selectedFile));
599
- // Advancing with isError response causes the saga to call errorHandler and yield
600
- const yieldStep = generator.next(fakeErrorResponse);
601
- expect(errorHandler).toHaveBeenCalledWith('Upload failed');
602
- expect(yieldStep.done).toBe(false);
603
- // After resuming from the yield, the return statement completes the generator
604
- expect(generator.next().done).toBe(true);
605
- expect(successHandler).not.toHaveBeenCalled();
606
- });
607
-
608
445
  it('handles error thrown from api', () => {
609
446
  const error = new Error('Fetch failed');
610
447
  expectSaga(sendZippedFile, action)
@@ -909,4 +746,403 @@ describe('watchForGetTemplateInfoById saga', () => {
909
746
  .not.call(getTemplateInfoById, action)
910
747
  .run();
911
748
  });
912
- });
749
+ });
750
+
751
+ describe('watchGetOrgLevelCampaignSettings saga', () => {
752
+ it('should take latest GET_ORG_LEVEL_CAMPAIGN_SETTINGS_REQUEST', () => {
753
+ const generator = watchGetOrgLevelCampaignSettings();
754
+ expect(generator.next().value).toEqual(
755
+ takeLatest(types.GET_ORG_LEVEL_CAMPAIGN_SETTINGS_REQUEST, getOrgLevelCampaignSettings),
756
+ );
757
+ });
758
+ });
759
+
760
+ describe('watchGetUserList saga', () => {
761
+ it('should take latest GET_USER_LIST_REQUEST', () => {
762
+ const generator = watchGetUserList();
763
+ expect(generator.next().value).toEqual(
764
+ takeLatest(types.GET_USER_LIST_REQUEST, fetchUserList),
765
+ );
766
+ });
767
+ });
768
+
769
+ describe('getAllTemplates wechat channel', () => {
770
+ it('should combine mapped and richmedia and sort by Most Recent (desc updatedAt)', () => {
771
+ const wechatChannel = {
772
+ channel: 'wechat',
773
+ queryParams: { page: 1, sortBy: 'Most Recent' },
774
+ };
775
+ const apiResult = {
776
+ response: {
777
+ mapped: [{ name: 'Template B', updatedAt: '2020-01-01' }],
778
+ richmedia: [{ name: 'Template A', updatedAt: '2023-06-01' }],
779
+ unMapped: {},
780
+ },
781
+ };
782
+ const generator = getAllTemplates(wechatChannel);
783
+ generator.next(); // yield call(Api.getAllTemplates, ...)
784
+ const putEffect = generator.next(apiResult).value;
785
+ // Most recent first: Template A (2023) before Template B (2020)
786
+ expect(putEffect).toEqual(
787
+ put({
788
+ type: types.GET_ALL_TEMPLATES_SUCCESS,
789
+ data: {
790
+ templates: [
791
+ { name: 'Template A', updatedAt: '2023-06-01' },
792
+ { name: 'Template B', updatedAt: '2020-01-01' },
793
+ ],
794
+ },
795
+ weCRMTemplate: {},
796
+ isReset: true,
797
+ }),
798
+ );
799
+ expect(generator.next().done).toBe(true);
800
+ });
801
+
802
+ it('should combine mapped and richmedia and sort Alphabetically for wechat channel', () => {
803
+ const wechatChannel = {
804
+ channel: 'wechat',
805
+ queryParams: { page: 1, sortBy: 'Alphabetically' },
806
+ };
807
+ const apiResult = {
808
+ response: {
809
+ mapped: [{ name: 'Z Template' }, { name: 'A Template' }],
810
+ richmedia: [],
811
+ unMapped: {},
812
+ },
813
+ };
814
+ const generator = getAllTemplates(wechatChannel);
815
+ generator.next(); // yield call(Api.getAllTemplates, ...)
816
+ // b.name - a.name on strings gives NaN; sort order is unchanged from original
817
+ expect(generator.next(apiResult).value).toEqual(
818
+ put({
819
+ type: types.GET_ALL_TEMPLATES_SUCCESS,
820
+ data: { templates: [{ name: 'Z Template' }, { name: 'A Template' }] },
821
+ weCRMTemplate: {},
822
+ isReset: true,
823
+ }),
824
+ );
825
+ expect(generator.next().done).toBe(true);
826
+ });
827
+
828
+ it('should replace COPY_OF string in template names when intlCopyOf is set', () => {
829
+ const channelWithCopy = {
830
+ channel: 'email',
831
+ queryParams: { page: 1 },
832
+ intlCopyOf: 'Copia de',
833
+ };
834
+ const apiResult = {
835
+ response: {
836
+ templates: [{ name: 'Copy of Template 1' }, { name: 'Regular Template' }],
837
+ unMapped: {},
838
+ },
839
+ };
840
+ const generator = getAllTemplates(channelWithCopy);
841
+ generator.next();
842
+ const putEffect = generator.next(apiResult).value;
843
+ expect(putEffect).toEqual(
844
+ put({
845
+ type: types.GET_ALL_TEMPLATES_SUCCESS,
846
+ data: {
847
+ templates: [
848
+ { name: 'Copia de Template 1' },
849
+ { name: 'Regular Template' },
850
+ ],
851
+ unMapped: {},
852
+ },
853
+ weCRMTemplate: {},
854
+ isReset: true,
855
+ }),
856
+ );
857
+ expect(generator.next().done).toBe(true);
858
+ });
859
+ });
860
+ describe('archiveTemplateSaga', () => {
861
+ it('should dispatch ARCHIVE_TEMPLATE_SUCCESS, refresh listing, then show success notification', () => {
862
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
863
+ const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
864
+ const gen = archiveTemplateSaga(action);
865
+ // call archiveTemplate
866
+ gen.next();
867
+ // put ARCHIVE_TEMPLATE_SUCCESS with id
868
+ expect(gen.next().value).toEqual(put({ type: types.ARCHIVE_TEMPLATE_SUCCESS, id: 'id1' }));
869
+ // yield select archiveFilter
870
+ const selectStep = gen.next();
871
+ expect(selectStep.value).toBeDefined();
872
+ // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
873
+ const callStep = gen.next('active');
874
+ expect(callStep.value).toHaveProperty('@@redux-saga/IO');
875
+ // CapNotification.success runs (not yielded) then generator is done
876
+ expect(gen.next().done).toBe(true);
877
+ expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({ message: 'Template archived successfully' }));
878
+ });
879
+
880
+ it('should dispatch ARCHIVE_TEMPLATE_FAILURE and call CapNotification.error on error', () => {
881
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
882
+ const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
883
+ const gen = archiveTemplateSaga(action);
884
+ gen.next(); // call archiveTemplate
885
+ const error = new Error('archive failed');
886
+ expect(gen.throw(error).value).toEqual(put({ type: types.ARCHIVE_TEMPLATE_FAILURE, error }));
887
+ // advance past put — CapNotification.error executes and generator finishes
888
+ const done = gen.next();
889
+ expect(done.done).toBe(true);
890
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to archive template' });
891
+ });
892
+ });
893
+
894
+ describe('unarchiveTemplateSaga', () => {
895
+ it('should dispatch UNARCHIVE_TEMPLATE_SUCCESS, refresh listing, then show success notification', () => {
896
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
897
+ const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
898
+ const gen = unarchiveTemplateSaga(action);
899
+ // call unarchiveTemplate
900
+ gen.next();
901
+ // put UNARCHIVE_TEMPLATE_SUCCESS with id
902
+ expect(gen.next().value).toEqual(put({ type: types.UNARCHIVE_TEMPLATE_SUCCESS, id: 'id1' }));
903
+ // yield select archiveFilter
904
+ const selectStep = gen.next();
905
+ expect(selectStep.value).toBeDefined();
906
+ // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
907
+ const callStep = gen.next('archived');
908
+ expect(callStep.value).toHaveProperty('@@redux-saga/IO');
909
+ // CapNotification.success runs (not yielded) then generator is done
910
+ expect(gen.next().done).toBe(true);
911
+ expect(CapNotification.success).toHaveBeenCalledWith(expect.objectContaining({ message: 'Template unarchived successfully' }));
912
+ });
913
+
914
+ it('should dispatch UNARCHIVE_TEMPLATE_FAILURE and call CapNotification.error on error', () => {
915
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
916
+ const action = { channel: 'EMAIL', id: 'id1', templateName: 'Test' };
917
+ const gen = unarchiveTemplateSaga(action);
918
+ gen.next(); // call unarchiveTemplate
919
+ const error = new Error('unarchive failed');
920
+ expect(gen.throw(error).value).toEqual(put({ type: types.UNARCHIVE_TEMPLATE_FAILURE, error }));
921
+ // advance past put — CapNotification.error executes and generator finishes
922
+ const done = gen.next();
923
+ expect(done.done).toBe(true);
924
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to unarchive template' });
925
+ });
926
+ });
927
+
928
+ describe('bulkArchiveTemplatesSaga', () => {
929
+ it('should dispatch BULK_ARCHIVE_SUCCESS, refresh listing, then show success notification', () => {
930
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
931
+ const action = { channel: 'EMAIL', ids: ['id1', 'id2'] };
932
+ const gen = bulkArchiveTemplatesSaga(action);
933
+ // call bulkArchiveTemplates
934
+ gen.next();
935
+ // put BULK_ARCHIVE_SUCCESS
936
+ expect(gen.next({ response: { modifiedCount: 2 } }).value).toEqual(put({ type: types.BULK_ARCHIVE_SUCCESS }));
937
+ // yield select archiveFilter
938
+ const selectStep = gen.next();
939
+ expect(selectStep.value).toBeDefined();
940
+ // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
941
+ const callStep = gen.next('active');
942
+ expect(callStep.value).toHaveProperty('@@redux-saga/IO');
943
+ // CapNotification.success runs (not yielded) then generator is done
944
+ expect(gen.next().done).toBe(true);
945
+ expect(CapNotification.success).toHaveBeenCalledWith({ message: '2 templates archived successfully' });
946
+ });
947
+
948
+ it('should dispatch BULK_ARCHIVE_FAILURE and call CapNotification.error on error', () => {
949
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
950
+ const action = { channel: 'EMAIL', ids: ['id1'] };
951
+ const gen = bulkArchiveTemplatesSaga(action);
952
+ gen.next(); // call bulkArchiveTemplates
953
+ const error = new Error('bulk archive failed');
954
+ expect(gen.throw(error).value).toEqual(put({ type: types.BULK_ARCHIVE_FAILURE, error }));
955
+ // advance past put — CapNotification.error executes and generator finishes
956
+ const done = gen.next();
957
+ expect(done.done).toBe(true);
958
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to archive templates' });
959
+ });
960
+ });
961
+
962
+ describe('bulkUnarchiveTemplatesSaga', () => {
963
+ it('should dispatch BULK_UNARCHIVE_SUCCESS, refresh listing, then show success notification', () => {
964
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
965
+ const action = { channel: 'EMAIL', ids: ['id1', 'id2'] };
966
+ const gen = bulkUnarchiveTemplatesSaga(action);
967
+ // call bulkUnarchiveTemplates
968
+ gen.next();
969
+ // put BULK_UNARCHIVE_SUCCESS
970
+ expect(gen.next({ response: { modifiedCount: 2 } }).value).toEqual(put({ type: types.BULK_UNARCHIVE_SUCCESS }));
971
+ // yield select archiveFilter
972
+ const selectStep = gen.next();
973
+ expect(selectStep.value).toBeDefined();
974
+ // call getAllTemplates (listing refresh) — notification fires AFTER this resolves
975
+ const callStep = gen.next('archived');
976
+ expect(callStep.value).toHaveProperty('@@redux-saga/IO');
977
+ // CapNotification.success runs (not yielded) then generator is done
978
+ expect(gen.next().done).toBe(true);
979
+ expect(CapNotification.success).toHaveBeenCalledWith({ message: '2 templates unarchived successfully' });
980
+ });
981
+
982
+ it('should dispatch BULK_UNARCHIVE_FAILURE and call CapNotification.error on error', () => {
983
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
984
+ const action = { channel: 'EMAIL', ids: ['id1'] };
985
+ const gen = bulkUnarchiveTemplatesSaga(action);
986
+ gen.next(); // call bulkUnarchiveTemplates
987
+ const error = new Error('bulk unarchive failed');
988
+ expect(gen.throw(error).value).toEqual(put({ type: types.BULK_UNARCHIVE_FAILURE, error }));
989
+ // advance past put — CapNotification.error executes and generator finishes
990
+ const done = gen.next();
991
+ expect(done.done).toBe(true);
992
+ expect(CapNotification.error).toHaveBeenCalledWith({ message: 'Failed to unarchive templates' });
993
+ });
994
+ });
995
+
996
+ describe('archive watcher sagas', () => {
997
+ it('watchArchiveTemplate should take latest ARCHIVE_TEMPLATE_REQUEST', () => {
998
+ const gen = watchArchiveTemplate();
999
+ expect(gen.next().value).toEqual(takeLatest(types.ARCHIVE_TEMPLATE_REQUEST, archiveTemplateSaga));
1000
+ });
1001
+
1002
+ it('watchUnarchiveTemplate should take latest UNARCHIVE_TEMPLATE_REQUEST', () => {
1003
+ const gen = watchUnarchiveTemplate();
1004
+ expect(gen.next().value).toEqual(takeLatest(types.UNARCHIVE_TEMPLATE_REQUEST, unarchiveTemplateSaga));
1005
+ });
1006
+
1007
+ it('watchBulkArchive should take latest BULK_ARCHIVE_REQUEST', () => {
1008
+ const gen = watchBulkArchive();
1009
+ expect(gen.next().value).toEqual(takeLatest(types.BULK_ARCHIVE_REQUEST, bulkArchiveTemplatesSaga));
1010
+ });
1011
+
1012
+ it('watchBulkUnarchive should take latest BULK_UNARCHIVE_REQUEST', () => {
1013
+ const gen = watchBulkUnarchive();
1014
+ expect(gen.next().value).toEqual(takeLatest(types.BULK_UNARCHIVE_REQUEST, bulkUnarchiveTemplatesSaga));
1015
+ });
1016
+ });
1017
+
1018
+ describe('fetchWeCrmAccounts failure', () => {
1019
+ it('should dispatch GET_WECRM_ACCOUNTS_FAILURE on error', () => {
1020
+ const action = { source: 'test-source' };
1021
+ const gen = fetchWeCrmAccounts(action);
1022
+ gen.next(); // call fetchWeCrmAccounts
1023
+ const error = new Error('fetch failed');
1024
+ expect(gen.throw(error).value).toEqual(
1025
+ put({ type: types.GET_WECRM_ACCOUNTS_FAILURE, data: error })
1026
+ );
1027
+ });
1028
+ });
1029
+
1030
+ describe('sendZippedFile saga', () => {
1031
+ it('should call errorHandler and return when result.status.isError is true', () => {
1032
+ const mockErrorHandler = jest.fn();
1033
+ const mockSuccessHandler = jest.fn();
1034
+ const action = { selectedFile: 'file.zip', errorHandler: mockErrorHandler, successHandler: mockSuccessHandler };
1035
+ const gen = sendZippedFile(action);
1036
+ gen.next(); // call Api.sendZippedFile
1037
+ const result = { status: { isError: true }, message: 'upload error' };
1038
+ // advance past yield call — enters isError branch
1039
+ const step = gen.next(result);
1040
+ // errorMessage = result.message; yield errorHandler(errorMessage)
1041
+ // errorHandler returns undefined so yielded value is undefined
1042
+ expect(step.value).toBeUndefined(); // yield errorHandler('upload error')
1043
+ const done = gen.next(); // return
1044
+ expect(done.done).toBe(true);
1045
+ });
1046
+
1047
+ it('should call successHandler after successful upload', () => {
1048
+ const mockErrorHandler = jest.fn();
1049
+ const mockSuccessHandler = jest.fn();
1050
+ const action = { selectedFile: 'file.zip', errorHandler: mockErrorHandler, successHandler: mockSuccessHandler };
1051
+ const gen = sendZippedFile(action);
1052
+ gen.next(); // call Api.sendZippedFile
1053
+ const result = {
1054
+ status: { isError: false },
1055
+ response: { metaEntity: { htmlContent: encodeURIComponent('<html></html>') } },
1056
+ };
1057
+ gen.next(result); // advance past isError check, put SEND_ZIPPED_FILE_SUCCESS
1058
+ gen.next(); // advance to successHandler yield
1059
+ const done = gen.next();
1060
+ expect(done.done).toBe(true);
1061
+ });
1062
+
1063
+ it('should call errorHandler and put SEND_ZIPPED_FILE_FAILURE on exception', () => {
1064
+ const mockErrorHandler = jest.fn();
1065
+ const mockSuccessHandler = jest.fn();
1066
+ const action = { selectedFile: 'file.zip', errorHandler: mockErrorHandler, successHandler: mockSuccessHandler };
1067
+ const gen = sendZippedFile(action);
1068
+ gen.next(); // call Api.sendZippedFile
1069
+ const error = new Error('network error');
1070
+ // throw error — enters catch: yield errorHandler()
1071
+ const step1 = gen.throw(error);
1072
+ // errorHandler returns undefined so yielded value is undefined
1073
+ expect(step1.value).toBeUndefined(); // yield errorHandler()
1074
+ // yield put SEND_ZIPPED_FILE_FAILURE
1075
+ const step2 = gen.next();
1076
+ expect(step2.value).toEqual(
1077
+ put({ type: types.SEND_ZIPPED_FILE_FAILURE, data: '' })
1078
+ );
1079
+ });
1080
+ });
1081
+
1082
+ describe('archive sagas - CapNotification.error coverage', () => {
1083
+ it('archiveTemplateSaga should call CapNotification.error on failure', () => {
1084
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1085
+ const gen = archiveTemplateSaga({ channel: 'EMAIL', id: 'id1', templateName: 'Test' });
1086
+ gen.next(); // call archiveTemplate
1087
+ const error = new Error('archive failed');
1088
+ gen.throw(error); // put ARCHIVE_TEMPLATE_FAILURE
1089
+ expect(CapNotification.error).toBeDefined();
1090
+ });
1091
+
1092
+ it('unarchiveTemplateSaga should call CapNotification.error on failure', () => {
1093
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1094
+ const gen = unarchiveTemplateSaga({ channel: 'EMAIL', id: 'id1', templateName: 'Test' });
1095
+ gen.next(); // call unarchiveTemplate
1096
+ const error = new Error('unarchive failed');
1097
+ gen.throw(error); // put UNARCHIVE_TEMPLATE_FAILURE
1098
+ expect(CapNotification.error).toBeDefined();
1099
+ });
1100
+
1101
+ it('bulkArchiveTemplatesSaga should call CapNotification.error on failure', () => {
1102
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1103
+ const gen = bulkArchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1'] });
1104
+ gen.next(); // call bulkArchiveTemplates
1105
+ const error = new Error('bulk archive failed');
1106
+ gen.throw(error); // put BULK_ARCHIVE_FAILURE
1107
+ expect(CapNotification.error).toBeDefined();
1108
+ });
1109
+
1110
+ it('bulkUnarchiveTemplatesSaga should call CapNotification.error on failure', () => {
1111
+ const { CapNotification } = require('@capillarytech/cap-ui-library');
1112
+ const gen = bulkUnarchiveTemplatesSaga({ channel: 'EMAIL', ids: ['id1'] });
1113
+ gen.next(); // call bulkUnarchiveTemplates
1114
+ const error = new Error('bulk unarchive failed');
1115
+ gen.throw(error); // put BULK_UNARCHIVE_FAILURE
1116
+ expect(CapNotification.error).toBeDefined();
1117
+ });
1118
+ });
1119
+
1120
+ describe('watchGetSenderDetails', () => {
1121
+ it('should take latest GET_SENDER_DETAILS_REQUEST', () => {
1122
+ const gen = watchGetSenderDetails();
1123
+ expect(gen.next().value).toEqual(takeLatest(types.GET_SENDER_DETAILS_REQUEST, getSenderDetails));
1124
+ });
1125
+ });
1126
+
1127
+ describe('v2TemplateSaga', () => {
1128
+ it('should yield all watchers including archive watchers', () => {
1129
+ const gen = v2TemplateSaga();
1130
+ const step = gen.next();
1131
+ // all() returns an IO object with an ALL key containing the array of effects
1132
+ expect(step.value).toHaveProperty('@@redux-saga/IO', true);
1133
+ expect(step.value).toHaveProperty('ALL');
1134
+ expect(step.value.ALL).toHaveLength(14);
1135
+ expect(step.done).toBe(false);
1136
+ });
1137
+ });
1138
+
1139
+ describe('v2TemplateSagaWatchGetDefaultBeeTemplates', () => {
1140
+ it('should yield all with watchSendingFile and watchGetDefaultBeeTemplates', () => {
1141
+ const gen = v2TemplateSagaWatchGetDefaultBeeTemplates();
1142
+ const step = gen.next();
1143
+ expect(step.value).toHaveProperty('@@redux-saga/IO', true);
1144
+ expect(step.value).toHaveProperty('ALL');
1145
+ expect(step.value.ALL).toHaveLength(2);
1146
+ expect(step.done).toBe(false);
1147
+ });
1148
+ });