@capillarytech/creatives-library 8.0.340-beta.0.4 → 8.0.340-beta.0.6

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 (67) hide show
  1. package/constants/unified.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -0
  5. package/utils/common.js +6 -0
  6. package/utils/test-utils.js +2 -2
  7. package/utils/tests/v2Common.test.js +46 -1
  8. package/utils/v2common.js +18 -0
  9. package/v2Components/CapTagList/index.js +5 -6
  10. package/v2Components/CapTagListWithInput/index.js +1 -1
  11. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +18 -6
  12. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +27 -0
  13. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +48 -0
  14. package/v2Components/TemplatePreview/_templatePreview.scss +22 -1
  15. package/v2Components/TemplatePreview/index.js +21 -9
  16. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +1 -0
  17. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  18. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +28 -20
  19. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +24 -16
  20. package/v2Containers/CreativesContainer/SlideBoxContent.js +16 -5
  21. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  22. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -4
  23. package/v2Containers/CreativesContainer/index.js +14 -1
  24. package/v2Containers/CreativesContainer/messages.js +4 -0
  25. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -4
  26. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +4 -4
  27. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +3 -0
  28. package/v2Containers/Email/reducer.js +12 -3
  29. package/v2Containers/Email/sagas.js +9 -4
  30. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
  31. package/v2Containers/Email/tests/reducer.test.js +47 -0
  32. package/v2Containers/Email/tests/sagas.test.js +146 -6
  33. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +8 -1
  34. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  35. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +7 -0
  36. package/v2Containers/MobilePush/Create/index.js +1 -1
  37. package/v2Containers/MobilePush/Edit/index.js +1 -1
  38. package/v2Containers/Sms/Create/index.js +3 -0
  39. package/v2Containers/Sms/SCHEMA_FORMBUILDER_MAP.md +1 -1
  40. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  41. package/v2Containers/Templates/_templates.scss +155 -24
  42. package/v2Containers/Templates/actions.js +44 -0
  43. package/v2Containers/Templates/constants.js +31 -0
  44. package/v2Containers/Templates/index.js +400 -59
  45. package/v2Containers/Templates/messages.js +96 -0
  46. package/v2Containers/Templates/reducer.js +84 -1
  47. package/v2Containers/Templates/sagas.js +64 -0
  48. package/v2Containers/Templates/selectors.js +12 -0
  49. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  50. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1166 -1112
  51. package/v2Containers/Templates/tests/index.test.js +6 -0
  52. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  53. package/v2Containers/Templates/tests/sagas.test.js +390 -8
  54. package/v2Containers/Templates/tests/selector.test.js +32 -0
  55. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
  56. package/v2Containers/Viber/constants.js +8 -0
  57. package/v2Containers/Viber/index.js +5 -0
  58. package/v2Containers/Viber/messages.js +4 -0
  59. package/v2Containers/Viber/reducer.js +26 -3
  60. package/v2Containers/Viber/sagas.js +50 -8
  61. package/v2Containers/Viber/tests/index.test.js +80 -0
  62. package/v2Containers/Viber/tests/reducer.test.js +297 -0
  63. package/v2Containers/Viber/tests/saga.test.js +412 -40
  64. package/v2Containers/Whatsapp/constants.js +8 -0
  65. package/v2Containers/Whatsapp/index.js +145 -5
  66. package/v2Containers/Whatsapp/index.scss +12 -0
  67. package/v2Containers/Whatsapp/messages.js +16 -0
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
4
4
  import CapLabel from "@capillarytech/cap-ui-library/CapLabel";
5
5
  import messages from "./messages";
6
+ import { createAsyncAssetUploadConstants } from '../../utils/asyncAssetUpload';
6
7
 
7
8
  export const DEFAULT_ACTION = 'app/v2Containers/Viber/DEFAULT_ACTION';
8
9
  export const ALLOWED_EXTENSIONS_REGEX = (/\.(jpe?g|png)$/i);
