@capillarytech/creatives-library 8.0.34-alpha.1 → 8.0.36

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 (70) hide show
  1. package/app.js +0 -1
  2. package/containers/App/test/saga.test.js +11 -0
  3. package/containers/Assets/Gallery/sagas.js +2 -1
  4. package/containers/Assets/Gallery/tests/saga.test.js +161 -0
  5. package/containers/Cap/tests/saga.test.js +8 -1
  6. package/containers/Dashboard/test/saga.test.js +9 -0
  7. package/containers/Ebill/test/saga.test.js +11 -0
  8. package/containers/Email/sagas.js +1 -1
  9. package/containers/Email/test/saga.test.js +278 -0
  10. package/containers/Line/Create/tests/saga.test.js +209 -0
  11. package/containers/Line/Edit/sagas.js +4 -4
  12. package/containers/Line/Edit/test/saga.test.js +164 -0
  13. package/containers/MobilePush/Create/index.js +1 -1
  14. package/containers/MobilePush/Create/test/saga.test.js +19 -0
  15. package/containers/MobilePush/Edit/index.js +1 -1
  16. package/containers/MobilePush/Edit/tests/saga.test.js +255 -0
  17. package/containers/Sms/Create/test/saga.test.js +12 -0
  18. package/containers/Sms/Edit/test/saga.test.js +13 -0
  19. package/containers/Templates/test/saga.test.js +244 -0
  20. package/containers/WeChat/MapTemplates/test/saga.test.js +159 -0
  21. package/containers/WeChat/RichmediaTemplates/Create/test/saga.test.js +13 -0
  22. package/containers/WeChat/RichmediaTemplates/Edit/test/saga.test.js +12 -0
  23. package/hoc/test/WithReactRouterV3Compatibility.test.js +135 -0
  24. package/hoc/withReactRouterV3Compatibility.js +4 -4
  25. package/package.json +3 -3
  26. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +4 -2
  27. package/tests/integration/TemplateCreation/api-response.js +10 -1
  28. package/tests/integration/TemplateCreation/msw-handler.js +3 -0
  29. package/utils/authWrapper.js +0 -6
  30. package/utils/tests/authWrapper.test.js +142 -0
  31. package/utils/tests/checkStore.test.js +72 -0
  32. package/utils/tests/customAuth.test.js +30 -0
  33. package/v2Components/NavigationBar/tests/saga.test.js +35 -0
  34. package/v2Containers/Assets/Gallery/sagas.js +1 -1
  35. package/v2Containers/Assets/Gallery/tests/saga.test.js +41 -0
  36. package/v2Containers/BeeEditor/test/saga.test.js +13 -0
  37. package/v2Containers/CallTask/test/saga.test.js +13 -0
  38. package/v2Containers/Cap/sagas.js +3 -3
  39. package/v2Containers/Cap/tests/Cap.test.js +2 -18
  40. package/v2Containers/Cap/tests/saga.test.js +73 -1
  41. package/v2Containers/CapFacebookPreview/tests/saga.test.js +13 -0
  42. package/v2Containers/Ebill/test/saga.test.js +13 -0
  43. package/v2Containers/Email/sagas.js +3 -2
  44. package/v2Containers/Email/tests/sagas.test.js +216 -0
  45. package/v2Containers/FTP/test/saga.test.js +12 -0
  46. package/v2Containers/Facebook/test/saga.test.js +171 -0
  47. package/v2Containers/InApp/tests/sagas.test.js +13 -0
  48. package/v2Containers/LanguageProvider/sagas.js +1 -1
  49. package/v2Containers/LanguageProvider/tests/saga.test.js +40 -0
  50. package/v2Containers/Line/Container/test/saga.test.js +228 -0
  51. package/v2Containers/MobilePush/Create/index.js +1 -1
  52. package/v2Containers/MobilePush/Create/test/saga.test.js +19 -0
  53. package/v2Containers/MobilePush/Edit/index.js +1 -1
  54. package/v2Containers/MobilePush/Edit/test/saga.test.js +255 -0
  55. package/v2Containers/Rcs/tests/saga.test.js +13 -0
  56. package/v2Containers/Sms/Create/test/saga.test.js +8 -0
  57. package/v2Containers/Sms/Edit/test/saga.test.js +12 -0
  58. package/v2Containers/SmsTrai/Create/tests/saga.test.js +13 -0
  59. package/v2Containers/TagList/index.js +20 -8
  60. package/v2Containers/TagList/tests/TagList.test.js +11 -4
  61. package/v2Containers/TagList/tests/mockdata.js +4 -20
  62. package/v2Containers/Templates/sagas.js +7 -7
  63. package/v2Containers/Templates/tests/sagas.test.js +138 -0
  64. package/v2Containers/TemplatesV2/index.js +25 -51
  65. package/v2Containers/Viber/tests/saga.test.js +187 -0
  66. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +160 -0
  67. package/v2Containers/WeChat/RichmediaTemplates/Create/test/saga.test.js +12 -0
  68. package/v2Containers/WeChat/RichmediaTemplates/Edit/test/saga.test.js +13 -0
  69. package/v2Containers/Whatsapp/tests/saga.test.js +9 -1
  70. package/v2Containers/Zalo/tests/saga.test.js +8 -1
