@capillarytech/creatives-library 8.0.342 → 8.0.343-alpha.2
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.
- package/package.json +1 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +6 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +4 -1
- package/v2Containers/Viber/constants.js +8 -0
- package/v2Containers/Viber/index.js +5 -0
- package/v2Containers/Viber/messages.js +4 -0
- package/v2Containers/Viber/reducer.js +26 -3
- package/v2Containers/Viber/sagas.js +50 -8
- package/v2Containers/Viber/tests/index.test.js +80 -0
- package/v2Containers/Viber/tests/reducer.test.js +297 -0
- package/v2Containers/Viber/tests/saga.test.js +412 -40
package/package.json
CHANGED
|
@@ -776,8 +776,12 @@ export function SlideBoxContent(props) {
|
|
|
776
776
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
777
777
|
onHtmlEditorValidationStateChange={onHtmlEditorValidationStateChange}
|
|
778
778
|
location={{
|
|
779
|
-
pathname: `/email/edit/${templateData._id}`,
|
|
780
|
-
query: {
|
|
779
|
+
pathname: `/email/edit/${templateData._id || ''}`,
|
|
780
|
+
query: {
|
|
781
|
+
type: 'embedded',
|
|
782
|
+
module: 'library',
|
|
783
|
+
...(templateData._id ? { id: templateData._id } : {}),
|
|
784
|
+
},
|
|
781
785
|
}}
|
|
782
786
|
params={{ id: templateData._id }}
|
|
783
787
|
/>
|
|
@@ -708,7 +708,10 @@ const useEmailWrapper = ({
|
|
|
708
708
|
} else if (location?.pathname?.includes('/edit/')) {
|
|
709
709
|
// Extract id from pathname if it's in the format /edit/:id
|
|
710
710
|
const match = location.pathname.match(/\/edit\/([^/]+)/);
|
|
711
|
-
|
|
711
|
+
// Reject sentinel strings that come from unguarded template literals
|
|
712
|
+
// (e.g. `/edit/${maybeUndefined}` → "/edit/undefined"), so they don't
|
|
713
|
+
// get treated as real template ids downstream.
|
|
714
|
+
if (match && match[1] && match[1] !== 'undefined' && match[1] !== 'null') {
|
|
712
715
|
emailParams.id = match[1];
|
|
713
716
|
}
|
|
714
717
|
}
|
|
@@ -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';
|
|
@@ -419,6 +419,7 @@ export const Viber = (props) => {
|
|
|
419
419
|
channel={VIBER}
|
|
420
420
|
errorMessage={formatMessage(messages.videoErrorMessage)}
|
|
421
421
|
showVideoNameAndDuration
|
|
422
|
+
assetUploading={viber?.assetUploading}
|
|
422
423
|
/>
|
|
423
424
|
);
|
|
424
425
|
|
|
@@ -795,6 +796,10 @@ export const Viber = (props) => {
|
|
|
795
796
|
) {
|
|
796
797
|
return true;
|
|
797
798
|
}
|
|
799
|
+
// block submit while a media asset upload is still in progress
|
|
800
|
+
if ((isMediaTypeImage || isMediaTypeVideo) && viber?.assetUploading) {
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
798
803
|
if (isBtnTypeCta && !isCtaSaved) {
|
|
799
804
|
return true;
|
|
800
805
|
}
|
|
@@ -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
|
-
|
|
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(
|
|
11
|
+
export function* uploadViberAsset(params) {
|
|
8
12
|
try {
|
|
9
|
-
const result = yield call(Api.uploadFile,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
}
|
|
@@ -303,4 +303,84 @@ describe('Test Viber container', () => {
|
|
|
303
303
|
});
|
|
304
304
|
expect(doneBtn).toBeDisabled();
|
|
305
305
|
});
|
|
306
|
+
|
|
307
|
+
it('disables Done while a replacement video asset is uploading (finding 1)', async () => {
|
|
308
|
+
renderComponent({
|
|
309
|
+
actions: mockActions,
|
|
310
|
+
globalActions: mockGlobalActions,
|
|
311
|
+
templateData: { mode: 'create' },
|
|
312
|
+
viber: {
|
|
313
|
+
uploadedAssetData: {},
|
|
314
|
+
createTemplateInProgress: false,
|
|
315
|
+
templateDetails: templateDetailsVideo,
|
|
316
|
+
// a new upload has started after the template was loaded
|
|
317
|
+
assetUploading: true,
|
|
318
|
+
},
|
|
319
|
+
location: {
|
|
320
|
+
pathname: '/sms/edit',
|
|
321
|
+
query: { type: false, module: 'default' },
|
|
322
|
+
search: '',
|
|
323
|
+
},
|
|
324
|
+
isFullMode: true,
|
|
325
|
+
params: { id: 'test' },
|
|
326
|
+
handleClose: jest.fn(),
|
|
327
|
+
metaEntities,
|
|
328
|
+
getDefaultTags,
|
|
329
|
+
injectedTags,
|
|
330
|
+
});
|
|
331
|
+
const doneBtn = screen.getByRole('button', { name: /done/i });
|
|
332
|
+
expect(doneBtn).toBeDisabled();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('disables Done while an image asset is uploading (finding 1)', async () => {
|
|
336
|
+
renderComponent({
|
|
337
|
+
actions: mockActions,
|
|
338
|
+
globalActions: mockGlobalActions,
|
|
339
|
+
templateData: { mode: 'create' },
|
|
340
|
+
viber: {
|
|
341
|
+
uploadedAssetData: {},
|
|
342
|
+
createTemplateInProgress: false,
|
|
343
|
+
templateDetails: templateDetailsImage,
|
|
344
|
+
assetUploading: true,
|
|
345
|
+
},
|
|
346
|
+
location: {
|
|
347
|
+
pathname: '/sms/edit',
|
|
348
|
+
query: { type: false, module: 'default' },
|
|
349
|
+
search: '',
|
|
350
|
+
},
|
|
351
|
+
isFullMode: true,
|
|
352
|
+
params: { id: 'test' },
|
|
353
|
+
handleClose: jest.fn(),
|
|
354
|
+
});
|
|
355
|
+
const doneBtn = screen.getByRole('button', { name: /done/i });
|
|
356
|
+
expect(doneBtn).toBeDisabled();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('keeps Done enabled when assetUploading is true but media type is text', async () => {
|
|
360
|
+
renderComponent({
|
|
361
|
+
actions: mockActions,
|
|
362
|
+
globalActions: mockGlobalActions,
|
|
363
|
+
templateData: { mode: 'create' },
|
|
364
|
+
viber: {
|
|
365
|
+
uploadedAssetData: {},
|
|
366
|
+
createTemplateInProgress: false,
|
|
367
|
+
templateDetails: templateDetailsText,
|
|
368
|
+
// stale upload flag, but the form has no media so submission is safe
|
|
369
|
+
assetUploading: true,
|
|
370
|
+
},
|
|
371
|
+
location: {
|
|
372
|
+
pathname: '/sms/edit',
|
|
373
|
+
query: { type: false, module: 'default' },
|
|
374
|
+
search: '',
|
|
375
|
+
},
|
|
376
|
+
isFullMode: true,
|
|
377
|
+
params: { id: 'test' },
|
|
378
|
+
handleClose: jest.fn(),
|
|
379
|
+
metaEntities,
|
|
380
|
+
getDefaultTags,
|
|
381
|
+
injectedTags,
|
|
382
|
+
});
|
|
383
|
+
const doneBtn = screen.getByRole('button', { name: /done/i });
|
|
384
|
+
expect(doneBtn).toBeEnabled();
|
|
385
|
+
});
|
|
306
386
|
});
|
|
@@ -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
|
+
});
|
|
@@ -1,50 +1,305 @@
|
|
|
1
1
|
import { expectSaga } from 'redux-saga-test-plan';
|
|
2
|
-
import
|
|
2
|
+
import * as matchers from 'redux-saga-test-plan/matchers';
|
|
3
|
+
import { call } from 'redux-saga/effects';
|
|
3
4
|
import { throwError } from 'redux-saga-test-plan/providers';
|
|
4
5
|
import * as Api from '../../../services/api';
|
|
5
6
|
import * as sagas from '../sagas';
|
|
6
|
-
import { v2ViberSagas } from '../sagas';
|
|
7
7
|
import * as types from '../constants';
|
|
8
|
+
import { pollAssetStatus } from '../../../sagas/assetPolling';
|
|
9
|
+
import { ASSET_STATUS } from '../../../utils/assetStatusConstants';
|
|
10
|
+
import messages from '../messages';
|
|
8
11
|
|
|
9
12
|
describe('Viber Sagas', () => {
|
|
10
|
-
|
|
11
13
|
describe('uploadViberAsset Saga', () => {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
const mockAsset = {
|
|
15
|
+
_id: 'asset-123',
|
|
16
|
+
type: 'IMAGE',
|
|
17
|
+
url: 'https://example.com/image.jpg',
|
|
18
|
+
};
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const mockParams = {
|
|
21
|
+
file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
|
|
22
|
+
assetType: 'image',
|
|
23
|
+
fileParams: {},
|
|
24
|
+
templateType: 0,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
it('handles async upload (202) with polling', () => {
|
|
28
|
+
const mockResponse = {
|
|
29
|
+
status: { code: 202 },
|
|
30
|
+
response: {
|
|
31
|
+
assetId: 'asset-123',
|
|
32
|
+
asset: mockAsset,
|
|
33
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
38
|
+
.provide([
|
|
39
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
40
|
+
[matchers.call.fn(pollAssetStatus), undefined],
|
|
41
|
+
])
|
|
42
|
+
.put({
|
|
43
|
+
type: types.UPLOAD_VIBER_ASSET_PROCESSING,
|
|
44
|
+
payload: {
|
|
45
|
+
assetId: 'asset-123',
|
|
46
|
+
asset: mockAsset,
|
|
47
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
.call.fn(Api.uploadFile)
|
|
51
|
+
.call.fn(pollAssetStatus)
|
|
52
|
+
.run();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('handles async when processingStatus is processing with 200 status', () => {
|
|
56
|
+
const mockResponse = {
|
|
57
|
+
status: { code: 200 },
|
|
58
|
+
response: {
|
|
59
|
+
assetId: 'asset-123',
|
|
60
|
+
asset: mockAsset,
|
|
61
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
62
|
+
},
|
|
20
63
|
};
|
|
21
64
|
|
|
22
|
-
return expectSaga(sagas.uploadViberAsset,
|
|
65
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
23
66
|
.provide([
|
|
24
|
-
[call(Api.uploadFile
|
|
67
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
68
|
+
[matchers.call.fn(pollAssetStatus), undefined],
|
|
69
|
+
])
|
|
70
|
+
.put({
|
|
71
|
+
type: types.UPLOAD_VIBER_ASSET_PROCESSING,
|
|
72
|
+
payload: {
|
|
73
|
+
assetId: 'asset-123',
|
|
74
|
+
asset: mockAsset,
|
|
75
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
.call.fn(pollAssetStatus)
|
|
79
|
+
.run();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('handles sync upload (201) without polling', () => {
|
|
83
|
+
const mockResponse = {
|
|
84
|
+
status: { code: 201 },
|
|
85
|
+
response: {
|
|
86
|
+
asset: mockAsset,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
91
|
+
.provide([
|
|
92
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
25
93
|
])
|
|
26
94
|
.put({
|
|
27
95
|
type: types.UPLOAD_VIBER_ASSET_SUCCESS,
|
|
28
|
-
data:
|
|
29
|
-
statusCode:
|
|
30
|
-
templateType:
|
|
96
|
+
data: mockAsset,
|
|
97
|
+
statusCode: 201,
|
|
98
|
+
templateType: 0,
|
|
31
99
|
})
|
|
100
|
+
.not.call(pollAssetStatus)
|
|
32
101
|
.run();
|
|
33
102
|
});
|
|
34
103
|
|
|
35
|
-
it('
|
|
36
|
-
const
|
|
104
|
+
it('extracts assetId from asset._id when assetId omitted', () => {
|
|
105
|
+
const mockResponse = {
|
|
106
|
+
status: { code: 202 },
|
|
107
|
+
response: {
|
|
108
|
+
asset: { ...mockAsset, _id: 'asset-456' },
|
|
109
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
37
112
|
|
|
38
|
-
return expectSaga(sagas.uploadViberAsset,
|
|
113
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
39
114
|
.provide([
|
|
40
|
-
[call(Api.uploadFile
|
|
115
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
116
|
+
[matchers.call.fn(pollAssetStatus), undefined],
|
|
117
|
+
])
|
|
118
|
+
.put({
|
|
119
|
+
type: types.UPLOAD_VIBER_ASSET_PROCESSING,
|
|
120
|
+
payload: {
|
|
121
|
+
assetId: 'asset-456',
|
|
122
|
+
asset: { ...mockAsset, _id: 'asset-456' },
|
|
123
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
.run();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('dispatches FAILED when assetId missing on async response', () => {
|
|
130
|
+
const mockResponse = {
|
|
131
|
+
status: { code: 202 },
|
|
132
|
+
response: {
|
|
133
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
138
|
+
.provide([
|
|
139
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
140
|
+
])
|
|
141
|
+
.put({
|
|
142
|
+
type: types.UPLOAD_VIBER_ASSET_FAILED,
|
|
143
|
+
payload: {
|
|
144
|
+
assetId: undefined,
|
|
145
|
+
error: messages.assetIdMissingError,
|
|
146
|
+
},
|
|
147
|
+
templateType: 0,
|
|
148
|
+
})
|
|
149
|
+
.not.call(pollAssetStatus)
|
|
150
|
+
.run();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('handles upload error', () => {
|
|
154
|
+
const uploadError = new Error('Upload failed');
|
|
155
|
+
|
|
156
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
157
|
+
.provide([
|
|
158
|
+
[matchers.call.fn(Api.uploadFile), throwError(uploadError)],
|
|
41
159
|
])
|
|
42
160
|
.put({
|
|
43
161
|
type: types.UPLOAD_VIBER_ASSET_FAILURE,
|
|
44
|
-
error
|
|
162
|
+
error: uploadError,
|
|
163
|
+
})
|
|
164
|
+
.run();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('falls back to statusCode on response when status object missing', () => {
|
|
168
|
+
const mockResponse = {
|
|
169
|
+
statusCode: 201,
|
|
170
|
+
response: { asset: mockAsset },
|
|
171
|
+
};
|
|
172
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
173
|
+
.provide([
|
|
174
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
175
|
+
])
|
|
176
|
+
.put({
|
|
177
|
+
type: types.UPLOAD_VIBER_ASSET_SUCCESS,
|
|
178
|
+
data: mockAsset,
|
|
179
|
+
statusCode: 201,
|
|
180
|
+
templateType: 0,
|
|
181
|
+
})
|
|
182
|
+
.run();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('passes undefined statusCode through when no status info present (no default)', () => {
|
|
186
|
+
const mockResponse = { response: { asset: mockAsset } };
|
|
187
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
188
|
+
.provide([
|
|
189
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
190
|
+
])
|
|
191
|
+
.put({
|
|
192
|
+
type: types.UPLOAD_VIBER_ASSET_SUCCESS,
|
|
193
|
+
data: mockAsset,
|
|
194
|
+
statusCode: undefined,
|
|
195
|
+
templateType: 0,
|
|
45
196
|
})
|
|
46
197
|
.run();
|
|
47
198
|
});
|
|
199
|
+
|
|
200
|
+
it('handles empty response object', () => {
|
|
201
|
+
const mockResponse = { status: { code: 201 } };
|
|
202
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
203
|
+
.provide([
|
|
204
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
205
|
+
])
|
|
206
|
+
.put({
|
|
207
|
+
type: types.UPLOAD_VIBER_ASSET_SUCCESS,
|
|
208
|
+
data: {},
|
|
209
|
+
statusCode: 201,
|
|
210
|
+
templateType: 0,
|
|
211
|
+
})
|
|
212
|
+
.run();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('normalizes a flat async response (asset fields at top level) for polling', () => {
|
|
216
|
+
// API returns a flat asset object instead of { asset: {...} }
|
|
217
|
+
const flatResponse = {
|
|
218
|
+
status: { code: 202 },
|
|
219
|
+
response: {
|
|
220
|
+
_id: 'flat-asset-789',
|
|
221
|
+
type: 'VIDEO',
|
|
222
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
return expectSaga(sagas.uploadViberAsset, { ...mockParams, assetType: undefined })
|
|
226
|
+
.provide([
|
|
227
|
+
[matchers.call.fn(Api.uploadFile), flatResponse],
|
|
228
|
+
[matchers.call.fn(pollAssetStatus), undefined],
|
|
229
|
+
])
|
|
230
|
+
.put({
|
|
231
|
+
type: types.UPLOAD_VIBER_ASSET_PROCESSING,
|
|
232
|
+
payload: {
|
|
233
|
+
assetId: 'flat-asset-789',
|
|
234
|
+
asset: flatResponse.response,
|
|
235
|
+
processingStatus: ASSET_STATUS.PROCESSING,
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
.call.fn(pollAssetStatus)
|
|
239
|
+
.run();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('normalizes a flat sync response (asset fields at top level)', () => {
|
|
243
|
+
const flatAsset = { _id: 'flat-sync-1', url: 'https://cdn/x.jpg' };
|
|
244
|
+
const flatResponse = {
|
|
245
|
+
status: { code: 201 },
|
|
246
|
+
response: flatAsset,
|
|
247
|
+
};
|
|
248
|
+
return expectSaga(sagas.uploadViberAsset, mockParams)
|
|
249
|
+
.provide([
|
|
250
|
+
[matchers.call.fn(Api.uploadFile), flatResponse],
|
|
251
|
+
])
|
|
252
|
+
.put({
|
|
253
|
+
type: types.UPLOAD_VIBER_ASSET_SUCCESS,
|
|
254
|
+
data: flatAsset,
|
|
255
|
+
statusCode: 201,
|
|
256
|
+
templateType: 0,
|
|
257
|
+
})
|
|
258
|
+
.run();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('derives assetType from asset.type when params.assetType omitted', () => {
|
|
262
|
+
const paramsWithoutType = {
|
|
263
|
+
file: new File(['v'], 'v.mp4', { type: 'video/mp4' }),
|
|
264
|
+
fileParams: {},
|
|
265
|
+
templateType: 1,
|
|
266
|
+
};
|
|
267
|
+
const mockResponse = {
|
|
268
|
+
status: { code: 202 },
|
|
269
|
+
response: {
|
|
270
|
+
assetId: 'asset-vid',
|
|
271
|
+
asset: { _id: 'asset-vid', type: 'VIDEO' },
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
return expectSaga(sagas.uploadViberAsset, paramsWithoutType)
|
|
275
|
+
.provide([
|
|
276
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
277
|
+
[matchers.call.fn(pollAssetStatus), undefined],
|
|
278
|
+
])
|
|
279
|
+
.call.fn(pollAssetStatus)
|
|
280
|
+
.run();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('defaults assetType to image when no type info available', () => {
|
|
284
|
+
const paramsWithoutType = {
|
|
285
|
+
file: new File(['x'], 'x', { type: '' }),
|
|
286
|
+
fileParams: {},
|
|
287
|
+
templateType: 0,
|
|
288
|
+
};
|
|
289
|
+
const mockResponse = {
|
|
290
|
+
status: { code: 202 },
|
|
291
|
+
response: {
|
|
292
|
+
assetId: 'asset-noop',
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
return expectSaga(sagas.uploadViberAsset, paramsWithoutType)
|
|
296
|
+
.provide([
|
|
297
|
+
[matchers.call.fn(Api.uploadFile), mockResponse],
|
|
298
|
+
[matchers.call.fn(pollAssetStatus), undefined],
|
|
299
|
+
])
|
|
300
|
+
.call.fn(pollAssetStatus)
|
|
301
|
+
.run();
|
|
302
|
+
});
|
|
48
303
|
});
|
|
49
304
|
|
|
50
305
|
describe('createViberTemplate Saga', () => {
|
|
@@ -54,18 +309,18 @@ describe('Viber Sagas', () => {
|
|
|
54
309
|
it('handles creating template successfully', () => {
|
|
55
310
|
const fakeResponse = {
|
|
56
311
|
response: { id: 2, content: template.content },
|
|
57
|
-
status: { code: 201 }
|
|
312
|
+
status: { code: 201 },
|
|
58
313
|
};
|
|
59
314
|
|
|
60
315
|
return expectSaga(sagas.createViberTemplate, { template, callback })
|
|
61
316
|
.provide([
|
|
62
|
-
[call(Api.createViberTemplate, { template }), fakeResponse]
|
|
317
|
+
[call(Api.createViberTemplate, { template }), fakeResponse],
|
|
63
318
|
])
|
|
64
319
|
.put({
|
|
65
320
|
type: types.CREATE_VIBER_TEMPLATE_SUCCESS,
|
|
66
321
|
data: fakeResponse.response,
|
|
67
322
|
statusCode: fakeResponse.status.code,
|
|
68
|
-
errorMsg: undefined
|
|
323
|
+
errorMsg: undefined,
|
|
69
324
|
})
|
|
70
325
|
.run()
|
|
71
326
|
.then(() => {
|
|
@@ -76,21 +331,74 @@ describe('Viber Sagas', () => {
|
|
|
76
331
|
it('handles failure in creating template', () => {
|
|
77
332
|
const error = new Error({ message: 'Creation failed', status: { code: 400 } });
|
|
78
333
|
|
|
79
|
-
const errorMsg = 'Creation failed';
|
|
80
334
|
return expectSaga(sagas.createViberTemplate, { template, callback })
|
|
81
335
|
.provide([
|
|
82
|
-
[call(Api.createViberTemplate, { template }), throwError(error)]
|
|
336
|
+
[call(Api.createViberTemplate, { template }), throwError(error)],
|
|
83
337
|
])
|
|
84
338
|
.put({
|
|
85
339
|
type: types.CREATE_VIBER_TEMPLATE_FAILURE,
|
|
86
340
|
error,
|
|
87
|
-
errorMsg
|
|
341
|
+
errorMsg: undefined,
|
|
88
342
|
})
|
|
89
343
|
.run()
|
|
90
344
|
.then(() => {
|
|
91
345
|
expect(callback).toHaveBeenCalledWith(null, undefined);
|
|
92
346
|
});
|
|
93
347
|
});
|
|
348
|
+
|
|
349
|
+
it('handles 4xx API response (treated as failure)', () => {
|
|
350
|
+
const cb = jest.fn();
|
|
351
|
+
const fakeResponse = {
|
|
352
|
+
response: {},
|
|
353
|
+
status: { code: 400 },
|
|
354
|
+
message: 'bad request',
|
|
355
|
+
};
|
|
356
|
+
return expectSaga(sagas.createViberTemplate, { template, callback: cb })
|
|
357
|
+
.provide([
|
|
358
|
+
[call(Api.createViberTemplate, { template }), fakeResponse],
|
|
359
|
+
])
|
|
360
|
+
.put({
|
|
361
|
+
type: types.CREATE_VIBER_TEMPLATE_FAILURE,
|
|
362
|
+
error: 'bad request',
|
|
363
|
+
errorMsg: 'bad request',
|
|
364
|
+
})
|
|
365
|
+
.run()
|
|
366
|
+
.then(() => {
|
|
367
|
+
expect(cb).toHaveBeenCalledWith(null, 'bad request');
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('handles success when no callback is supplied', () => {
|
|
372
|
+
const fakeResponse = {
|
|
373
|
+
response: { id: 42 },
|
|
374
|
+
status: { code: 201 },
|
|
375
|
+
};
|
|
376
|
+
return expectSaga(sagas.createViberTemplate, { template })
|
|
377
|
+
.provide([
|
|
378
|
+
[call(Api.createViberTemplate, { template }), fakeResponse],
|
|
379
|
+
])
|
|
380
|
+
.put({
|
|
381
|
+
type: types.CREATE_VIBER_TEMPLATE_SUCCESS,
|
|
382
|
+
data: fakeResponse.response,
|
|
383
|
+
statusCode: 201,
|
|
384
|
+
errorMsg: undefined,
|
|
385
|
+
})
|
|
386
|
+
.run();
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('handles failure when no callback is supplied', () => {
|
|
390
|
+
const error = new Error('Creation failed');
|
|
391
|
+
return expectSaga(sagas.createViberTemplate, { template })
|
|
392
|
+
.provide([
|
|
393
|
+
[call(Api.createViberTemplate, { template }), throwError(error)],
|
|
394
|
+
])
|
|
395
|
+
.put({
|
|
396
|
+
type: types.CREATE_VIBER_TEMPLATE_FAILURE,
|
|
397
|
+
error,
|
|
398
|
+
errorMsg: undefined,
|
|
399
|
+
})
|
|
400
|
+
.run();
|
|
401
|
+
});
|
|
94
402
|
});
|
|
95
403
|
|
|
96
404
|
describe('editTemplate Saga', () => {
|
|
@@ -100,18 +408,18 @@ describe('Viber Sagas', () => {
|
|
|
100
408
|
it('handles editing template successfully', () => {
|
|
101
409
|
const fakeResponse = {
|
|
102
410
|
response: { updated: true },
|
|
103
|
-
status: { code: 200 }
|
|
411
|
+
status: { code: 200 },
|
|
104
412
|
};
|
|
105
413
|
|
|
106
414
|
return expectSaga(sagas.editTemplate, { template, callback })
|
|
107
415
|
.provide([
|
|
108
|
-
[call(Api.createViberTemplate, { template }), fakeResponse]
|
|
416
|
+
[call(Api.createViberTemplate, { template }), fakeResponse],
|
|
109
417
|
])
|
|
110
418
|
.put({
|
|
111
419
|
type: types.EDIT_VIBER_TEMPLATE_SUCCESS,
|
|
112
420
|
data: fakeResponse.response,
|
|
113
421
|
statusCode: fakeResponse.status.code,
|
|
114
|
-
errorMsg: undefined
|
|
422
|
+
errorMsg: undefined,
|
|
115
423
|
})
|
|
116
424
|
.run()
|
|
117
425
|
.then(() => {
|
|
@@ -123,7 +431,7 @@ describe('Viber Sagas', () => {
|
|
|
123
431
|
const errorMsg = 'Error in editing template';
|
|
124
432
|
return expectSaga(sagas.editTemplate, { template, callback })
|
|
125
433
|
.provide([
|
|
126
|
-
[call(Api.createViberTemplate, { template }), throwError({ message: errorMsg, status: { code: 400 } })]
|
|
434
|
+
[call(Api.createViberTemplate, { template }), throwError({ message: errorMsg, status: { code: 400 } })],
|
|
127
435
|
])
|
|
128
436
|
.put({
|
|
129
437
|
type: types.EDIT_VIBER_TEMPLATE_FAILURE,
|
|
@@ -135,6 +443,60 @@ describe('Viber Sagas', () => {
|
|
|
135
443
|
expect(callback).toHaveBeenCalledWith(null, undefined);
|
|
136
444
|
});
|
|
137
445
|
});
|
|
446
|
+
|
|
447
|
+
it('handles 4xx API response (treated as failure)', () => {
|
|
448
|
+
const cb = jest.fn();
|
|
449
|
+
const fakeResponse = {
|
|
450
|
+
response: {},
|
|
451
|
+
status: { code: 422 },
|
|
452
|
+
message: 'invalid payload',
|
|
453
|
+
};
|
|
454
|
+
return expectSaga(sagas.editTemplate, { template, callback: cb })
|
|
455
|
+
.provide([
|
|
456
|
+
[call(Api.createViberTemplate, { template }), fakeResponse],
|
|
457
|
+
])
|
|
458
|
+
.put({
|
|
459
|
+
type: types.EDIT_VIBER_TEMPLATE_FAILURE,
|
|
460
|
+
error: 'invalid payload',
|
|
461
|
+
errorMsg: 'invalid payload',
|
|
462
|
+
})
|
|
463
|
+
.run()
|
|
464
|
+
.then(() => {
|
|
465
|
+
expect(cb).toHaveBeenCalledWith(null, 'invalid payload');
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('handles success when no callback is supplied', () => {
|
|
470
|
+
const fakeResponse = {
|
|
471
|
+
response: { updated: true },
|
|
472
|
+
status: { code: 200 },
|
|
473
|
+
};
|
|
474
|
+
return expectSaga(sagas.editTemplate, { template })
|
|
475
|
+
.provide([
|
|
476
|
+
[call(Api.createViberTemplate, { template }), fakeResponse],
|
|
477
|
+
])
|
|
478
|
+
.put({
|
|
479
|
+
type: types.EDIT_VIBER_TEMPLATE_SUCCESS,
|
|
480
|
+
data: fakeResponse.response,
|
|
481
|
+
statusCode: 200,
|
|
482
|
+
errorMsg: undefined,
|
|
483
|
+
})
|
|
484
|
+
.run();
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('handles failure when no callback is supplied', () => {
|
|
488
|
+
const error = new Error('Edit failed');
|
|
489
|
+
return expectSaga(sagas.editTemplate, { template })
|
|
490
|
+
.provide([
|
|
491
|
+
[call(Api.createViberTemplate, { template }), throwError(error)],
|
|
492
|
+
])
|
|
493
|
+
.put({
|
|
494
|
+
type: types.EDIT_VIBER_TEMPLATE_FAILURE,
|
|
495
|
+
error,
|
|
496
|
+
errorMsg: undefined,
|
|
497
|
+
})
|
|
498
|
+
.run();
|
|
499
|
+
});
|
|
138
500
|
});
|
|
139
501
|
|
|
140
502
|
describe('getTemplateDetails Saga', () => {
|
|
@@ -143,16 +505,16 @@ describe('Viber Sagas', () => {
|
|
|
143
505
|
|
|
144
506
|
it('handles fetching template details successfully', () => {
|
|
145
507
|
const fakeResponse = {
|
|
146
|
-
response: { id
|
|
508
|
+
response: { id, name: 'Detailed Template' },
|
|
147
509
|
};
|
|
148
510
|
|
|
149
511
|
return expectSaga(sagas.getTemplateDetails, { id, callback })
|
|
150
512
|
.provide([
|
|
151
|
-
[call(Api.getTemplateDetails, { id, channel: 'VIBER' }), fakeResponse]
|
|
513
|
+
[call(Api.getTemplateDetails, { id, channel: 'VIBER' }), fakeResponse],
|
|
152
514
|
])
|
|
153
515
|
.put({
|
|
154
516
|
type: types.GET_VIBER_TEMPLATE_DETAILS_SUCCESS,
|
|
155
|
-
data: fakeResponse.response
|
|
517
|
+
data: fakeResponse.response,
|
|
156
518
|
})
|
|
157
519
|
.run()
|
|
158
520
|
.then(() => {
|
|
@@ -165,23 +527,33 @@ describe('Viber Sagas', () => {
|
|
|
165
527
|
|
|
166
528
|
return expectSaga(sagas.getTemplateDetails, { id, callback })
|
|
167
529
|
.provide([
|
|
168
|
-
[call(Api.getTemplateDetails, { id, channel: 'VIBER' }), throwError(error)]
|
|
530
|
+
[call(Api.getTemplateDetails, { id, channel: 'VIBER' }), throwError(error)],
|
|
169
531
|
])
|
|
170
532
|
.put({
|
|
171
533
|
type: types.GET_VIBER_TEMPLATE_DETAILS_FAILURE,
|
|
172
|
-
error
|
|
534
|
+
error,
|
|
173
535
|
})
|
|
174
536
|
.run()
|
|
175
537
|
.then(() => {
|
|
176
538
|
expect(callback).not.toHaveBeenCalledWith();
|
|
177
539
|
});
|
|
178
540
|
});
|
|
179
|
-
});
|
|
180
541
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return expectSaga(
|
|
542
|
+
it('handles success without a callback', () => {
|
|
543
|
+
const fakeResponse = { response: { id, name: 'No CB' } };
|
|
544
|
+
return expectSaga(sagas.getTemplateDetails, { id })
|
|
545
|
+
.provide([
|
|
546
|
+
[call(Api.getTemplateDetails, { id, channel: 'VIBER' }), fakeResponse],
|
|
547
|
+
])
|
|
548
|
+
.put({
|
|
549
|
+
type: types.GET_VIBER_TEMPLATE_DETAILS_SUCCESS,
|
|
550
|
+
data: fakeResponse.response,
|
|
551
|
+
})
|
|
552
|
+
.run();
|
|
184
553
|
});
|
|
185
554
|
});
|
|
186
555
|
|
|
187
|
-
|
|
556
|
+
describe('v2ViberSagas Combined', () => {
|
|
557
|
+
it('should initialize all Viber-related watcher sagas without error', () => expectSaga(sagas.v2ViberSagas).run());
|
|
558
|
+
});
|
|
559
|
+
});
|