@@ -16,6 +17,13 @@ export const UPLOAD_VIBER_ASSET_REQUEST = 'app/v2Containers/Viber/UPLOAD_ASSET_R
16
17
  export const UPLOAD_VIBER_ASSET_SUCCESS = 'app/v2Containers/Viber/UPLOAD_ASSET_SUCCESS';
17
18
  export const UPLOAD_VIBER_ASSET_FAILURE = 'app/v2Containers/Viber/UPLOAD_ASSET_FAILURE';
18
19
 
20
+ const viberAsyncPrefix = 'app/v2Containers/Viber';
21
+ const asyncUploadConstants = createAsyncAssetUploadConstants('VIBER', viberAsyncPrefix);
22
+ export const UPLOAD_VIBER_ASSET_PROCESSING = asyncUploadConstants.UPLOAD_VIBER_ASSET_PROCESSING;
23
+ export const UPLOAD_VIBER_ASSET_COMPLETED = asyncUploadConstants.UPLOAD_VIBER_ASSET_COMPLETED;
24
+ export const UPLOAD_VIBER_ASSET_FAILED = asyncUploadConstants.UPLOAD_VIBER_ASSET_FAILED;
25
+ export const UPLOAD_VIBER_ASSET_TIMEOUT = asyncUploadConstants.UPLOAD_VIBER_ASSET_TIMEOUT;
26
+
19
27
 
20
28
  export const CREATE_VIBER_TEMPLATE_REQUEST = 'app/v2Containers/Viber/CREATE_VIBER_TEMPLATE_REQUEST';
21
29
  export const CREATE_VIBER_TEMPLATE_SUCCESS = 'app/v2Containers/Viber/CREATE_VIBER_TEMPLATE_SUCCESS';
@@ -422,6 +422,7 @@ export const Viber = (props) => {
422
422
  channel={VIBER}
423
423
  errorMessage={formatMessage(messages.videoErrorMessage)}
424
424
  showVideoNameAndDuration
425
+ assetUploading={viber?.assetUploading}
425
426
  />
426
427
  );
427
428
 
@@ -802,6 +803,10 @@ export const Viber = (props) => {
802
803
  ) {
803
804
  return true;
804
805
  }
806
+ // block submit while a media asset upload is still in progress
807
+ if ((isMediaTypeImage || isMediaTypeVideo) && viber?.assetUploading) {
808
+ return true;
809
+ }
805
810
  if (isBtnTypeCta && !isCtaSaved) {
806
811
  return true;
807
812
  }
@@ -210,4 +210,8 @@ export default defineMessages({
210
210
  id: `${scope}.cancel`,
211
211
  defaultMessage: 'Cancel',
212
212
  },
213
+ assetIdMissingError: {
214
+ id: `${scope}.assetIdMissingError`,
215
+ defaultMessage: 'Asset upload initiated but no asset ID was returned from the server. Unable to track processing status.',
216
+ },
213
217
  });
@@ -6,10 +6,19 @@
6
6
 
7
7
  import { fromJS } from 'immutable';
8
8
  import * as types from './constants';
9
+ import { createAsyncAssetUploadReducerCases } from '../../utils/asyncAssetUpload';
10
+
11
+ const asyncUploadCases = createAsyncAssetUploadReducerCases({
12
+ PROCESSING: types.UPLOAD_VIBER_ASSET_PROCESSING,
13
+ COMPLETED: types.UPLOAD_VIBER_ASSET_COMPLETED,
14
+ FAILED: types.UPLOAD_VIBER_ASSET_FAILED,
15
+ TIMEOUT: types.UPLOAD_VIBER_ASSET_TIMEOUT,
16
+ });
9
17
 
10
18
  const initialState = fromJS({
11
19
  uploadedAssetData: {},
12
20
  createTemplateInProgress: false,
21
+ assetProcessing: {},
13
22
  });
14
23
 
