@capillarytech/creatives-library 8.0.358 → 8.0.359-alpha.1
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/constants/unified.js +29 -0
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/rcsPayloadUtils.test.js +226 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +166 -108
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +346 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +150 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +810 -222
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/FormBuilder/index.js +5 -4
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +956 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +37 -22
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -31
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
- package/v2Containers/CreativesContainer/index.js +322 -103
- package/v2Containers/CreativesContainer/index.scss +83 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +120 -11
- package/v2Containers/Rcs/index.js +2577 -812
- package/v2Containers/Rcs/index.scss +281 -8
- package/v2Containers/Rcs/messages.js +34 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98036 -70145
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/index.test.js +152 -121
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +106 -40
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +640 -130
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +166 -9
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +121 -53
- package/v2Containers/Templates/sagas.js +56 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { expectSaga } from 'redux-saga-test-plan';
|
|
2
|
+
import {
|
|
3
|
+
take, call, takeLatest, takeEvery, put,
|
|
4
|
+
} from 'redux-saga/effects';
|
|
2
5
|
import * as matchers from 'redux-saga-test-plan/matchers';
|
|
3
6
|
import { throwError } from 'redux-saga-test-plan/providers';
|
|
4
|
-
import { call, takeLatest, put } from 'redux-saga/effects';
|
|
5
7
|
import * as api from '../../../services/api';
|
|
6
8
|
import * as types from '../constants';
|
|
7
9
|
import * as cdnUtils from '../../../utils/cdnTransformation';
|
|
8
10
|
import {
|
|
9
11
|
getCdnTransformationConfig,
|
|
10
12
|
getAllTemplates,
|
|
13
|
+
getLocalSmsTemplates,
|
|
11
14
|
watchGetCdnTransformationConfig,
|
|
12
15
|
watchGetAllTemplates,
|
|
13
16
|
getSenderDetails,
|
|
14
17
|
fetchWeCrmAccounts,
|
|
15
18
|
sendZippedFile,
|
|
16
19
|
fetchUserList,
|
|
17
|
-
watchGetUserList,
|
|
18
20
|
deleteRcsTemplate,
|
|
19
21
|
watchDeleteRcsTemplate,
|
|
20
22
|
watchForGetTemplateInfoById,
|
|
@@ -39,10 +41,14 @@ import {
|
|
|
39
41
|
watchUnarchiveTemplate,
|
|
40
42
|
watchBulkArchive,
|
|
41
43
|
watchBulkUnarchive,
|
|
44
|
+
watchGetUserList,
|
|
42
45
|
} from '../sagas';
|
|
43
|
-
|
|
44
|
-
import { ZALO_TEMPLATE_INFO_REQUEST } from '../../Zalo/constants';
|
|
46
|
+
|
|
45
47
|
import * as mockData from './mockData';
|
|
48
|
+
import { fetchSmsTemplatesFromQuery } from '../utils/smsTemplatesListApi';
|
|
49
|
+
import { ZALO } from '../../CreativesContainer/constants';
|
|
50
|
+
import { VIET_GUYS, ZALO_TEMPLATE_INFO_REQUEST } from '../../Zalo/constants';
|
|
51
|
+
import { getTemplateInfoById } from '../../Zalo/saga';
|
|
46
52
|
|
|
47
53
|
jest.mock('@capillarytech/cap-ui-library', () => ({
|
|
48
54
|
CapNotification: {
|
|
@@ -167,8 +173,8 @@ describe('templateList saga', () => {
|
|
|
167
173
|
],
|
|
168
174
|
]).put({
|
|
169
175
|
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
170
|
-
data: mockData.getAllTemplatesListSuccess,
|
|
171
|
-
weCRMTemplate: mockData.getAllTemplatesListSuccess,
|
|
176
|
+
data: mockData.getAllTemplatesListSuccess.response,
|
|
177
|
+
weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
|
|
172
178
|
isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
|
|
173
179
|
})
|
|
174
180
|
.run();
|
|
@@ -184,8 +190,8 @@ describe('templateList saga', () => {
|
|
|
184
190
|
],
|
|
185
191
|
]).put({
|
|
186
192
|
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
187
|
-
data: mockData.getAllTemplatesListSuccess,
|
|
188
|
-
weCRMTemplate: mockData.getAllTemplatesListSuccess,
|
|
193
|
+
data: mockData.getAllTemplatesListSuccess.response,
|
|
194
|
+
weCRMTemplate: mockData.getAllTemplatesListSuccess.response?.unMapped,
|
|
189
195
|
isReset: mockData.getAllTemplatesListSuccess?.queryParams?.page === 1,
|
|
190
196
|
})
|
|
191
197
|
.run();
|
|
@@ -204,6 +210,163 @@ describe('templateList saga', () => {
|
|
|
204
210
|
})
|
|
205
211
|
.run();
|
|
206
212
|
});
|
|
213
|
+
|
|
214
|
+
it('SMS channel uses fetchSmsTemplatesFromQuery', () => {
|
|
215
|
+
const smsAction = {
|
|
216
|
+
type: types.GET_ALL_TEMPLATES_REQUEST,
|
|
217
|
+
channel: 'Sms',
|
|
218
|
+
queryParams: { page: 1, perPage: 25, name: '', sortBy: 'Most Recent' },
|
|
219
|
+
intlCopyOf: 'Kopie',
|
|
220
|
+
};
|
|
221
|
+
const fetched = {
|
|
222
|
+
channelTemplates: { templates: [{ name: 'Kopie Foo' }], totalCount: 1 },
|
|
223
|
+
weCRMTemplate: undefined,
|
|
224
|
+
raw: {},
|
|
225
|
+
};
|
|
226
|
+
return expectSaga(getAllTemplates, smsAction)
|
|
227
|
+
.provide([
|
|
228
|
+
[call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), fetched],
|
|
229
|
+
])
|
|
230
|
+
.put({
|
|
231
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
232
|
+
data: fetched.channelTemplates,
|
|
233
|
+
weCRMTemplate: fetched.weCRMTemplate,
|
|
234
|
+
isReset: true,
|
|
235
|
+
})
|
|
236
|
+
.run();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('SMS channel propagates fetchSmsTemplatesFromQuery errors', () => {
|
|
240
|
+
const smsAction = {
|
|
241
|
+
type: types.GET_ALL_TEMPLATES_REQUEST,
|
|
242
|
+
channel: 'SMS',
|
|
243
|
+
queryParams: { page: 2, perPage: 25 },
|
|
244
|
+
intlCopyOf: '',
|
|
245
|
+
};
|
|
246
|
+
const error = new Error('sms list fail');
|
|
247
|
+
return expectSaga(getAllTemplates, smsAction)
|
|
248
|
+
.provide([
|
|
249
|
+
[call(fetchSmsTemplatesFromQuery, smsAction.queryParams, smsAction.intlCopyOf), throwError(error)],
|
|
250
|
+
])
|
|
251
|
+
.put({
|
|
252
|
+
type: types.GET_ALL_TEMPLATES_FAILURE,
|
|
253
|
+
error,
|
|
254
|
+
})
|
|
255
|
+
.run();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('wechat channel merges mapped and richmedia and applies Most Recent sort', () => {
|
|
259
|
+
const wechatApiResponse = {
|
|
260
|
+
response: {
|
|
261
|
+
mapped: [
|
|
262
|
+
{ name: 'B', updatedAt: '2024-01-02T00:00:00Z' },
|
|
263
|
+
{ name: 'A', updatedAt: '2024-01-01T00:00:00Z' },
|
|
264
|
+
],
|
|
265
|
+
richmedia: [{ name: 'C', updatedAt: '2024-01-03T00:00:00Z' }],
|
|
266
|
+
unMapped: [],
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
const action = {
|
|
270
|
+
channel: 'wechat',
|
|
271
|
+
queryParams: { sortBy: 'Most Recent', page: 1 },
|
|
272
|
+
intlCopyOf: '',
|
|
273
|
+
};
|
|
274
|
+
return expectSaga(getAllTemplates, action)
|
|
275
|
+
.provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
|
|
276
|
+
.put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
|
|
277
|
+
.run();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('wechat channel applies Alphabetically sort', () => {
|
|
281
|
+
const wechatApiResponse = {
|
|
282
|
+
response: {
|
|
283
|
+
mapped: [{ name: 'Zebra', updatedAt: '2024-01-02T00:00:00Z' }],
|
|
284
|
+
richmedia: [{ name: 'Apple', updatedAt: '2024-01-01T00:00:00Z' }],
|
|
285
|
+
unMapped: [],
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
const action = {
|
|
289
|
+
channel: 'wechat',
|
|
290
|
+
queryParams: { sortBy: 'Alphabetically', page: 1 },
|
|
291
|
+
intlCopyOf: '',
|
|
292
|
+
};
|
|
293
|
+
return expectSaga(getAllTemplates, action)
|
|
294
|
+
.provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
|
|
295
|
+
.put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
|
|
296
|
+
.run();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('wechat channel replaces Copy-of prefix when intlCopyOf is set', () => {
|
|
300
|
+
const wechatApiResponse = {
|
|
301
|
+
response: {
|
|
302
|
+
mapped: [{ name: 'Copy of Promo', updatedAt: '2024-01-01T00:00:00Z' }],
|
|
303
|
+
richmedia: [],
|
|
304
|
+
unMapped: [],
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
const action = {
|
|
308
|
+
channel: 'wechat',
|
|
309
|
+
queryParams: { page: 1 },
|
|
310
|
+
intlCopyOf: 'Kopia av ',
|
|
311
|
+
};
|
|
312
|
+
return expectSaga(getAllTemplates, action)
|
|
313
|
+
.provide([[matchers.call.fn(api.getAllTemplates), wechatApiResponse]])
|
|
314
|
+
.put.actionType(types.GET_ALL_TEMPLATES_SUCCESS)
|
|
315
|
+
.run();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe('getLocalSmsTemplates saga', () => {
|
|
320
|
+
it('invokes onSuccess with fetched data', () => {
|
|
321
|
+
const onSuccess = jest.fn();
|
|
322
|
+
const action = {
|
|
323
|
+
queryParams: { page: 1 },
|
|
324
|
+
intlCopyOf: '',
|
|
325
|
+
onSuccess,
|
|
326
|
+
};
|
|
327
|
+
const fetched = { channelTemplates: { templates: [] }, raw: {} };
|
|
328
|
+
return expectSaga(getLocalSmsTemplates, action)
|
|
329
|
+
.provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
|
|
330
|
+
.call(onSuccess, fetched)
|
|
331
|
+
.run();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('invokes onFailure when fetch throws', () => {
|
|
335
|
+
const onFailure = jest.fn();
|
|
336
|
+
const error = new Error('local list fail');
|
|
337
|
+
const action = {
|
|
338
|
+
queryParams: { page: 1 },
|
|
339
|
+
intlCopyOf: 'Kopie',
|
|
340
|
+
onFailure,
|
|
341
|
+
};
|
|
342
|
+
return expectSaga(getLocalSmsTemplates, action)
|
|
343
|
+
.provide([
|
|
344
|
+
[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(error)],
|
|
345
|
+
])
|
|
346
|
+
.call(onFailure, error)
|
|
347
|
+
.run();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('does not throw when callbacks are omitted', () => {
|
|
351
|
+
const action = { queryParams: { page: 1 }, intlCopyOf: '' };
|
|
352
|
+
return expectSaga(getLocalSmsTemplates, action)
|
|
353
|
+
.provide([
|
|
354
|
+
[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), throwError(new Error('x'))],
|
|
355
|
+
])
|
|
356
|
+
.run();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('does not call onSuccess when it is not a function', () => {
|
|
360
|
+
const action = {
|
|
361
|
+
queryParams: { page: 1 },
|
|
362
|
+
intlCopyOf: '',
|
|
363
|
+
onSuccess: 'not-a-fn',
|
|
364
|
+
};
|
|
365
|
+
const fetched = { channelTemplates: { templates: [] } };
|
|
366
|
+
return expectSaga(getLocalSmsTemplates, action)
|
|
367
|
+
.provide([[call(fetchSmsTemplatesFromQuery, action.queryParams, action.intlCopyOf), fetched]])
|
|
368
|
+
.run();
|
|
369
|
+
});
|
|
207
370
|
});
|
|
208
371
|
|
|
209
372
|
describe('watchForTemplates saga', () => {
|
|
@@ -397,7 +560,7 @@ describe('getSenderDetails Saga', () => {
|
|
|
397
560
|
|
|
398
561
|
describe('fetchWeCrmAccounts Saga', () => {
|
|
399
562
|
const action = { source: 'CRM' };
|
|
400
|
-
|
|
563
|
+
it('handles successful fetching of WeCRM accounts', () => {
|
|
401
564
|
const fakeResponse = {
|
|
402
565
|
response: [{ id: 1, name: 'Account One' }]
|
|
403
566
|
};
|
|
@@ -410,14 +573,11 @@ describe('fetchWeCrmAccounts Saga', () => {
|
|
|
410
573
|
})
|
|
411
574
|
);
|
|
412
575
|
});
|
|
413
|
-
|
|
576
|
+
it('handles error thrown from api', () => {
|
|
414
577
|
const error = new Error('Fetch failed');
|
|
415
|
-
expectSaga(fetchWeCrmAccounts, action
|
|
578
|
+
expectSaga(fetchWeCrmAccounts, action)
|
|
416
579
|
.provide([
|
|
417
|
-
[
|
|
418
|
-
call(api.fetchWeCrmAccounts),
|
|
419
|
-
throwError(error)
|
|
420
|
-
],
|
|
580
|
+
[call(api.fetchWeCrmAccounts, action.source), throwError(error)],
|
|
421
581
|
])
|
|
422
582
|
.put({
|
|
423
583
|
type: types.GET_WECRM_ACCOUNTS_FAILURE,
|
|
@@ -459,6 +619,29 @@ describe('sendZippedFile Saga', () => {
|
|
|
459
619
|
expect(generator.next().value).toEqual(call(api.sendZippedFile, action.selectedFile));
|
|
460
620
|
});
|
|
461
621
|
|
|
622
|
+
it('calls errorHandler with message and returns early when isError is true', () => {
|
|
623
|
+
const errorHandler = jest.fn();
|
|
624
|
+
const successHandler = jest.fn();
|
|
625
|
+
const errorAction = {
|
|
626
|
+
selectedFile: 'upload.zip',
|
|
627
|
+
errorHandler,
|
|
628
|
+
successHandler,
|
|
629
|
+
};
|
|
630
|
+
const fakeErrorResponse = {
|
|
631
|
+
status: { isError: true },
|
|
632
|
+
message: 'Upload failed',
|
|
633
|
+
};
|
|
634
|
+
const generator = sendZippedFile(errorAction);
|
|
635
|
+
expect(generator.next().value).toEqual(call(api.sendZippedFile, errorAction.selectedFile));
|
|
636
|
+
// Advancing with isError response causes the saga to call errorHandler and yield
|
|
637
|
+
const yieldStep = generator.next(fakeErrorResponse);
|
|
638
|
+
expect(errorHandler).toHaveBeenCalledWith('Upload failed');
|
|
639
|
+
expect(yieldStep.done).toBe(false);
|
|
640
|
+
// After resuming from the yield, the return statement completes the generator
|
|
641
|
+
expect(generator.next().done).toBe(true);
|
|
642
|
+
expect(successHandler).not.toHaveBeenCalled();
|
|
643
|
+
});
|
|
644
|
+
|
|
462
645
|
it('handles error thrown from api', () => {
|
|
463
646
|
const error = new Error('Fetch failed');
|
|
464
647
|
expectSaga(sendZippedFile, action)
|
|
@@ -1224,7 +1407,7 @@ describe('v2TemplateSaga', () => {
|
|
|
1224
1407
|
// all() returns an IO object with an ALL key containing the array of effects
|
|
1225
1408
|
expect(step.value).toHaveProperty('@@redux-saga/IO', true);
|
|
1226
1409
|
expect(step.value).toHaveProperty('ALL');
|
|
1227
|
-
expect(step.value.ALL).toHaveLength(
|
|
1410
|
+
expect(step.value.ALL).toHaveLength(15);
|
|
1228
1411
|
expect(step.done).toBe(false);
|
|
1229
1412
|
});
|
|
1230
1413
|
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as Api from '../../../services/api';
|
|
2
|
+
import { isTraiDLTEnable } from '../../../utils/common';
|
|
3
|
+
|
|
4
|
+
jest.mock('../../../services/api', () => ({
|
|
5
|
+
getAllTemplates: jest.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
jest.mock('../../../utils/common', () => ({
|
|
9
|
+
isTraiDLTEnable: jest.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
buildSmsTemplatesListQueryParams,
|
|
14
|
+
fetchSmsTemplatesFromQuery,
|
|
15
|
+
fetchSmsTemplatesListPage,
|
|
16
|
+
SMS_TEMPLATES_LIST_SORT_MOST_RECENT,
|
|
17
|
+
} from '../utils/smsTemplatesListApi';
|
|
18
|
+
|
|
19
|
+
describe('smsTemplatesListApi', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('buildSmsTemplatesListQueryParams', () => {
|
|
25
|
+
it('includes traiEnable when TRAI DLT is enabled', () => {
|
|
26
|
+
isTraiDLTEnable.mockReturnValue(true);
|
|
27
|
+
const q = buildSmsTemplatesListQueryParams({
|
|
28
|
+
page: 1,
|
|
29
|
+
perPage: 25,
|
|
30
|
+
name: 'x',
|
|
31
|
+
sortBy: SMS_TEMPLATES_LIST_SORT_MOST_RECENT,
|
|
32
|
+
isFullMode: true,
|
|
33
|
+
smsRegister: {},
|
|
34
|
+
});
|
|
35
|
+
expect(q).toEqual({
|
|
36
|
+
page: 1,
|
|
37
|
+
perPage: 25,
|
|
38
|
+
sortBy: SMS_TEMPLATES_LIST_SORT_MOST_RECENT,
|
|
39
|
+
name: 'x',
|
|
40
|
+
traiEnable: true,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('omits traiEnable when TRAI DLT is disabled', () => {
|
|
45
|
+
isTraiDLTEnable.mockReturnValue(false);
|
|
46
|
+
const q = buildSmsTemplatesListQueryParams({
|
|
47
|
+
page: 2,
|
|
48
|
+
perPage: 10,
|
|
49
|
+
isFullMode: false,
|
|
50
|
+
smsRegister: null,
|
|
51
|
+
});
|
|
52
|
+
expect(q.traiEnable).toBeUndefined();
|
|
53
|
+
expect(q.name).toBe('');
|
|
54
|
+
expect(q.sortBy).toBe(SMS_TEMPLATES_LIST_SORT_MOST_RECENT);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('fetchSmsTemplatesFromQuery', () => {
|
|
59
|
+
it('returns channelTemplates, weCRMTemplate, and raw; maps intl copy on names', async () => {
|
|
60
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
61
|
+
response: {
|
|
62
|
+
templates: [{ name: 'Copy of A', _id: '1' }],
|
|
63
|
+
unMapped: { u: 1 },
|
|
64
|
+
totalCount: 5,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const out = await fetchSmsTemplatesFromQuery(
|
|
69
|
+
{ page: 1, perPage: 25 },
|
|
70
|
+
'Kopie',
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
expect(Api.getAllTemplates).toHaveBeenCalledWith({
|
|
74
|
+
channel: 'Sms',
|
|
75
|
+
queryParams: { page: 1, perPage: 25 },
|
|
76
|
+
});
|
|
77
|
+
expect(out.weCRMTemplate).toEqual({ u: 1 });
|
|
78
|
+
expect(out.raw.response.templates[0].name).toBe('Copy of A');
|
|
79
|
+
expect(out.channelTemplates.templates[0].name).toBe('Kopie A');
|
|
80
|
+
expect(out.channelTemplates.totalCount).toBe(5);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('skips name mapping when intlCopyOf is empty', async () => {
|
|
84
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
85
|
+
response: {
|
|
86
|
+
templates: [{ name: 'Copy of A' }],
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
const out = await fetchSmsTemplatesFromQuery({ page: 1 }, '');
|
|
90
|
+
expect(out.channelTemplates.templates[0].name).toBe('Copy of A');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('handles missing response.templates', async () => {
|
|
94
|
+
Api.getAllTemplates.mockResolvedValue({ response: {} });
|
|
95
|
+
const out = await fetchSmsTemplatesFromQuery({}, '');
|
|
96
|
+
expect(out.channelTemplates.templates).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('uses empty response when raw.response is missing', async () => {
|
|
100
|
+
Api.getAllTemplates.mockResolvedValue({});
|
|
101
|
+
const out = await fetchSmsTemplatesFromQuery({}, '');
|
|
102
|
+
expect(out.channelTemplates.templates).toEqual([]);
|
|
103
|
+
expect(out.weCRMTemplate).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('does not map names when intlCopyOf is set but templates list is empty', async () => {
|
|
107
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
108
|
+
response: { templates: [] },
|
|
109
|
+
});
|
|
110
|
+
const out = await fetchSmsTemplatesFromQuery({ page: 1 }, 'X');
|
|
111
|
+
expect(out.channelTemplates.templates).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('uses empty string when template name is missing during intl mapping', async () => {
|
|
115
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
116
|
+
response: {
|
|
117
|
+
templates: [{ _id: 'n', name: undefined }],
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
const out = await fetchSmsTemplatesFromQuery({ page: 1 }, 'Lbl');
|
|
121
|
+
expect(out.channelTemplates.templates[0].name).toBe('');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('fetchSmsTemplatesListPage', () => {
|
|
126
|
+
it('falls back to total when totalCount is absent', async () => {
|
|
127
|
+
isTraiDLTEnable.mockReturnValue(false);
|
|
128
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
129
|
+
response: {
|
|
130
|
+
templates: [{ _id: 'a' }],
|
|
131
|
+
total: 7,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const page = await fetchSmsTemplatesListPage({
|
|
136
|
+
page: 1,
|
|
137
|
+
perPage: 25,
|
|
138
|
+
name: '',
|
|
139
|
+
sortBy: SMS_TEMPLATES_LIST_SORT_MOST_RECENT,
|
|
140
|
+
isFullMode: true,
|
|
141
|
+
smsRegister: {},
|
|
142
|
+
intlCopyOf: '',
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(page.templates).toEqual([{ _id: 'a' }]);
|
|
146
|
+
expect(page.totalCount).toBe(7);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('uses totalCount when set', async () => {
|
|
150
|
+
isTraiDLTEnable.mockReturnValue(false);
|
|
151
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
152
|
+
response: {
|
|
153
|
+
templates: [],
|
|
154
|
+
totalCount: 12,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
const page = await fetchSmsTemplatesListPage({
|
|
158
|
+
page: 1,
|
|
159
|
+
perPage: 25,
|
|
160
|
+
isFullMode: false,
|
|
161
|
+
smsRegister: {},
|
|
162
|
+
});
|
|
163
|
+
expect(page.totalCount).toBe(12);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('normalizes total to 0 when missing counts', async () => {
|
|
167
|
+
isTraiDLTEnable.mockReturnValue(false);
|
|
168
|
+
Api.getAllTemplates.mockResolvedValue({
|
|
169
|
+
response: { templates: [] },
|
|
170
|
+
});
|
|
171
|
+
const page = await fetchSmsTemplatesListPage({
|
|
172
|
+
page: 1,
|
|
173
|
+
perPage: 25,
|
|
174
|
+
isFullMode: false,
|
|
175
|
+
smsRegister: {},
|
|
176
|
+
});
|
|
177
|
+
expect(page.totalCount).toBe(0);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import * as Api from '../../../services/api';
|
|
3
|
+
import { COPY_OF } from '../../../constants/unified';
|
|
4
|
+
import { isTraiDLTEnable } from '../../../utils/common';
|
|
5
|
+
|
|
6
|
+
/** Matches Templates `getAllTemplates` default for SMS. */
|
|
7
|
+
export const SMS_TEMPLATES_LIST_SORT_MOST_RECENT = 'Most Recent';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Same query shape as Redux `GET_ALL_TEMPLATES` for channel Sms (DLT vs non-DLT via traiEnable).
|
|
11
|
+
*/
|
|
12
|
+
export function buildSmsTemplatesListQueryParams({
|
|
13
|
+
page,
|
|
14
|
+
perPage,
|
|
15
|
+
name = '',
|
|
16
|
+
sortBy = SMS_TEMPLATES_LIST_SORT_MOST_RECENT,
|
|
17
|
+
isFullMode,
|
|
18
|
+
smsRegister,
|
|
19
|
+
}) {
|
|
20
|
+
const traiDlt = isTraiDLTEnable(isFullMode, smsRegister);
|
|
21
|
+
return {
|
|
22
|
+
page,
|
|
23
|
+
perPage,
|
|
24
|
+
sortBy,
|
|
25
|
+
name: name || '',
|
|
26
|
+
...(traiDlt ? { traiEnable: true } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* SMS list for Redux saga: uses queryParams already built by Templates (incl. traiEnable).
|
|
32
|
+
* Applies the same "Copy of" → intl label as the former inline saga logic.
|
|
33
|
+
*/
|
|
34
|
+
export async function fetchSmsTemplatesFromQuery(queryParams, intlCopyOf = '') {
|
|
35
|
+
const raw = await Api.getAllTemplates({ channel: 'Sms', queryParams });
|
|
36
|
+
const response = raw.response || {};
|
|
37
|
+
let templates = get(response, 'templates', []) || [];
|
|
38
|
+
if (intlCopyOf && templates.length) {
|
|
39
|
+
templates = templates.map((template) => ({
|
|
40
|
+
...template,
|
|
41
|
+
name: (template.name || '').replace(new RegExp(COPY_OF, 'g'), intlCopyOf),
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
const channelTemplates = { ...response, templates };
|
|
45
|
+
return {
|
|
46
|
+
channelTemplates,
|
|
47
|
+
weCRMTemplate: response.unMapped,
|
|
48
|
+
raw,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* SMS list for the RCS SMS fallback picker only.
|
|
54
|
+
* Called from `useLocalTemplateList` in SmsFallback — keeps data in component state and pairs with
|
|
55
|
+
* `localTemplatesConfig` / `useLocalTemplates` in TemplatesV2. Does **not** use GET_ALL_TEMPLATES saga.
|
|
56
|
+
* Same HTTP call shape as `fetchSmsTemplatesFromQuery` (used by the main SMS GET_ALL_TEMPLATES saga).
|
|
57
|
+
*/
|
|
58
|
+
export async function fetchSmsTemplatesListPage({
|
|
59
|
+
page,
|
|
60
|
+
perPage,
|
|
61
|
+
name,
|
|
62
|
+
sortBy,
|
|
63
|
+
isFullMode,
|
|
64
|
+
smsRegister,
|
|
65
|
+
intlCopyOf = '',
|
|
66
|
+
}) {
|
|
67
|
+
const queryParams = buildSmsTemplatesListQueryParams({
|
|
68
|
+
page,
|
|
69
|
+
perPage,
|
|
70
|
+
name,
|
|
71
|
+
sortBy,
|
|
72
|
+
isFullMode,
|
|
73
|
+
smsRegister,
|
|
74
|
+
});
|
|
75
|
+
const { channelTemplates } = await fetchSmsTemplatesFromQuery(queryParams, intlCopyOf);
|
|
76
|
+
const templates = channelTemplates.templates || [];
|
|
77
|
+
const totalCount = get(channelTemplates, 'totalCount', get(channelTemplates, 'total', 0)) || 0;
|
|
78
|
+
return { templates, totalCount };
|
|
79
|
+
}
|
|
@@ -10,7 +10,7 @@ export default css`
|
|
|
10
10
|
|
|
11
11
|
.component-wrapper {
|
|
12
12
|
${(props) => props.isFullMode ? `
|
|
13
|
-
max-width:
|
|
13
|
+
max-width: 71.25rem;
|
|
14
14
|
margin: 0 auto;
|
|
15
15
|
width: 100%;
|
|
16
16
|
padding: 0.714rem 0;
|
|
@@ -23,6 +23,77 @@ export default css`
|
|
|
23
23
|
height: calc(100vh - 11.25rem);
|
|
24
24
|
} `}
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/* SMS fallback / local list: single pane skips .cap-tab-v2, so flex-fill the grid instead of viewport-fixed pagination height */
|
|
28
|
+
.creatives-templates-container--local-sms.library-mode {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
flex: 1;
|
|
32
|
+
min-height: 0;
|
|
33
|
+
height: 100%;
|
|
34
|
+
|
|
35
|
+
.component-wrapper {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
flex: 1;
|
|
39
|
+
min-height: 0;
|
|
40
|
+
height: 100%;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.templates-v2-local-sms-pane {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
flex: 1;
|
|
47
|
+
min-height: 0;
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
height: 100%;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.templates-v2-local-sms-pane .creatives-templates-list.library-mode {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
flex: 1;
|
|
56
|
+
min-height: 0;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
height: 100%;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type {
|
|
62
|
+
flex: 1;
|
|
63
|
+
min-height: 0;
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type > div {
|
|
70
|
+
flex: 1;
|
|
71
|
+
min-height: 0;
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type > div > div:first-child {
|
|
78
|
+
flex: 1;
|
|
79
|
+
min-height: 0;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.templates-v2-local-sms-pane .v2-pagination-container,
|
|
86
|
+
.templates-v2-local-sms-pane .v2-pagination-container-half {
|
|
87
|
+
/* Match _templates local-SMS: bounded height so overflow-y scroll works (flex+100% alone often doesn’t) */
|
|
88
|
+
flex: 0 1 auto;
|
|
89
|
+
min-height: 0;
|
|
90
|
+
height: calc(100vh - 12rem);
|
|
91
|
+
max-height: calc(100vh - 12rem);
|
|
92
|
+
overflow-y: auto;
|
|
93
|
+
overflow-x: hidden;
|
|
94
|
+
-webkit-overflow-scrolling: touch;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
26
97
|
`;
|
|
27
98
|
|
|
28
99
|
export const CapTabStyle = css`
|