@@ -110,25 +110,8 @@ export const TagListData = {
110
110
  export const eventContextTags = [
111
111
  {
112
112
  "profileId": "BEHAVIOURAL_EVENT_PROFILE",
113
- "method": "getEventData",
114
- "field": "butterfly",
115
- "params": null,
116
- "returnType": {
117
- "isList": false,
118
- "typeInfo": {
119
- "type": "DOUBLE",
120
- "typeId": null
121
- },
122
- "list": false
123
- },
124
- "factId": "getEventData_butterfly",
125
- "tagName": "(butterfly)",
126
- "isTaggable": true,
127
- "uniqueId": "yW_DH7vjKk__BEHAVIOURAL__monsoon",
128
- "isDynamicFact": true
129
- },
130
- {
131
- "profileId": "BEHAVIOURAL_EVENT_PROFILE",
113
+ // "profileName": "Behavioural event",
114
+ // "label": 'raindrops',
132
115
  "method": "getEventData",
133
116
  "field": "raindrops",
134
117
  "params": null,
@@ -149,6 +132,7 @@ export const eventContextTags = [
149
132
  {
150
133
  "factId": "5LFP9e",
151
134
  "profileId": "CUSTOMER_PROFILE",
135
+ "profileName": "Current Customer",
152
136
  "params": [],
153
137
  "returnType": {
154
138
  "isList": false,
@@ -159,7 +143,7 @@ export const eventContextTags = [
159
143
  "list": false
160
144
  },
161
145
  "tagName": "entryTrigger.lifetimePurchases",
162
- label: 'lifetimePurchases',
146
+ "label": 'lifetimePurchases',
163
147
  "isTaggable": true,
164
148
  "uniqueId": "yW_DH7vjKk__BEHAVIOURAL__monsoon",
165
149
  "isDynamicFact": false
@@ -185,43 +185,43 @@ export function* watchGetAllTemplates() {
185
185
 
186
186
  }
187
187
 
188
- function* watchDeleteTemplate() {
188
+ export function* watchDeleteTemplate() {
189
189
  yield takeLatest(types.DELETE_TEMPLATE_REQUEST, deleteTemplate);
190
190
 
191
191
 
192
192
  }
193
193
 
194
- function* watchGetUserList() {
194
+ export function* watchGetUserList() {
195
195
  yield takeLatest(types.GET_USER_LIST_REQUEST, fetchUserList);
196
196
 
197
197
 
198
198
  }
199
199
 
200
- function* watchFetchWeCrmAccounts() {
200
+ export function* watchFetchWeCrmAccounts() {
201
201
  yield takeLatest(types.GET_WECRM_ACCOUNTS_REQUEST, fetchWeCrmAccounts);
202
202
 
203
203
 
204
204
  }
205
205
 
206
- function* watchSendingFile() {
206
+ export function* watchSendingFile() {
207
207
  yield takeLatest(types.SEND_ZIPPED_FILE_REQUEST, sendZippedFile);
208
208
 
209
209
 
210
210
  }
211
211
 
212
- function* watchGetDefaultBeeTemplates() {
212
+ export function* watchGetDefaultBeeTemplates() {
213
213
  yield takeLatest(types.GET_DEAFULT_BEE_TEMPLATES_REQUEST, getDefaultBeeTemplates);
214
214
 
215
215
 
216
216
  }
217
217
 
218
- function* watchGetTemplateDetails() {
218
+ export function* watchGetTemplateDetails() {
219
219
  yield takeLatest(types.GET_TEMPLATE_DETAILS_REQUEST, getTemplateDetails);
220
220
 
221
221
 
222
222
  }
223
223
 
224
- function* watchGetSenderDetails() {
224
+ export function* watchGetSenderDetails() {
225
225
  yield takeLatest(types.GET_SENDER_DETAILS_REQUEST, getSenderDetails);
226
226
 
227
227
 
@@ -11,11 +11,15 @@ import {
11
11
  watchGetCdnTransformationConfig,
12
12
  watchGetAllTemplates,
13
13
  getSenderDetails,
14
+ fetchWeCrmAccounts,
15
+ sendZippedFile,
16
+ fetchUserList
14
17
  } from '../sagas';
15
18
 
16
19
  import * as mockData from './mockData';
17
20
  import { ZALO } from '../../CreativesContainer/constants';
18
21
  import { VIET_GUYS } from '../../Zalo/constants';
22
+ import { throwError } from 'redux-saga-test-plan/providers';
19
23
 
20
24
  describe('getCdnTransformationConfig saga', () => {
21
25
  it("handle valid response from api", () => {
@@ -187,3 +191,137 @@ describe('getSenderDetails Saga', () => {
187
191
  );
188
192
  });
189
193
  });
194
+
195
+ describe('fetchWeCrmAccounts Saga', () => {
196
+ const action = { source: 'CRM' };
197
+
198
+ it('handles successful fetching of WeCRM accounts', () => {
199
+ const fakeResponse = {
200
+ response: [{ id: 1, name: 'Account One' }]
201
+ };
202
+ const generator = fetchWeCrmAccounts(action);
203
+ expect(generator.next().value).toEqual(call(api.fetchWeCrmAccounts, action.source));
204
+ expect(generator.next(fakeResponse).value).toEqual(
205
+ put({
206
+ type: types.GET_WECRM_ACCOUNTS_SUCCESS,
207
+ data: fakeResponse.response
208
+ })
209
+ );
210
+ });
211
+
212
+ it('handles error thrown from api', () => {
213
+ const error = new Error('Fetch failed');
214
+ expectSaga(fetchWeCrmAccounts, action.source)
215
+ .provide([
216
+ [
217
+ call(api.fetchWeCrmAccounts),
218
+ throwError(error)
219
+ ],
220
+ ])
221
+ .put({
222
+ type: types.GET_WECRM_ACCOUNTS_FAILURE,
223
+ data: error
224
+ })
225
+ .run();
226
+ });
227
+ });
228
+
229
+
230
+ describe('sendZippedFile Saga', () => {
231
+ const action = {
232
+ selectedFile: 'dummyFile.zip',
233
+ errorHandler: jest.fn(),
234
+ successHandler: jest.fn()
235
+ };
236
+
237
+ it('handles successful sending of zipped file', () => {
238
+ const fakeResponse = {
239
+ status: { isError: false },
240
+ response: { metaEntity: { htmlContent: encodeURIComponent('<html></html>') } }
241
+ };
242
+ const generator = sendZippedFile(action);
243
+ expect(generator.next().value).toEqual(call(api.sendZippedFile, action.selectedFile));
244
+ expect(generator.next(fakeResponse).value).toEqual(
245
+ put({
246
+ type: types.SEND_ZIPPED_FILE_SUCCESS,
247
+ selectedTemplate: decodeURIComponent(fakeResponse.response.metaEntity.htmlContent)
248
+ })
249
+ );
250
+ });
251
+
252
+ it('handles scenario when API result indicates an error', () => {
253
+ const fakeErrorResponse = {
254
+ status: { isError: true },
255
+ message: 'Error processing file'
256
+ };
257
+ const generator = sendZippedFile(action);
258
+ expect(generator.next().value).toEqual(call(api.sendZippedFile, action.selectedFile));
259
+ });
260
+
261
+ it('handles error thrown from api', () => {
262
+ const error = new Error('Fetch failed');
263
+ expectSaga(sendZippedFile, action)
264
+ .provide([
265
+ [
266
+ call(api.sendZippedFile),
267
+ throwError(error)
268
+ ],
269
+ ])
270
+ .put({
271
+ type: types.SEND_ZIPPED_FILE_FAILURE,
272
+ data: error
273
+ })
274
+ .run();
275
+ });
276
+ });
277
+
278
+ describe('fetchUserList Saga', () => {
279
+
280
+ it('handles successful fetching of user list', () => {
281
+ const fakeResponse = {
282
+ data: {
283
+ result: [
284
+ { id: 1, name: 'User One' },
285
+ { id: 2, name: 'User Two' }
286
+ ]
287
+ }
288
+ };
289
+ const generator = fetchUserList();
290
+ expect(generator.next().value).toEqual(call(api.getUserList));
291
+ expect(generator.next(fakeResponse).value).toEqual(
292
+ put({
293
+ type: types.GET_USER_LIST_SUCCESS,
294
+ data: fakeResponse.data.result
295
+ })
296
+ );
297
+ expect(generator.next().done).toBe(true);
298
+ });
299
+
300
+ it('handles error thrown from API when fetching user list', () => {
301
+ const error = new Error('Fetch failed');
302
+ const generator = fetchUserList();
303
+ expect(generator.next().value).toEqual(call(api.getUserList));
304
+ expect(generator.throw(error).value).toEqual(
305
+ put({
306
+ type: types.GET_USER_LIST_FAILURE,
307
+ data: error
308
+ })
309
+ );
310
+ expect(generator.next().done).toBe(true);
311
+ });
312
+ it('handles error thrown from api', () => {
313
+ const error = new Error('Fetch failed');
314
+ expectSaga(fetchUserList)
315
+ .provide([
316
+ [
317
+ call(api.getUserList),
318
+ throwError(error)
319
+ ],
320
+ ])
321
+ .put({
322
+ type: types.GET_USER_LIST_FAILURE,
323
+ data: error
324
+ })
325
+ .run();
326
+ });
327
+ });
@@ -6,14 +6,11 @@
6
6
 
7
7
  import PropTypes from 'prop-types';
8
8
  import React from 'react';
9
- import { all } from 'redux-saga/effects';
10
9
  import { connect } from 'react-redux';
11
10
  import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
12
11
  import { createStructuredSelector } from 'reselect';
13
12
  import { bindActionCreators, compose } from 'redux';
14
- import {
15
- CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon, CapTooltip,
16
- } from '@capillarytech/cap-ui-library';
13
+ import { CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon, CapTooltip } from '@capillarytech/cap-ui-library';
17
14
  import { find, get } from 'lodash';
18
15
  import isEmpty from 'lodash/isEmpty';
19
16
  import Helmet from 'react-helmet';
@@ -33,9 +30,7 @@ import FTP from '../FTP';
33
30
  import Gallery from '../Assets/Gallery';
34
31
  import withStyles from '../../hoc/withStyles';
35
32
  import styles, { CapTabStyle } from './TemplatesV2.style';
36
- import {
37
- CREATIVES_UI_VIEW, LOYALTY, WHATSAPP, RCS, LINE, EMAIL, ASSETS, JP_LOCALE_HIDE_FEATURE, ZALO, INAPP,
38
- } from '../App/constants';
33
+ import { CREATIVES_UI_VIEW, LOYALTY, WHATSAPP, RCS, LINE, EMAIL, ASSETS, JP_LOCALE_HIDE_FEATURE, ZALO, INAPP } from '../App/constants';
39
34
  import AccessForbidden from '../../v2Components/AccessForbidden';
40
35
  import { getObjFromQueryParams } from '../../utils/v2common';
41
36
  import { makeSelectAuthenticated, selectCurrentOrgDetails } from "../Cap/selectors";
@@ -124,15 +119,13 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
124
119
  // eslint-disable-next-line no-param-reassign
125
120
  pane.disabled = true;
126
121
  if (pane.key === 'facebook' && showDisabledFBInfo) {
127
- pane.tab = (
128
- <CapTooltip
122
+ pane.tab = (<CapTooltip
129
123
  title={
130
124
  intl.formatMessage(messages.facebookDisableinfo)
131
125
  }
132
126
  >
133
127
  {intl.formatMessage(messages.facebook)}
134
- </CapTooltip>
135
- );
128
+ </CapTooltip>);
136
129
  }
137
130
  }
138
131
  return pane;
@@ -185,15 +178,13 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
185
178
  this.setState({selectedChannel: nextProps.channel, panes });
186
179
  }
187
180
  }
188
-
189
- getTemplateDataForGrid = ({
190
- templates, handlers, filterContent, channel, isLoading, loadingTip,
191
- }) => {
181
+ getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
192
182
  const currentChannel = channel.toUpperCase();
193
183
  const cardDataList = templates.map((template) => {
194
- const templateData = {
184
+ const templateData =
185
+ {
195
186
  key: `${currentChannel}-card-${template.name}`,
196
- title: <span title={template.name}>{template.name}</span>,
187
+ title: <span title={template.name} >{template.name}</span>,
197
188
  extra: [<CapIcon type="eye" onClick={() => { if (this.props.isFullMode) { handlers.handlePreviewClick(template); } else { this.props.handlePeviewTemplate(template); } }} />],
198
189
  hoverOption: <CapButton onClick={(e) => handlers.handleEditClick(e, template._id)}>{this.props.intl.formatMessage(this.props.isFullMode ? messages.edit : messages.select)}</CapButton>,
199
190
  };
@@ -204,8 +195,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
204
195
  }
205
196
  return templateData;
206
197
  });
207
- return (
208
- <div>
198
+ return (<div>
209
199
  {filterContent}
210
200
  <CapSpin spinning={isLoading} tip={loadingTip}>
211
201
  <div className="pagination-container">
@@ -213,12 +203,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
213
203
  </div>
214
204
  </CapSpin>
215
205
 
216
- </div>
217
- );
206
+ </div>);
218
207
  }
219
-
220
- getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode} />
221
-
208
+ getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode}/>
222
209
  getCallTaskComponent = () => (
223
210
  <CallTask
224
211
  onCreateNew={this.props.createNew}
@@ -228,13 +215,11 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
228
215
  );
229
216
 
230
217
  getFacebookComponent = () => {
231
- const {
232
- messageDetails, cap, onFacebookSubmit,
218
+ const { messageDetails, cap, onFacebookSubmit,
233
219
  messageStrategy,
234
220
  showDisabledFBInfo,
235
221
  orgUnitId,
236
- onSelectTemplate,
237
- } = this.props;
222
+ onSelectTemplate } = this.props;
238
223
  return (
239
224
  <Facebook
240
225
  {...this.props}
@@ -248,15 +233,12 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
248
233
  />
249
234
  );
250
235
  }
251
-
252
236
  getViberComponent =() => (
253
237
  <Viber
254
238
  {...this.props}
255
239
  isCreateFlow
256
- >
257
- </Viber>
240
+ ></Viber>
258
241
  )
259
-
260
242
  getLineComponent = () => (
261
243
  <Line
262
244
  {...this.props}
@@ -293,8 +275,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
293
275
  case 'assets':
294
276
  return this.getGalleryComponent(location);
295
277
  default:
296
- return (
297
- <Templates
278
+ return (<Templates
298
279
  key={channel}
299
280
  location={location}
300
281
  route={{name: channel}}
@@ -307,8 +288,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
307
288
  messageStrategy={this.props.messageStrategy}
308
289
  smsRegister={smsRegister}
309
290
  hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
310
- />
311
- );
291
+ />);
312
292
  }
313
293
  }
314
294
 
@@ -330,17 +310,13 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
330
310
  const panes = this.setChannelContent(selectedChannel, this.state.panes);
331
311
  this.setState({panes, selectedChannel}, () => { if (!this.props.isFullMode) this.props.onChannelChange(selectedChannel); });
332
312
  }
333
-
334
313
  selectTemplate = (id) => find(this.props.TemplatesList, {_id: id})
335
314
 
336
315
  isLoading() {
337
316
  return this.props.Templates.loadingTemplates;
338
317
  }
339
-
340
318
  render() {
341
- const {
342
- isFullMode, className, cap = {}, authData = {},
343
- } = this.props;
319
+ const { isFullMode, className, cap = {}, authData = {}} = this.props;
344
320
  const { accessiblePermissions = []} = cap.user || authData.user || {};
345
321
  let isCreativeAccessible = true;
346
322
  if (!accessiblePermissions.includes(CREATIVES_UI_VIEW)) {
@@ -349,16 +325,14 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
349
325
  return (
350
326
  !isCreativeAccessible ? <AccessForbidden /> : (
351
327
  <div className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`} data-testid="cap-wrapper">
352
- {isFullMode && (
353
- <Helmet
354
- title={this.props.intl.formatMessage(messages.creatives)}
355
- meta={[
356
- { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
357
- ]}
358
- />
359
- )}
328
+ {isFullMode && <Helmet
329
+ title={this.props.intl.formatMessage(messages.creatives)}
330
+ meta={[
331
+ { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
332
+ ]}
333
+ />}
360
334
  <div className="component-wrapper">
361
- {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives} />} description={<FormattedMessage {...messages.creativesDesc} />} />}
335
+ {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives}/>} description={<FormattedMessage {...messages.creativesDesc}/>}/>}
362
336
  <StyledCapTab
363
337
  panes={this.state.panes}
364
338
  onChange={this.channelChange}
@@ -403,7 +377,7 @@ TemplatesV2.defaultProps = {
403
377
  };
404
378
 
405
379
  const mapStateToProps = createStructuredSelector({
406
- Global: makeSelectAuthenticated(),
380
+ cap: makeSelectAuthenticated(),
407
381
  Templates: makeSelectTemplates(),
408
382
  TemplatesList: makeSelectTemplatesResponse(),
409
383
  currentOrgDetails: selectCurrentOrgDetails(),
@@ -0,0 +1,187 @@
1
+ import { expectSaga } from 'redux-saga-test-plan';
2
+ import { call, put } from 'redux-saga/effects';
3
+ import { throwError } from 'redux-saga-test-plan/providers';
4
+ import * as Api from '../../../services/api';
5
+ import * as sagas from '../sagas';
6
+ import { v2ViberSagas } from '../sagas';
7
+ import * as types from '../constants';
8
+
9
+ describe('Viber Sagas', () => {
10
+
11
+ describe('uploadViberAsset Saga', () => {
12
+ const file = new Blob(['file contents'], { type: 'text/plain' });
13
+ const assetType = 'image';
14
+ const fileParams = { directory: 'profile', templateType: 'viber' };
15
+
16
+ it('handles uploading asset successfully', () => {
17
+ const fakeResponse = {
18
+ response: { asset: { id: 1, url: 'http://example.com/image.png' } },
19
+ status: { code: 200 }
20
+ };
21
+
22
+ return expectSaga(sagas.uploadViberAsset, file, assetType, fileParams)
23
+ .provide([
24
+ [call(Api.uploadFile, file, assetType, fileParams), fakeResponse]
25
+ ])
26
+ .put({
27
+ type: types.UPLOAD_VIBER_ASSET_SUCCESS,
28
+ data: fakeResponse.response.asset,
29
+ statusCode: fakeResponse.status.code,
30
+ templateType: undefined
31
+ })
32
+ .run();
33
+ });
34
+
35
+ it('handles asset upload failure', () => {
36
+ const error = new Error('Upload failed');
37
+
38
+ return expectSaga(sagas.uploadViberAsset, file, assetType, fileParams)
39
+ .provide([
40
+ [call(Api.uploadFile, file, assetType, fileParams), throwError(error)]
41
+ ])
42
+ .put({
43
+ type: types.UPLOAD_VIBER_ASSET_FAILURE,
44
+ error
45
+ })
46
+ .run();
47
+ });
48
+ });
49
+
50
+ describe('createViberTemplate Saga', () => {
51
+ const template = { content: 'New Viber Template' };
52
+ const callback = jest.fn();
53
+
54
+ it('handles creating template successfully', () => {
55
+ const fakeResponse = {
56
+ response: { id: 2, content: template.content },
57
+ status: { code: 201 }
58
+ };
59
+
60
+ return expectSaga(sagas.createViberTemplate, { template, callback })
61
+ .provide([
62
+ [call(Api.createViberTemplate, { template }), fakeResponse]
63
+ ])
64
+ .put({
65
+ type: types.CREATE_VIBER_TEMPLATE_SUCCESS,
66
+ data: fakeResponse.response,
67
+ statusCode: fakeResponse.status.code,
68
+ errorMsg: undefined
69
+ })
70
+ .run()
71
+ .then(() => {
72
+ expect(callback).toHaveBeenCalledWith(fakeResponse.response);
73
+ });
74
+ });
75
+
76
+ it('handles failure in creating template', () => {
77
+ const error = new Error({ message: 'Creation failed', status: { code: 400 } });
78
+
79
+ const errorMsg = 'Creation failed';
80
+ return expectSaga(sagas.createViberTemplate, { template, callback })
81
+ .provide([
82
+ [call(Api.createViberTemplate, { template }), throwError(error)]
83
+ ])
84
+ .put({
85
+ type: types.CREATE_VIBER_TEMPLATE_FAILURE,
86
+ error,
87
+ errorMsg : undefined
88
+ })
89
+ .run()
90
+ .then(() => {
91
+ expect(callback).toHaveBeenCalledWith(null, undefined);
92
+ });
93
+ });
94
+ });
95
+
96
+ describe('editTemplate Saga', () => {
97
+ const template = { id: 1, content: 'Updated Content' };
98
+ const callback = jest.fn();
99
+
100
+ it('handles editing template successfully', () => {
101
+ const fakeResponse = {
102
+ response: { updated: true },
103
+ status: { code: 200 }
104
+ };
105
+
106
+ return expectSaga(sagas.editTemplate, { template, callback })
107
+ .provide([
108
+ [call(Api.createViberTemplate, { template }), fakeResponse]
109
+ ])
110
+ .put({
111
+ type: types.EDIT_VIBER_TEMPLATE_SUCCESS,
112
+ data: fakeResponse.response,
113
+ statusCode: fakeResponse.status.code,
114
+ errorMsg: undefined
115
+ })
116
+ .run()
117
+ .then(() => {
118
+ expect(callback).toHaveBeenCalledWith(fakeResponse.response);
119
+ });
120
+ });
121
+
122
+ it('handles failure in editing template', () => {
123
+ const errorMsg = 'Error in editing template';
124
+ return expectSaga(sagas.editTemplate, { template, callback })
125
+ .provide([
126
+ [call(Api.createViberTemplate, { template }), throwError({ message: errorMsg, status: { code: 400 } })]
127
+ ])
128
+ .put({
129
+ type: types.EDIT_VIBER_TEMPLATE_FAILURE,
130
+ error: { message: 'Error in editing template', status: { code: 400 } },
131
+ errorMsg: undefined,
132
+ })
133
+ .run()
134
+ .then(() => {
135
+ expect(callback).toHaveBeenCalledWith(null, undefined);
136
+ });
137
+ });
138
+ });
139
+
140
+ describe('getTemplateDetails Saga', () => {
141
+ const id = '123';
142
+ const callback = jest.fn();
143
+
144
+ it('handles fetching template details successfully', () => {
145
+ const fakeResponse = {
146
+ response: { id: id, name: 'Detailed Template' }
147
+ };
148
+
149
+ return expectSaga(sagas.getTemplateDetails, { id, callback })
150
+ .provide([
151
+ [call(Api.getTemplateDetails, { id, channel: 'VIBER' }), fakeResponse]
152
+ ])
153
+ .put({
154
+ type: types.GET_VIBER_TEMPLATE_DETAILS_SUCCESS,
155
+ data: fakeResponse.response
156
+ })
157
+ .run()
158
+ .then(() => {
159
+ expect(callback).not.toHaveBeenCalledWith(0);
160
+ });
161
+ });
162
+
163
+ it('handles failure in fetching template details', () => {
164
+ const error = new Error('Fetch failed');
165
+
166
+ return expectSaga(sagas.getTemplateDetails, { id, callback })
167
+ .provide([
168
+ [call(Api.getTemplateDetails, { id, channel: 'VIBER' }), throwError(error)]
169
+ ])
170
+ .put({
171
+ type: types.GET_VIBER_TEMPLATE_DETAILS_FAILURE,
172
+ error
173
+ })
174
+ .run()
175
+ .then(() => {
176
+ expect(callback).not.toHaveBeenCalledWith();
177
+ });
178
+ });
179
+ });
180
+
181
+ describe('v2ViberSagas Combined', () => {
182
+ it('should initialize all Viber-related watcher sagas without error', () => {
183
+ return expectSaga(v2ViberSagas).run();
184
+ });
185
+ });
186
+
187
+ });