15
24
  function viberReducer(state = initialState, action) {
@@ -37,14 +46,25 @@ function viberReducer(state = initialState, action) {
37
46
  return state
38
47
  .set('uploadAssetSuccess', (action.statusCode !== undefined && action.statusCode !== '' && action.statusCode < 300))
39
48
  .set('assetUploading', false)
40
- .set(action.templateType !== undefined ? `uploadedAssetData${action.templateType}` : 'uploadedAssetData', action.data);
49
+ .set(action.templateType !== undefined ? `uploadedAssetData${action.templateType}` : 'uploadedAssetData', fromJS(action.data));
41
50
  case types.UPLOAD_VIBER_ASSET_FAILURE:
42
51
  return state
43
52
  .set('uploadAssetSuccess', false)
44
53
  .set('assetUploading', false);
54
+ case asyncUploadCases.PROCESSING:
55
+ return asyncUploadCases.handleProcessing(state, action);
56
+ case asyncUploadCases.COMPLETED:
57
+ return asyncUploadCases.handleCompleted(state, action);
58
+ case asyncUploadCases.FAILED:
59
+ return asyncUploadCases.handleFailed(state, action);
60
+ case asyncUploadCases.TIMEOUT:
61
+ return asyncUploadCases.handleTimeout(state, action);
45
62
  case types.CLEAR_VIBER_ASSET:
46
63
  return state
47
- .delete(action.templateType !== undefined ? `uploadedAssetData${action.templateType}` : 'uploadedAssetData');
64
+ .delete(action.templateType !== undefined ? `uploadedAssetData${action.templateType}` : 'uploadedAssetData')
65
+ .set('assetProcessing', fromJS({}))
66
+ .set('assetUploading', false)
67
+ .set('uploadAssetSuccess', false);
48
68
  // FOR EDIT
49
69
  case types.EDIT_VIBER_TEMPLATE_REQUEST:
50
70
  return state
@@ -75,7 +95,10 @@ function viberReducer(state = initialState, action) {
75
95
  return state
76
96
  .delete('uploadedAssetData')
77
97
  .delete('metaEntities')
78
- .delete('templateDetails');
98
+ .delete('templateDetails')
99
+ .set('assetProcessing', fromJS({}))
100
+ .set('assetUploading', false)
101
+ .set('uploadAssetSuccess', false);
79
102
  default:
80
103
  return state;
81
104
  }
@@ -2,17 +2,59 @@ import {
2
2
  call, put, takeLatest, all,
3
3
  } from 'redux-saga/effects';
4
4
  import * as Api from '../../services/api';
5
+ import { pollAssetStatus } from '../../sagas/assetPolling';
6
+ import { createPollingConfig } from '../../utils/asyncAssetUpload';
7
+ import { ASSET_STATUS } from '../../utils/assetStatusConstants';
5
8
  import * as types from './constants';
9
+ import messages from './messages';
6
10
 
7
- export function* uploadViberAsset(file, assetType, fileParams ) {
11
+ export function* uploadViberAsset(params) {
8
12
  try {
9
- const result = yield call(Api.uploadFile, file, assetType, fileParams);
10
- yield put({
11
- type: types.UPLOAD_VIBER_ASSET_SUCCESS,
12
- data: result.response.asset,
13
- statusCode: result.status ? result.status.code : '',
14
- templateType: file.templateType,
15
- });
13
+ const result = yield call(Api.uploadFile, params);
14
+ const responseData = result?.response || {};
15
+ const statusCode = result?.status?.code || result?.statusCode;
16
+ const asset = responseData?.asset || responseData;
17
+
18
+ if (statusCode === 202 || responseData?.processingStatus === ASSET_STATUS.PROCESSING) {
19
+ const assetId = asset?._id || responseData?.assetId;
20
+ const { templateType } = params || {};
21
+ const assetType = params?.assetType || asset?.type?.toLowerCase() || 'image';
22
+
23
+ if (assetId) {
24
+ yield put({
25
+ type: types.UPLOAD_VIBER_ASSET_PROCESSING,
26
+ payload: {
27
+ assetId,
28
+ asset,
29
+ processingStatus: ASSET_STATUS.PROCESSING,
30
+ },
31
+ });
32
+
33
+ const actionTypes = {
34
+ COMPLETED: types.UPLOAD_VIBER_ASSET_COMPLETED,
35
+ FAILED: types.UPLOAD_VIBER_ASSET_FAILED,
36
+ TIMEOUT: types.UPLOAD_VIBER_ASSET_TIMEOUT,
37
+ };
38
+ const pollingConfig = createPollingConfig(assetType, assetId, actionTypes, templateType);
39
+ yield call(pollAssetStatus, pollingConfig);
40
+ } else {
41
+ yield put({
42
+ type: types.UPLOAD_VIBER_ASSET_FAILED,
43
+ payload: {
44
+ assetId: undefined,
45
+ error: messages.assetIdMissingError,
46
+ },
47
+ templateType,
48
+ });
49
+ }
50
+ } else {
51
+ yield put({
52
+ type: types.UPLOAD_VIBER_ASSET_SUCCESS,
53
+ data: asset,
54
+ statusCode,
55
+ templateType: params?.templateType,
56
+ });
57
+ }
16
58
  } catch (error) {
17
59
  yield put({ type: types.UPLOAD_VIBER_ASSET_FAILURE, error });
18
60
  }
@@ -309,4 +309,84 @@ describe('Test Viber container', () => {
309
309
  });
310
310
  expect(doneBtn).toBeDisabled();
311
311
  });
312
+
313
+ it('disables Done while a replacement video asset is uploading (finding 1)', async () => {
314
+ renderComponent({
315
+ actions: mockActions,
316
+ globalActions: mockGlobalActions,
317
+ templateData: { mode: 'create' },
318
+ viber: {
319
+ uploadedAssetData: {},
320
+ createTemplateInProgress: false,
321
+ templateDetails: templateDetailsVideo,
322
+ // a new upload has started after the template was loaded
323
+ assetUploading: true,
324
+ },
325
+ location: {
326
+ pathname: '/sms/edit',
327
+ query: { type: false, module: 'default' },
328
+ search: '',
329
+ },
330
+ isFullMode: true,
331
+ params: { id: 'test' },
332
+ handleClose: jest.fn(),
333
+ metaEntities,
334
+ getDefaultTags,
335
+ injectedTags,
336
+ });
337
+ const doneBtn = screen.getByRole('button', { name: /done/i });
338
+ expect(doneBtn).toBeDisabled();
339
+ });
340
+
341
+ it('disables Done while an image asset is uploading (finding 1)', async () => {
342
+ renderComponent({
343
+ actions: mockActions,
344
+ globalActions: mockGlobalActions,
345
+ templateData: { mode: 'create' },
346
+ viber: {
347
+ uploadedAssetData: {},
348
+ createTemplateInProgress: false,
349
+ templateDetails: templateDetailsImage,
350
+ assetUploading: true,
351
+ },
352
+ location: {
353
+ pathname: '/sms/edit',
354
+ query: { type: false, module: 'default' },
355
+ search: '',
356
+ },
357
+ isFullMode: true,
358
+ params: { id: 'test' },
359
+ handleClose: jest.fn(),
360
+ });
361
+ const doneBtn = screen.getByRole('button', { name: /done/i });
362
+ expect(doneBtn).toBeDisabled();
363
+ });
364
+
365
+ it('keeps Done enabled when assetUploading is true but media type is text', async () => {
366
+ renderComponent({
367
+ actions: mockActions,
368
+ globalActions: mockGlobalActions,
369
+ templateData: { mode: 'create' },
370
+ viber: {
371
+ uploadedAssetData: {},
372
+ createTemplateInProgress: false,
373
+ templateDetails: templateDetailsText,
374
+ // stale upload flag, but the form has no media so submission is safe
375
+ assetUploading: true,
376
+ },
377
+ location: {
378
+ pathname: '/sms/edit',
379
+ query: { type: false, module: 'default' },
380
+ search: '',
381
+ },
382
+ isFullMode: true,
383
+ params: { id: 'test' },
384
+ handleClose: jest.fn(),
385
+ metaEntities,
386
+ getDefaultTags,
387
+ injectedTags,
388
+ });
389
+ const doneBtn = screen.getByRole('button', { name: /done/i });
390
+ expect(doneBtn).toBeEnabled();
391
+ });
312
392
  });
