@capillarytech/creatives-library 8.0.316-alpha.2 → 8.0.316-alpha.4
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 +14 -0
- package/package.json +1 -1
- package/utils/templateVarUtils.js +172 -0
- package/utils/tests/templateVarUtils.test.js +160 -0
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +11 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +12 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -0
- package/v2Components/CommonTestAndPreview/index.js +693 -155
- package/v2Components/CommonTestAndPreview/messages.js +41 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +172 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +269 -1
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +245 -0
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +25 -4
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +100 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +19 -1
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +13 -13
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -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 +107 -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 +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +327 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +8 -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 +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/v2Containers/CreativesContainer/index.js +286 -93
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -10
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/InApp/index.js +4 -5
- package/v2Containers/Rcs/constants.js +32 -1
- package/v2Containers/Rcs/index.js +953 -882
- package/v2Containers/Rcs/index.scss +85 -6
- package/v2Containers/Rcs/messages.js +10 -1
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +40834 -1963
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/index.test.js +41 -38
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
- package/v2Containers/Rcs/tests/utils.test.js +379 -1
- package/v2Containers/Rcs/utils.js +358 -10
- package/v2Containers/Sms/Create/index.js +81 -36
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +609 -128
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +9 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4327 -2374
- 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 +61 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +90 -40
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +110 -12
- 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/Viber/index.js +4 -9
- package/v2Containers/WebPush/Create/components/MessageSection.js +54 -18
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +28 -0
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +7 -3
- package/v2Containers/Whatsapp/index.js +10 -32
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
- package/v2Containers/Whatsapp/tests/index.test.js +172 -0
|
@@ -24,6 +24,20 @@ export default defineMessages({
|
|
|
24
24
|
id: `${scope}.personalizationTags`,
|
|
25
25
|
defaultMessage: 'Personalization Tags',
|
|
26
26
|
},
|
|
27
|
+
rcsTagsSectionTitle: {
|
|
28
|
+
id: `${scope}.rcsTagsSectionTitle`,
|
|
29
|
+
defaultMessage: 'RCS Tags',
|
|
30
|
+
},
|
|
31
|
+
/** Primary SMS section when channel is SMS — `messages[`${channel}TagsSectionTitle`]` resolves to this. */
|
|
32
|
+
SMSTagsSectionTitle: {
|
|
33
|
+
id: `${scope}.SMSTagsSectionTitle`,
|
|
34
|
+
defaultMessage: 'SMS tags',
|
|
35
|
+
},
|
|
36
|
+
/** Fallback SMS under RCS — same label as SMS Test & Preview for consistent personalization UX. */
|
|
37
|
+
smsFallbackTagsSectionTitle: {
|
|
38
|
+
id: `${scope}.smsFallbackTagsSectionTitle`,
|
|
39
|
+
defaultMessage: 'SMS tags',
|
|
40
|
+
},
|
|
27
41
|
customValues: {
|
|
28
42
|
id: `${scope}.customValues`,
|
|
29
43
|
defaultMessage: 'Custom Values',
|
|
@@ -36,6 +50,14 @@ export default defineMessages({
|
|
|
36
50
|
id: `${scope}.discardCustomValues`,
|
|
37
51
|
defaultMessage: 'Discard custom values',
|
|
38
52
|
},
|
|
53
|
+
rcsSenderIdLabel: {
|
|
54
|
+
id: `${scope}.rcsSenderIdLabel`,
|
|
55
|
+
defaultMessage: 'RCS Sender ID',
|
|
56
|
+
},
|
|
57
|
+
fallbackSmsSenderIdLabel: {
|
|
58
|
+
id: `${scope}.fallbackSmsSenderIdLabel`,
|
|
59
|
+
defaultMessage: 'Fallback SMS Sender ID',
|
|
60
|
+
},
|
|
39
61
|
updatePreview: {
|
|
40
62
|
id: `${scope}.updatePreview`,
|
|
41
63
|
defaultMessage: 'Update Preview',
|
|
@@ -92,6 +114,14 @@ export default defineMessages({
|
|
|
92
114
|
id: `${scope}.previewTitle`,
|
|
93
115
|
defaultMessage: 'Preview',
|
|
94
116
|
},
|
|
117
|
+
rcsTab: {
|
|
118
|
+
id: `${scope}.rcsTab`,
|
|
119
|
+
defaultMessage: 'RCS',
|
|
120
|
+
},
|
|
121
|
+
smsFallbackTab: {
|
|
122
|
+
id: `${scope}.smsFallbackTab`,
|
|
123
|
+
defaultMessage: 'Fallback SMS',
|
|
124
|
+
},
|
|
95
125
|
previewPlaceholder: {
|
|
96
126
|
id: `${scope}.previewPlaceholder`,
|
|
97
127
|
defaultMessage: 'Click "Update Preview" to see the rendered email.',
|
|
@@ -180,14 +210,22 @@ export default defineMessages({
|
|
|
180
210
|
id: `${scope}.lastModified`,
|
|
181
211
|
defaultMessage: 'Last modified',
|
|
182
212
|
},
|
|
183
|
-
|
|
184
|
-
id: `${scope}.
|
|
185
|
-
defaultMessage: 'by',
|
|
213
|
+
byAuthor: {
|
|
214
|
+
id: `${scope}.byAuthor`,
|
|
215
|
+
defaultMessage: 'by {name}',
|
|
186
216
|
},
|
|
187
217
|
senderId: {
|
|
188
218
|
id: `${scope}.senderId`,
|
|
189
219
|
defaultMessage: 'Sender ID',
|
|
190
220
|
},
|
|
221
|
+
rcsSenderIdLabel: {
|
|
222
|
+
id: `${scope}.rcsSenderIdLabel`,
|
|
223
|
+
defaultMessage: 'RCS sender ID',
|
|
224
|
+
},
|
|
225
|
+
domainLabel: {
|
|
226
|
+
id: `${scope}.domainLabel`,
|
|
227
|
+
defaultMessage: 'Domain',
|
|
228
|
+
},
|
|
191
229
|
urlPreviewImage: {
|
|
192
230
|
id: `${scope}.urlPreviewImage`,
|
|
193
231
|
defaultMessage: 'URL Preview Image',
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for Liquid `/preview` (updateEmailPreview) responses.
|
|
3
|
+
* The API may nest the payload under `data` or return fields at the top level.
|
|
4
|
+
* SMS responses often use `messageBody` instead of `resolvedBody`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getFallbackResolvedContent } from '../../utils/templateVarUtils';
|
|
8
|
+
import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from './constants';
|
|
9
|
+
|
|
10
|
+
export function normalizePreviewApiPayload(raw) {
|
|
11
|
+
if (!raw || typeof raw !== 'object') return raw;
|
|
12
|
+
const next = { ...raw };
|
|
13
|
+
if (next.resolvedBody == null) {
|
|
14
|
+
const alt = next.messageBody ?? next.previewMessage ?? next.previewText;
|
|
15
|
+
if (alt != null) next.resolvedBody = alt;
|
|
16
|
+
}
|
|
17
|
+
return next;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns a normalized preview object suitable for Redux `previewData`, or null on error / empty.
|
|
22
|
+
*/
|
|
23
|
+
export function extractPreviewFromLiquidResponse(response) {
|
|
24
|
+
if (!response || response.error) return null;
|
|
25
|
+
if (response.errors && Array.isArray(response.errors) && response.errors.length > 0) return null;
|
|
26
|
+
|
|
27
|
+
let body;
|
|
28
|
+
if (response.data !== undefined && response.data !== null && typeof response.data === 'object') {
|
|
29
|
+
body = normalizePreviewApiPayload(response.data);
|
|
30
|
+
} else if (typeof response === 'object' && !Array.isArray(response)) {
|
|
31
|
+
body = normalizePreviewApiPayload(response);
|
|
32
|
+
} else {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!body) return null;
|
|
37
|
+
if (body.resolvedBody === undefined && body.messageBody === undefined) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return body;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* RCS SMS fallback: merge template (`templateContent` / `content`) with VarSegment
|
|
45
|
+
* `rcsSmsFallbackVarMapped` before Liquid /preview, tag extraction, or createMessageMeta.
|
|
46
|
+
* Raw template alone is stale when the user edits slot values.
|
|
47
|
+
*/
|
|
48
|
+
export function getSmsFallbackTextForTagExtraction(smsFallbackContext) {
|
|
49
|
+
const rawTemplateBody =
|
|
50
|
+
smsFallbackContext?.templateContent ?? smsFallbackContext?.content ?? '';
|
|
51
|
+
if (!rawTemplateBody) return '';
|
|
52
|
+
const rcsSmsFallbackVarMapped = smsFallbackContext?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP];
|
|
53
|
+
const hasRcsSmsFallbackVarMappedEntries =
|
|
54
|
+
Object.keys(rcsSmsFallbackVarMapped ?? {}).length > 0;
|
|
55
|
+
if (hasRcsSmsFallbackVarMappedEntries) {
|
|
56
|
+
return getFallbackResolvedContent(rawTemplateBody, rcsSmsFallbackVarMapped ?? {});
|
|
57
|
+
}
|
|
58
|
+
return rawTemplateBody;
|
|
59
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
call, put, takeLatest, all,
|
|
6
|
+
call, put, takeLatest, takeEvery, all,
|
|
7
7
|
} from 'redux-saga/effects';
|
|
8
8
|
import get from 'lodash/get';
|
|
9
9
|
import isEmpty from 'lodash/isEmpty';
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
CHANNELS,
|
|
46
46
|
} from './constants';
|
|
47
47
|
import { parseSenderDetailsResponse } from './DeliverySettings/utils/parseSenderDetailsResponse';
|
|
48
|
+
import { extractPreviewFromLiquidResponse } from './previewApiUtils';
|
|
48
49
|
|
|
49
50
|
// Search Customers Saga
|
|
50
51
|
export function* searchCustomersSaga(action) {
|
|
@@ -91,11 +92,12 @@ export function* updatePreviewSaga(action) {
|
|
|
91
92
|
const customValues = action.payload.resolvedTags;
|
|
92
93
|
|
|
93
94
|
const response = yield call(Api.updateEmailPreview, action.payload);
|
|
94
|
-
|
|
95
|
+
const previewPayload = extractPreviewFromLiquidResponse(response);
|
|
96
|
+
if (previewPayload) {
|
|
95
97
|
yield put({
|
|
96
98
|
type: UPDATE_PREVIEW_SUCCESS,
|
|
97
99
|
payload: {
|
|
98
|
-
previewData:
|
|
100
|
+
previewData: previewPayload,
|
|
99
101
|
customValues, // Pass custom values to be preserved
|
|
100
102
|
},
|
|
101
103
|
});
|
|
@@ -236,8 +238,13 @@ export function* createMessageMetaSaga(action) {
|
|
|
236
238
|
export function* getPrefilledValuesSaga(action) {
|
|
237
239
|
try {
|
|
238
240
|
const response = yield call(Api.updateEmailPreview, action.payload);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
const body =
|
|
242
|
+
response?.data !== undefined && response?.data !== null
|
|
243
|
+
? response.data
|
|
244
|
+
: response;
|
|
245
|
+
const resolvedTagValues = body?.resolvedTagValues;
|
|
246
|
+
if (resolvedTagValues != null) {
|
|
247
|
+
yield put({ type: GET_PREFILLED_VALUES_SUCCESS, payload: { values: resolvedTagValues } });
|
|
241
248
|
} else {
|
|
242
249
|
// Pass all errors from API response to state
|
|
243
250
|
yield put({
|
|
@@ -337,7 +344,9 @@ export function* getWeCrmAccountsSaga(action) {
|
|
|
337
344
|
}
|
|
338
345
|
|
|
339
346
|
export function* watchGetSenderDetails() {
|
|
340
|
-
|
|
347
|
+
// takeEvery: RCS test & preview dispatches RCS then SMS back-to-back; takeLatest would
|
|
348
|
+
// cancel the RCS fetch so senderDetailsByChannel.RCS stayed empty while SMS loaded.
|
|
349
|
+
yield takeEvery(GET_SENDER_DETAILS_REQUESTED, getSenderDetailsSaga);
|
|
341
350
|
}
|
|
342
351
|
|
|
343
352
|
export function* watchGetWeCrmAccounts() {
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import { IntlProvider } from 'react-intl';
|
|
8
|
+
import CustomValuesEditor from '../CustomValuesEditor';
|
|
9
|
+
|
|
10
|
+
jest.mock('@capillarytech/cap-ui-library/CapRow', () => {
|
|
11
|
+
const React = require('react');
|
|
12
|
+
return function CapRow(props) {
|
|
13
|
+
return React.createElement('div', { className: props?.className, 'data-testid': 'cap-row' }, props?.children);
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
jest.mock('@capillarytech/cap-ui-library/CapSpin', () => {
|
|
17
|
+
const React = require('react');
|
|
18
|
+
return function CapSpin() {
|
|
19
|
+
return React.createElement('div', { 'data-testid': 'cap-spin' }, 'spin');
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
jest.mock('@capillarytech/cap-ui-library/CapSwitch', () => {
|
|
23
|
+
const React = require('react');
|
|
24
|
+
return function CapSwitch(props) {
|
|
25
|
+
return React.createElement('input', {
|
|
26
|
+
type: 'checkbox',
|
|
27
|
+
'data-testid': 'json-switch',
|
|
28
|
+
checked: props?.checked,
|
|
29
|
+
onChange: (e) => props?.onChange?.(e.target.checked),
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
jest.mock('@capillarytech/cap-ui-library/CapButton', () => {
|
|
34
|
+
const React = require('react');
|
|
35
|
+
return function CapButton(props) {
|
|
36
|
+
return React.createElement('button', {
|
|
37
|
+
type: 'button',
|
|
38
|
+
'data-testid': 'cap-button',
|
|
39
|
+
onClick: props?.onClick,
|
|
40
|
+
disabled: props?.disabled,
|
|
41
|
+
'data-loading': props?.loading,
|
|
42
|
+
}, props?.children);
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
jest.mock('@capillarytech/cap-ui-library/CapInput', () => {
|
|
46
|
+
const React = require('react');
|
|
47
|
+
return function CapInput(props) {
|
|
48
|
+
return React.createElement('input', {
|
|
49
|
+
'data-testid': 'tag-input',
|
|
50
|
+
value: props?.value,
|
|
51
|
+
placeholder: props?.placeholder,
|
|
52
|
+
onChange: props?.onChange,
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
jest.mock('@capillarytech/cap-ui-library/CapLabel', () => {
|
|
57
|
+
const React = require('react');
|
|
58
|
+
return function CapLabel(props) {
|
|
59
|
+
return React.createElement('div', { className: props?.className, 'data-testid': 'cap-label' }, props?.children);
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const formatMessage = (msg) => (typeof msg === 'object' && msg?.id ? msg.id : String(msg));
|
|
64
|
+
|
|
65
|
+
const baseProps = {
|
|
66
|
+
isExtractingTags: false,
|
|
67
|
+
isUpdatePreviewDisabled: false,
|
|
68
|
+
showJSON: false,
|
|
69
|
+
setShowJSON: jest.fn(),
|
|
70
|
+
customValues: { 'tag.a': 'v1' },
|
|
71
|
+
handleJSONTextChange: jest.fn(),
|
|
72
|
+
sections: [
|
|
73
|
+
{
|
|
74
|
+
key: 'sec1',
|
|
75
|
+
title: 'Section A',
|
|
76
|
+
requiredTags: [{ fullPath: 'tag.a', name: 'a' }],
|
|
77
|
+
optionalTags: [{ fullPath: 'tag.b', name: 'b' }],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
handleCustomValueChange: jest.fn(),
|
|
81
|
+
handleDiscardCustomValues: jest.fn(),
|
|
82
|
+
handleUpdatePreview: jest.fn(),
|
|
83
|
+
isUpdatingPreview: false,
|
|
84
|
+
formatMessage,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
describe('CustomValuesEditor', () => {
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
jest.clearAllMocks();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('shows loading state when isExtractingTags', () => {
|
|
93
|
+
render(
|
|
94
|
+
<IntlProvider locale="en" messages={{}}>
|
|
95
|
+
<CustomValuesEditor {...baseProps} isExtractingTags />
|
|
96
|
+
</IntlProvider>,
|
|
97
|
+
);
|
|
98
|
+
expect(screen.getByTestId('cap-spin')).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('shows values missing label when isUpdatePreviewDisabled', () => {
|
|
102
|
+
render(
|
|
103
|
+
<IntlProvider locale="en" messages={{}}>
|
|
104
|
+
<CustomValuesEditor {...baseProps} isUpdatePreviewDisabled />
|
|
105
|
+
</IntlProvider>,
|
|
106
|
+
);
|
|
107
|
+
expect(document.querySelector('.values-missing-message')).toBeTruthy();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('toggles JSON mode and calls setShowJSON', () => {
|
|
111
|
+
const setShowJSON = jest.fn();
|
|
112
|
+
render(
|
|
113
|
+
<IntlProvider locale="en" messages={{}}>
|
|
114
|
+
<CustomValuesEditor {...baseProps} setShowJSON={setShowJSON} />
|
|
115
|
+
</IntlProvider>,
|
|
116
|
+
);
|
|
117
|
+
fireEvent.click(screen.getByTestId('json-switch'));
|
|
118
|
+
expect(setShowJSON).toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('renders JSON textarea and fires handleJSONTextChange', () => {
|
|
122
|
+
render(
|
|
123
|
+
<IntlProvider locale="en" messages={{}}>
|
|
124
|
+
<CustomValuesEditor {...baseProps} showJSON />
|
|
125
|
+
</IntlProvider>,
|
|
126
|
+
);
|
|
127
|
+
const ta = document.querySelector('.json-textarea');
|
|
128
|
+
expect(ta).toBeTruthy();
|
|
129
|
+
fireEvent.change(ta, { target: { value: '{}' } });
|
|
130
|
+
expect(baseProps.handleJSONTextChange).toHaveBeenCalledWith('{}');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('renders required and optional tag inputs when not JSON', () => {
|
|
134
|
+
render(
|
|
135
|
+
<IntlProvider locale="en" messages={{}}>
|
|
136
|
+
<CustomValuesEditor {...baseProps} />
|
|
137
|
+
</IntlProvider>,
|
|
138
|
+
);
|
|
139
|
+
const inputs = screen.getAllByTestId('tag-input');
|
|
140
|
+
expect(inputs.length).toBeGreaterThan(0);
|
|
141
|
+
fireEvent.change(inputs[0], { target: { value: 'new' } });
|
|
142
|
+
expect(baseProps.handleCustomValueChange).toHaveBeenCalledWith('tag.a', 'new');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('calls discard and update handlers', () => {
|
|
146
|
+
render(
|
|
147
|
+
<IntlProvider locale="en" messages={{}}>
|
|
148
|
+
<CustomValuesEditor {...baseProps} />
|
|
149
|
+
</IntlProvider>,
|
|
150
|
+
);
|
|
151
|
+
const buttons = screen.getAllByTestId('cap-button');
|
|
152
|
+
fireEvent.click(buttons[0]);
|
|
153
|
+
fireEvent.click(buttons[buttons.length - 1]);
|
|
154
|
+
expect(baseProps.handleDiscardCustomValues).toHaveBeenCalled();
|
|
155
|
+
expect(baseProps.handleUpdatePreview).toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('skips sections with no tags', () => {
|
|
159
|
+
render(
|
|
160
|
+
<IntlProvider locale="en" messages={{}}>
|
|
161
|
+
<CustomValuesEditor
|
|
162
|
+
{...baseProps}
|
|
163
|
+
sections={[
|
|
164
|
+
{ key: 'empty', requiredTags: [], optionalTags: [] },
|
|
165
|
+
baseProps.sections[0],
|
|
166
|
+
]}
|
|
167
|
+
/>
|
|
168
|
+
</IntlProvider>,
|
|
169
|
+
);
|
|
170
|
+
expect(screen.getAllByTestId('tag-input').length).toBeGreaterThan(0);
|
|
171
|
+
});
|
|
172
|
+
});
|
package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js
CHANGED
|
@@ -187,7 +187,7 @@ describe('ModifyDeliverySettings', () => {
|
|
|
187
187
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
188
188
|
|
|
189
189
|
renderComponent({
|
|
190
|
-
channel: '
|
|
190
|
+
channel: 'UNSUPPORTED_CHANNEL',
|
|
191
191
|
onSaveDeliverySettings: onSave,
|
|
192
192
|
});
|
|
193
193
|
|
|
@@ -614,6 +614,22 @@ describe('ModifyDeliverySettings', () => {
|
|
|
614
614
|
expect(senderNameOptions).toEqual(expect.arrayContaining(['fallback@test.com']));
|
|
615
615
|
expect(replyToOptions).toEqual(expect.arrayContaining(['Reply Label']));
|
|
616
616
|
});
|
|
617
|
+
|
|
618
|
+
it('should use reply-to label when value is empty in reply-to options', () => {
|
|
619
|
+
renderComponent({
|
|
620
|
+
channel: CHANNELS.EMAIL,
|
|
621
|
+
senderDetailsOptions: [{
|
|
622
|
+
domainId: 90,
|
|
623
|
+
domainName: 'Reply Label Domain',
|
|
624
|
+
emailSenders: [{ value: 'a@test.com' }],
|
|
625
|
+
emailRepliers: [{ label: 'OnlyLabel', value: '' }],
|
|
626
|
+
}],
|
|
627
|
+
deliverySettings: { domainId: 90, domainGatewayMapId: 1 },
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
const replyToOptions = Array.from(getSelects()[3].querySelectorAll('option')).map((opt) => opt.textContent);
|
|
631
|
+
expect(replyToOptions).toEqual(expect.arrayContaining(['OnlyLabel']));
|
|
632
|
+
});
|
|
617
633
|
});
|
|
618
634
|
|
|
619
635
|
describe('WHATSAPP flow', () => {
|
|
@@ -838,6 +854,19 @@ describe('ModifyDeliverySettings', () => {
|
|
|
838
854
|
sourceAccountIdentifier: 'waba-1',
|
|
839
855
|
});
|
|
840
856
|
});
|
|
857
|
+
|
|
858
|
+
it('should render disabled CapLabel with label3 for locked WhatsApp account row', () => {
|
|
859
|
+
renderComponent({
|
|
860
|
+
channel: CHANNELS.WHATSAPP,
|
|
861
|
+
senderDetailsOptions: whatsappDomains,
|
|
862
|
+
wecrmAccounts,
|
|
863
|
+
whatsappAccountFromForm: { accountName: 'Account One' },
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const labels = screen.getAllByTestId('cap-label');
|
|
867
|
+
const disabledHint = labels.find((el) => el.getAttribute('data-type') === 'label3');
|
|
868
|
+
expect(disabledHint).toBeTruthy();
|
|
869
|
+
});
|
|
841
870
|
});
|
|
842
871
|
|
|
843
872
|
describe('Done and close handling', () => {
|
|
@@ -885,4 +914,243 @@ describe('ModifyDeliverySettings', () => {
|
|
|
885
914
|
expect(onSave).toHaveBeenCalled();
|
|
886
915
|
});
|
|
887
916
|
});
|
|
917
|
+
|
|
918
|
+
describe('RCS flow', () => {
|
|
919
|
+
const rcsDomains = [
|
|
920
|
+
{
|
|
921
|
+
domainId: 51,
|
|
922
|
+
domainName: 'RCS Domain A',
|
|
923
|
+
dgmId: 511,
|
|
924
|
+
gsmSenders: [{ value: 'RCS_GSM_1' }, { value: 'RCS_GSM_2' }],
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
domainId: 52,
|
|
928
|
+
domainName: 'RCS Domain B',
|
|
929
|
+
dgmId: 522,
|
|
930
|
+
gsmSenders: [{ value: 'RCS_OTHER' }],
|
|
931
|
+
},
|
|
932
|
+
];
|
|
933
|
+
|
|
934
|
+
const smsFallbackDomains = [
|
|
935
|
+
{
|
|
936
|
+
domainId: 61,
|
|
937
|
+
domainName: 'SMS Fallback',
|
|
938
|
+
gsmSenders: [{ value: 'SMS_FB_1' }],
|
|
939
|
+
},
|
|
940
|
+
];
|
|
941
|
+
|
|
942
|
+
it('should render RCS domain and sender selects', () => {
|
|
943
|
+
renderComponent({
|
|
944
|
+
channel: CHANNELS.RCS,
|
|
945
|
+
senderDetailsOptions: rcsDomains,
|
|
946
|
+
});
|
|
947
|
+
expect(getSelects()).toHaveLength(2);
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
it('should set composite gsm sender id when RCS domain changes', () => {
|
|
951
|
+
const onSave = jest.fn();
|
|
952
|
+
|
|
953
|
+
renderComponent({
|
|
954
|
+
channel: CHANNELS.RCS,
|
|
955
|
+
senderDetailsOptions: rcsDomains,
|
|
956
|
+
onSaveDeliverySettings: onSave,
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
fireEvent.change(getSelects()[0], { target: { value: '51' } });
|
|
960
|
+
fireEvent.click(screen.getByText('Done'));
|
|
961
|
+
|
|
962
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
963
|
+
expect.objectContaining({
|
|
964
|
+
domainId: 51,
|
|
965
|
+
gsmSenderId: '51|RCS_GSM_1',
|
|
966
|
+
}),
|
|
967
|
+
);
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
it('should render SMS fallback domain and sender when fallback options exist', () => {
|
|
971
|
+
renderComponent({
|
|
972
|
+
channel: CHANNELS.RCS,
|
|
973
|
+
senderDetailsOptions: rcsDomains,
|
|
974
|
+
smsFallbackSenderDetailsOptions: smsFallbackDomains,
|
|
975
|
+
});
|
|
976
|
+
expect(getSelects()).toHaveLength(4);
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
it('should strip gsm and fallback sender ids that are not in current options on Done', () => {
|
|
980
|
+
const onSave = jest.fn();
|
|
981
|
+
|
|
982
|
+
renderComponent({
|
|
983
|
+
channel: CHANNELS.RCS,
|
|
984
|
+
senderDetailsOptions: rcsDomains,
|
|
985
|
+
smsFallbackSenderDetailsOptions: smsFallbackDomains,
|
|
986
|
+
deliverySettings: {
|
|
987
|
+
domainId: 51,
|
|
988
|
+
gsmSenderId: 'not-a-valid-composite',
|
|
989
|
+
smsFallbackDomainId: 61,
|
|
990
|
+
cdmaSenderId: '61|SMS_FB_1',
|
|
991
|
+
},
|
|
992
|
+
onSaveDeliverySettings: onSave,
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
fireEvent.click(screen.getByText('Done'));
|
|
996
|
+
|
|
997
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
998
|
+
expect.objectContaining({
|
|
999
|
+
domainId: 51,
|
|
1000
|
+
gsmSenderId: '',
|
|
1001
|
+
smsFallbackDomainId: 61,
|
|
1002
|
+
cdmaSenderId: '61|SMS_FB_1',
|
|
1003
|
+
}),
|
|
1004
|
+
);
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
it('should strip invalid SMS fallback composite sender on Done', () => {
|
|
1008
|
+
const onSave = jest.fn();
|
|
1009
|
+
|
|
1010
|
+
renderComponent({
|
|
1011
|
+
channel: CHANNELS.RCS,
|
|
1012
|
+
senderDetailsOptions: rcsDomains,
|
|
1013
|
+
smsFallbackSenderDetailsOptions: smsFallbackDomains,
|
|
1014
|
+
deliverySettings: {
|
|
1015
|
+
domainId: 51,
|
|
1016
|
+
gsmSenderId: '51|RCS_GSM_1',
|
|
1017
|
+
smsFallbackDomainId: 61,
|
|
1018
|
+
cdmaSenderId: 'bogus|value',
|
|
1019
|
+
},
|
|
1020
|
+
onSaveDeliverySettings: onSave,
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
fireEvent.click(screen.getByText('Done'));
|
|
1024
|
+
|
|
1025
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
1026
|
+
expect.objectContaining({
|
|
1027
|
+
gsmSenderId: '51|RCS_GSM_1',
|
|
1028
|
+
cdmaSenderId: '',
|
|
1029
|
+
}),
|
|
1030
|
+
);
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it('should filter SMS fallback domains and GSM senders when TRAI DLT and registered sender IDs apply', () => {
|
|
1034
|
+
const fallbackDlt = [
|
|
1035
|
+
{
|
|
1036
|
+
domainId: 71,
|
|
1037
|
+
domainName: 'Has Registered GSM',
|
|
1038
|
+
gsmSenders: [{ value: 'REG_OK' }, { value: 'NOT_ON_DLT' }],
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
domainId: 72,
|
|
1042
|
+
domainName: 'No Match',
|
|
1043
|
+
gsmSenders: [{ value: 'UNMATCHED' }],
|
|
1044
|
+
},
|
|
1045
|
+
];
|
|
1046
|
+
const onSave = jest.fn();
|
|
1047
|
+
|
|
1048
|
+
renderComponent({
|
|
1049
|
+
channel: CHANNELS.RCS,
|
|
1050
|
+
senderDetailsOptions: rcsDomains,
|
|
1051
|
+
smsFallbackSenderDetailsOptions: fallbackDlt,
|
|
1052
|
+
smsTraiDltEnabled: true,
|
|
1053
|
+
registeredSenderIds: ['REG_OK'],
|
|
1054
|
+
deliverySettings: {
|
|
1055
|
+
domainId: 51,
|
|
1056
|
+
gsmSenderId: '51|RCS_GSM_1',
|
|
1057
|
+
smsFallbackDomainId: 71,
|
|
1058
|
+
cdmaSenderId: '71|REG_OK',
|
|
1059
|
+
},
|
|
1060
|
+
onSaveDeliverySettings: onSave,
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
fireEvent.click(screen.getByText('Done'));
|
|
1064
|
+
|
|
1065
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
1066
|
+
expect.objectContaining({
|
|
1067
|
+
smsFallbackDomainId: 71,
|
|
1068
|
+
cdmaSenderId: '71|REG_OK',
|
|
1069
|
+
}),
|
|
1070
|
+
);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it('should set SMS fallback composite to first GSM after skipping domain-name echo row', () => {
|
|
1074
|
+
const fallbackEcho = [
|
|
1075
|
+
{
|
|
1076
|
+
domainId: 80,
|
|
1077
|
+
domainName: 'Echo',
|
|
1078
|
+
gsmSenders: [{ value: 'Echo' }, { value: 'REAL_SENDER' }],
|
|
1079
|
+
},
|
|
1080
|
+
];
|
|
1081
|
+
const onSave = jest.fn();
|
|
1082
|
+
|
|
1083
|
+
renderComponent({
|
|
1084
|
+
channel: CHANNELS.RCS,
|
|
1085
|
+
senderDetailsOptions: rcsDomains,
|
|
1086
|
+
smsFallbackSenderDetailsOptions: fallbackEcho,
|
|
1087
|
+
deliverySettings: {
|
|
1088
|
+
domainId: 51,
|
|
1089
|
+
gsmSenderId: '51|RCS_GSM_1',
|
|
1090
|
+
},
|
|
1091
|
+
onSaveDeliverySettings: onSave,
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
fireEvent.change(getSelects()[2], { target: { value: '80' } });
|
|
1095
|
+
fireEvent.click(screen.getByText('Done'));
|
|
1096
|
+
|
|
1097
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
1098
|
+
expect.objectContaining({
|
|
1099
|
+
smsFallbackDomainId: 80,
|
|
1100
|
+
cdmaSenderId: '80|REAL_SENDER',
|
|
1101
|
+
}),
|
|
1102
|
+
);
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it('should only list RCS sender options for the selected RCS domain', () => {
|
|
1106
|
+
const onSave = jest.fn();
|
|
1107
|
+
|
|
1108
|
+
renderComponent({
|
|
1109
|
+
channel: CHANNELS.RCS,
|
|
1110
|
+
senderDetailsOptions: rcsDomains,
|
|
1111
|
+
onSaveDeliverySettings: onSave,
|
|
1112
|
+
deliverySettings: {
|
|
1113
|
+
domainId: 52,
|
|
1114
|
+
gsmSenderId: '52|RCS_OTHER',
|
|
1115
|
+
},
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
fireEvent.click(screen.getByText('Done'));
|
|
1119
|
+
|
|
1120
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
1121
|
+
expect.objectContaining({
|
|
1122
|
+
domainId: 52,
|
|
1123
|
+
gsmSenderId: '52|RCS_OTHER',
|
|
1124
|
+
}),
|
|
1125
|
+
);
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
it('should set composite RCS gsm when domain id is numeric zero', () => {
|
|
1129
|
+
const rcsWithZero = [
|
|
1130
|
+
{
|
|
1131
|
+
domainId: 0,
|
|
1132
|
+
domainName: 'Zero Id',
|
|
1133
|
+
dgmId: 1,
|
|
1134
|
+
gsmSenders: [{ value: 'Z_GSM' }],
|
|
1135
|
+
},
|
|
1136
|
+
];
|
|
1137
|
+
const onSave = jest.fn();
|
|
1138
|
+
|
|
1139
|
+
renderComponent({
|
|
1140
|
+
channel: CHANNELS.RCS,
|
|
1141
|
+
senderDetailsOptions: rcsWithZero,
|
|
1142
|
+
onSaveDeliverySettings: onSave,
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
fireEvent.change(getSelects()[0], { target: { value: '0' } });
|
|
1146
|
+
fireEvent.click(screen.getByText('Done'));
|
|
1147
|
+
|
|
1148
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
1149
|
+
expect.objectContaining({
|
|
1150
|
+
domainId: 0,
|
|
1151
|
+
gsmSenderId: '0|Z_GSM',
|
|
1152
|
+
}),
|
|
1153
|
+
);
|
|
1154
|
+
});
|
|
1155
|
+
});
|
|
888
1156
|
});
|