@@ -0,0 +1,297 @@
1
+ import { fromJS } from 'immutable';
2
+ import viberReducer from '../reducer';
3
+ import * as types from '../constants';
4
+ import { ASSET_STATUS } from '../../../utils/assetStatusConstants';
5
+
6
+ const initialState = fromJS({
7
+ uploadedAssetData: {},
8
+ createTemplateInProgress: false,
9
+ assetProcessing: {},
10
+ });
11
+
12
+ describe('viberReducer', () => {
13
+ it('returns initial state for unknown action', () => {
14
+ expect(viberReducer(undefined, { type: '@@INIT' })).toEqual(initialState);
15
+ });
16
+
17
+ describe('CREATE_VIBER_TEMPLATE_*', () => {
18
+ it('handles CREATE_VIBER_TEMPLATE_REQUEST', () => {
19
+ const next = viberReducer(initialState, {
20
+ type: types.CREATE_VIBER_TEMPLATE_REQUEST,
21
+ });
22
+ expect(next.get('createTemplateInProgress')).toBe(true);
23
+ expect(next.get('createTemplateError')).toBe(false);
24
+ expect(next.get('createTemplateErrorMessage')).toEqual(fromJS(''));
25
+ });
26
+
27
+ it('handles CREATE_VIBER_TEMPLATE_SUCCESS with status > 300', () => {
28
+ const next = viberReducer(initialState, {
29
+ type: types.CREATE_VIBER_TEMPLATE_SUCCESS,
30
+ data: { id: 'x' },
31
+ statusCode: 400,
32
+ errorMsg: 'oops',
33
+ });
34
+ expect(next.get('createTemplateInProgress')).toBe(false);
35
+ expect(next.get('response')).toEqual({ id: 'x' });
36
+ expect(next.get('createTemplateError')).toBe(true);
37
+ expect(next.get('createTemplateErrorMessage')).toEqual(fromJS('oops'));
38
+ });
39
+
40
+ it('handles CREATE_VIBER_TEMPLATE_SUCCESS with status <= 300', () => {
41
+ const next = viberReducer(initialState, {
42
+ type: types.CREATE_VIBER_TEMPLATE_SUCCESS,
43
+ data: { id: 'x' },
44
+ statusCode: 200,
45
+ errorMsg: '',
46
+ });
47
+ expect(next.get('createTemplateError')).toBe(false);
48
+ });
49
+
50
+ it('handles CREATE_VIBER_TEMPLATE_FAILURE', () => {
51
+ const next = viberReducer(initialState, {
52
+ type: types.CREATE_VIBER_TEMPLATE_FAILURE,
53
+ errorMsg: 'bad',
54
+ });
55
+ expect(next.get('createTemplateInProgress')).toBe(false);
56
+ expect(next.get('createTemplateError')).toBe(true);
57
+ expect(next.get('createTemplateErrorMessage')).toEqual(fromJS('bad'));
58
+ });
59
+ });
60
+
61
+ describe('UPLOAD_VIBER_ASSET_*', () => {
62
+ it('handles UPLOAD_VIBER_ASSET_REQUEST', () => {
63
+ const next = viberReducer(initialState, {
64
+ type: types.UPLOAD_VIBER_ASSET_REQUEST,
65
+ });
66
+ expect(next.get('uploadAssetSuccess')).toBe(false);
67
+ expect(next.get('assetUploading')).toBe(true);
68
+ });
69
+
70
+ it('handles UPLOAD_VIBER_ASSET_SUCCESS and wraps data with fromJS (finding 2)', () => {
71
+ const payload = { url: 'https://cdn/x.png', meta: { size: 100 } };
72
+ const next = viberReducer(initialState, {
73
+ type: types.UPLOAD_VIBER_ASSET_SUCCESS,
74
+ data: payload,
75
+ statusCode: 200,
76
+ });
77
+ expect(next.get('assetUploading')).toBe(false);
78
+ expect(next.get('uploadAssetSuccess')).toBe(true);
79
+ // must be wrapped in Immutable (same as asyncUploadCases.handleCompleted)
80
+ const stored = next.get('uploadedAssetData');
81
+ expect(stored).toEqual(fromJS(payload));
82
+ // verify it's actually an Immutable Map, not a plain JS object
83
+ expect(stored.toJS).toBeDefined();
84
+ expect(typeof stored.toJS).toBe('function');
85
+ expect(stored.get('url')).toBe(payload.url);
86
+ });
87
+
88
+ it('handles UPLOAD_VIBER_ASSET_SUCCESS with templateType key wrapped in fromJS', () => {
89
+ const payload = { url: 'https://cdn/y.mp4' };
90
+ const next = viberReducer(initialState, {
91
+ type: types.UPLOAD_VIBER_ASSET_SUCCESS,
92
+ data: payload,
93
+ statusCode: 200,
94
+ templateType: 3,
95
+ });
96
+ const stored = next.get('uploadedAssetData3');
97
+ expect(stored).toEqual(fromJS(payload));
98
+ expect(stored.get('url')).toBe(payload.url);
99
+ });
100
+
101
+ it('UPLOAD_VIBER_ASSET_SUCCESS sets uploadAssetSuccess false when status >= 300', () => {
102
+ const next = viberReducer(initialState, {
103
+ type: types.UPLOAD_VIBER_ASSET_SUCCESS,
104
+ data: {},
105
+ statusCode: 500,
106
+ });
107
+ expect(next.get('uploadAssetSuccess')).toBe(false);
108
+ });
109
+
110
+ it('UPLOAD_VIBER_ASSET_SUCCESS sync and async produce same shape', () => {
111
+ const asset = { url: 'https://cdn/same.png' };
112
+ const sync = viberReducer(initialState, {
113
+ type: types.UPLOAD_VIBER_ASSET_SUCCESS,
114
+ data: asset,
115
+ statusCode: 200,
116
+ });
117
+ const async_ = viberReducer(initialState, {
118
+ type: types.UPLOAD_VIBER_ASSET_COMPLETED,
119
+ payload: { assetId: 'a1', asset },
120
+ });
121
+ // Both paths should store uploadedAssetData as Immutable fromJS(asset)
122
+ expect(sync.get('uploadedAssetData')).toEqual(async_.get('uploadedAssetData'));
123
+ });
124
+
125
+ it('handles UPLOAD_VIBER_ASSET_FAILURE', () => {
126
+ const next = viberReducer(initialState, {
127
+ type: types.UPLOAD_VIBER_ASSET_FAILURE,
128
+ });
129
+ expect(next.get('uploadAssetSuccess')).toBe(false);
130
+ expect(next.get('assetUploading')).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('async polling cases', () => {
135
+ it('handles UPLOAD_VIBER_ASSET_PROCESSING', () => {
136
+ const next = viberReducer(initialState, {
137
+ type: types.UPLOAD_VIBER_ASSET_PROCESSING,
138
+ payload: { assetId: 'a1', asset: { id: 'a1' } },
139
+ });
140
+ expect(next.get('assetUploading')).toBe(true);
141
+ expect(next.get('uploadAssetSuccess')).toBe(false);
142
+ expect(next.getIn(['assetProcessing', 'a1', 'status'])).toBe(ASSET_STATUS.PROCESSING);
143
+ });
144
+
145
+ it('handles UPLOAD_VIBER_ASSET_COMPLETED', () => {
146
+ const next = viberReducer(initialState, {
147
+ type: types.UPLOAD_VIBER_ASSET_COMPLETED,
148
+ payload: { assetId: 'a1', asset: { url: 'https://cdn/x.png' } },
149
+ });
150
+ expect(next.get('assetUploading')).toBe(false);
151
+ expect(next.get('uploadAssetSuccess')).toBe(true);
152
+ expect(next.getIn(['assetProcessing', 'a1', 'status'])).toBe(ASSET_STATUS.COMPLETED);
153
+ expect(next.get('uploadedAssetData')).toEqual(fromJS({ url: 'https://cdn/x.png' }));
154
+ });
155
+
156
+ it('handles UPLOAD_VIBER_ASSET_FAILED', () => {
157
+ const next = viberReducer(initialState, {
158
+ type: types.UPLOAD_VIBER_ASSET_FAILED,
159
+ payload: { assetId: 'a1', error: 'boom' },
160
+ });
161
+ expect(next.get('assetUploading')).toBe(false);
162
+ expect(next.get('uploadAssetSuccess')).toBe(false);
163
+ expect(next.getIn(['assetProcessing', 'a1', 'status'])).toBe(ASSET_STATUS.FAILED);
164
+ expect(next.getIn(['assetProcessing', 'a1', 'error'])).toBe('boom');
165
+ });
166
+
167
+ it('handles UPLOAD_VIBER_ASSET_TIMEOUT', () => {
168
+ const next = viberReducer(initialState, {
169
+ type: types.UPLOAD_VIBER_ASSET_TIMEOUT,
170
+ payload: { assetId: 'a1', message: 'timed out' },
171
+ });
172
+ expect(next.get('assetUploading')).toBe(false);
173
+ expect(next.getIn(['assetProcessing', 'a1', 'status'])).toBe(ASSET_STATUS.TIMEOUT);
174
+ expect(next.getIn(['assetProcessing', 'a1', 'error'])).toBe('timed out');
175
+ });
176
+ });
177
+
178
+ describe('CLEAR_VIBER_ASSET (finding 3)', () => {
179
+ const dirtyState = initialState
180
+ .set('assetUploading', true)
181
+ .set('uploadAssetSuccess', true)
182
+ .set('uploadedAssetData', fromJS({ url: 'x' }))
183
+ .set('uploadedAssetData2', fromJS({ url: 'y' }))
184
+ .setIn(['assetProcessing', 'a1'], fromJS({ status: 'processing' }));
185
+
186
+ it('clears default uploadedAssetData and resets upload flags', () => {
187
+ const next = viberReducer(dirtyState, { type: types.CLEAR_VIBER_ASSET });
188
+ expect(next.has('uploadedAssetData')).toBe(false);
189
+ expect(next.get('assetProcessing')).toEqual(fromJS({}));
190
+ expect(next.get('assetUploading')).toBe(false);
191
+ expect(next.get('uploadAssetSuccess')).toBe(false);
192
+ });
193
+
194
+ it('clears templateType-specific uploadedAssetData and resets upload flags', () => {
195
+ const next = viberReducer(dirtyState, {
196
+ type: types.CLEAR_VIBER_ASSET,
197
+ templateType: 2,
198
+ });
199
+ expect(next.has('uploadedAssetData2')).toBe(false);
200
+ // default key untouched by specific clear
201
+ expect(next.has('uploadedAssetData')).toBe(true);
202
+ expect(next.get('assetProcessing')).toEqual(fromJS({}));
203
+ expect(next.get('assetUploading')).toBe(false);
204
+ expect(next.get('uploadAssetSuccess')).toBe(false);
205
+ });
206
+ });
207
+
208
+ describe('CLEAR_VIBER_DATA (finding 3 mirror)', () => {
209
+ it('clears assetProcessing, assetUploading, uploadAssetSuccess', () => {
210
+ const dirtyState = initialState
211
+ .set('assetUploading', true)
212
+ .set('uploadAssetSuccess', true)
213
+ .set('metaEntities', fromJS({ some: 'data' }))
214
+ .set('templateDetails', fromJS({ id: 'x' }))
215
+ .setIn(['assetProcessing', 'a1'], fromJS({ status: 'processing' }));
216
+ const next = viberReducer(dirtyState, { type: types.CLEAR_VIBER_DATA });
217
+ expect(next.has('uploadedAssetData')).toBe(false);
218
+ expect(next.has('metaEntities')).toBe(false);
219
+ expect(next.has('templateDetails')).toBe(false);
220
+ expect(next.get('assetProcessing')).toEqual(fromJS({}));
221
+ expect(next.get('assetUploading')).toBe(false);
222
+ expect(next.get('uploadAssetSuccess')).toBe(false);
223
+ });
224
+ });
225
+
226
+ describe('EDIT_VIBER_TEMPLATE_*', () => {
227
+ it('handles EDIT_VIBER_TEMPLATE_REQUEST', () => {
228
+ const next = viberReducer(initialState, {
229
+ type: types.EDIT_VIBER_TEMPLATE_REQUEST,
230
+ });
231
+ expect(next.get('editTemplateInProgress')).toBe(true);
232
+ expect(next.get('editTemplateError')).toBe(false);
233
+ });
234
+
235
+ it('handles EDIT_VIBER_TEMPLATE_SUCCESS', () => {
236
+ const next = viberReducer(initialState, {
237
+ type: types.EDIT_VIBER_TEMPLATE_SUCCESS,
238
+ data: { id: 'e' },
239
+ statusCode: 200,
240
+ errorMsg: '',
241
+ });
242
+ expect(next.get('editTemplateInProgress')).toBe(false);
243
+ expect(next.get('editResponse')).toEqual({ id: 'e' });
244
+ expect(next.get('editTemplateError')).toBe(false);
245
+ });
246
+
247
+ it('handles EDIT_VIBER_TEMPLATE_SUCCESS error status', () => {
248
+ const next = viberReducer(initialState, {
249
+ type: types.EDIT_VIBER_TEMPLATE_SUCCESS,
250
+ data: {},
251
+ statusCode: 500,
252
+ errorMsg: 'err',
253
+ });
254
+ expect(next.get('editTemplateError')).toBe(true);
255
+ });
256
+
257
+ it('handles EDIT_VIBER_TEMPLATE_FAILURE', () => {
258
+ const next = viberReducer(initialState, {
259
+ type: types.EDIT_VIBER_TEMPLATE_FAILURE,
260
+ errorMsg: 'nope',
261
+ });
262
+ expect(next.get('editTemplateError')).toBe(true);
263
+ });
264
+ });
265
+
266
+ describe('GET/CLEAR branches', () => {
267
+ it('handles GET_VIBER_TEMPLATE_DETAILS_REQUEST', () => {
268
+ const next = viberReducer(initialState, {
269
+ type: types.GET_VIBER_TEMPLATE_DETAILS_REQUEST,
270
+ });
271
+ expect(next.get('getTemplateDetailsInProgress')).toBe(true);
272
+ });
273
+
274
+ it('handles GET_VIBER_TEMPLATE_DETAILS_SUCCESS', () => {
275
+ const next = viberReducer(initialState, {
276
+ type: types.GET_VIBER_TEMPLATE_DETAILS_SUCCESS,
277
+ data: { id: 'd' },
278
+ });
279
+ expect(next.get('getTemplateDetailsInProgress')).toBe(false);
280
+ expect(next.get('templateDetails')).toEqual({ id: 'd' });
281
+ });
282
+
283
+ it('handles GET_VIBER_TEMPLATE_DETAILS_FAILURE / CLEAR_VIBER_EDIT_RESPONSE_REQUEST', () => {
284
+ const next = viberReducer(initialState, {
285
+ type: types.CLEAR_VIBER_EDIT_RESPONSE_REQUEST,
286
+ });
287
+ expect(next.get('editResponse')).toEqual({});
288
+ });
289
+
290
+ it('handles CLEAR_VIBER_CREATE_RESPONSE_REQUEST', () => {
291
+ const next = viberReducer(initialState, {
292
+ type: types.CLEAR_VIBER_CREATE_RESPONSE_REQUEST,
293
+ });
294
+ expect(next.get('response')).toEqual({});
295
+ });
296
+ });
297
+ });