@capillarytech/creatives-library 8.0.304 → 8.0.305-alpha.0
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 +4 -1
- package/initialState.js +0 -2
- package/package.json +1 -1
- package/utils/common.js +12 -9
- package/utils/commonUtils.js +36 -93
- package/utils/tagValidations.js +83 -223
- package/utils/tests/commonUtil.test.js +147 -124
- package/utils/tests/tagValidations.test.js +441 -358
- package/v2Components/CapDeviceContent/index.js +10 -7
- package/v2Components/ErrorInfoNote/index.js +2 -5
- package/v2Components/FormBuilder/index.js +137 -203
- package/v2Components/FormBuilder/messages.js +0 -8
- package/v2Components/HtmlEditor/HTMLEditor.js +0 -5
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +0 -15
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +1 -2
- package/v2Containers/BeePopupEditor/index.js +9 -2
- package/v2Containers/Cap/mockData.js +0 -14
- package/v2Containers/Cap/reducer.js +3 -55
- package/v2Containers/Cap/tests/reducer.test.js +0 -102
- package/v2Containers/CreativesContainer/SlideBoxContent.js +40 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -5
- package/v2Containers/CreativesContainer/constants.js +6 -0
- package/v2Containers/CreativesContainer/index.js +47 -7
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +121 -4
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +23 -70
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +29 -137
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/FTP/messages.js +0 -4
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
- package/v2Containers/InApp/index.js +216 -120
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InApp/tests/mockData.js +1 -1
- package/v2Containers/InappAdvance/index.js +6 -110
- package/v2Containers/InappAdvance/tests/index.test.js +2 -0
- package/v2Containers/Line/Container/Text/index.js +0 -1
- package/v2Containers/MobilePush/Create/index.js +59 -19
- package/v2Containers/MobilePush/Edit/index.js +48 -20
- package/v2Containers/MobilePushNew/index.js +12 -32
- package/v2Containers/MobilepushWrapper/index.js +3 -1
- package/v2Containers/Rcs/index.js +12 -37
- package/v2Containers/Sms/Create/index.js +39 -3
- package/v2Containers/Sms/Create/messages.js +4 -0
- package/v2Containers/Sms/Edit/index.js +35 -3
- package/v2Containers/Sms/commonMethods.js +3 -6
- package/v2Containers/Sms/tests/commonMethods.test.js +122 -0
- package/v2Containers/SmsTrai/Edit/index.js +11 -47
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/SmsWrapper/index.js +2 -0
- package/v2Containers/TemplatesV2/index.js +28 -13
- package/v2Containers/Viber/index.js +0 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -3
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +17 -8
- package/v2Containers/WebPush/Create/utils/validation.test.js +44 -24
- package/v2Containers/Whatsapp/index.js +9 -17
- package/v2Containers/Zalo/index.js +3 -11
|
@@ -13,7 +13,7 @@ import { IntlProvider } from 'react-intl';
|
|
|
13
13
|
import EmailHTMLEditor from '../EmailHTMLEditor';
|
|
14
14
|
import { validateLiquidTemplateContent } from '../../../../utils/commonUtils';
|
|
15
15
|
import { validateTags } from '../../../../utils/tagValidations';
|
|
16
|
-
import {
|
|
16
|
+
import { isEmailUnsubscribeTagOptional } from '../../../../utils/common';
|
|
17
17
|
|
|
18
18
|
// Mock dependencies
|
|
19
19
|
jest.mock('../../../../utils/commonUtils', () => ({
|
|
@@ -24,11 +24,8 @@ jest.mock('../../../../utils/tagValidations', () => ({
|
|
|
24
24
|
validateTags: jest.fn(),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
-
// Create mutable mock for hasLiquidSupportFeature
|
|
28
|
-
const mockHasLiquidSupportFeature = jest.fn(() => true);
|
|
29
27
|
jest.mock('../../../../utils/common', () => ({
|
|
30
|
-
|
|
31
|
-
isEmailUnsubscribeTagMandatory: jest.fn(() => false),
|
|
28
|
+
isEmailUnsubscribeTagOptional: jest.fn(() => false),
|
|
32
29
|
}));
|
|
33
30
|
|
|
34
31
|
jest.mock('../../../../utils/history', () => ({
|
|
@@ -420,7 +417,7 @@ describe('EmailHTMLEditor', () => {
|
|
|
420
417
|
jest.clearAllMocks();
|
|
421
418
|
validateLiquidTemplateContent.mockResolvedValue(true);
|
|
422
419
|
validateTags.mockReturnValue({ valid: true });
|
|
423
|
-
|
|
420
|
+
isEmailUnsubscribeTagOptional.mockReturnValue(false);
|
|
424
421
|
// Reset mock functions
|
|
425
422
|
mockGetAllIssues.mockReturnValue([]);
|
|
426
423
|
mockGetValidationState.mockReturnValue({
|
|
@@ -428,88 +425,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
428
425
|
hasErrors: false,
|
|
429
426
|
issueCounts: { errors: 0, warnings: 0, total: 0 },
|
|
430
427
|
});
|
|
431
|
-
// Reset hasLiquidSupportFeature mock to return true by default
|
|
432
|
-
mockHasLiquidSupportFeature.mockReturnValue(true);
|
|
433
|
-
capturedApiValidationErrorsRef.current = null;
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
describe('mergedApiValidationErrors (lines 124-125)', () => {
|
|
437
|
-
beforeEach(() => {
|
|
438
|
-
global.__captureApiValidationErrorsRef = capturedApiValidationErrorsRef;
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
afterEach(() => {
|
|
442
|
-
delete global.__captureApiValidationErrorsRef;
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it('merges tag unsupported errors into standardErrors when tagValidationError has unsupportedTags', async () => {
|
|
446
|
-
validateTags.mockReturnValue({
|
|
447
|
-
valid: false,
|
|
448
|
-
unsupportedTags: ['tagA', 'tagB'],
|
|
449
|
-
});
|
|
450
|
-
renderWithIntl({
|
|
451
|
-
metaEntities: { tags: { standard: [{ name: 'customer.name' }] } },
|
|
452
|
-
tags: [{ name: 'customer.name' }],
|
|
453
|
-
supportedTags: [],
|
|
454
|
-
});
|
|
455
|
-
const changeButton = screen.getByTestId('trigger-content-change');
|
|
456
|
-
await act(async () => {
|
|
457
|
-
fireEvent.click(changeButton);
|
|
458
|
-
});
|
|
459
|
-
await waitFor(() => {
|
|
460
|
-
expect(capturedApiValidationErrorsRef.current).not.toBeNull();
|
|
461
|
-
expect(capturedApiValidationErrorsRef.current.liquidErrors).toEqual([]);
|
|
462
|
-
expect(capturedApiValidationErrorsRef.current.standardErrors).toContain(
|
|
463
|
-
'Unsupported tags are: tagA, tagB',
|
|
464
|
-
);
|
|
465
|
-
});
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it('merges tag missing errors into standardErrors when tagValidationError has missingTags and unsubscribe not mandatory', async () => {
|
|
469
|
-
isEmailUnsubscribeTagMandatory.mockReturnValue(false);
|
|
470
|
-
validateTags.mockReturnValue({
|
|
471
|
-
valid: false,
|
|
472
|
-
missingTags: ['unsubscribe'],
|
|
473
|
-
});
|
|
474
|
-
renderWithIntl({
|
|
475
|
-
metaEntities: { tags: { standard: [{ name: 'customer.name' }] } },
|
|
476
|
-
tags: [{ name: 'customer.name' }],
|
|
477
|
-
supportedTags: [],
|
|
478
|
-
});
|
|
479
|
-
const changeButton = screen.getByTestId('trigger-content-change');
|
|
480
|
-
await act(async () => {
|
|
481
|
-
fireEvent.click(changeButton);
|
|
482
|
-
});
|
|
483
|
-
await waitFor(() => {
|
|
484
|
-
expect(capturedApiValidationErrorsRef.current).not.toBeNull();
|
|
485
|
-
expect(capturedApiValidationErrorsRef.current.standardErrors).toContain(
|
|
486
|
-
'Missing tags are: unsubscribe',
|
|
487
|
-
);
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
it('uses apiValidationErrors.liquidErrors and concatenates apiValidationErrors.standardErrors with tag messages (merge shape)', async () => {
|
|
492
|
-
// When tag messages exist, mergedApiValidationErrors returns liquidErrors from apiValidationErrors
|
|
493
|
-
// and standardErrors = [...(apiValidationErrors?.standardErrors || []), ...tagMessages] (lines 124-125)
|
|
494
|
-
validateTags.mockReturnValue({
|
|
495
|
-
valid: false,
|
|
496
|
-
unsupportedTags: ['customTag'],
|
|
497
|
-
});
|
|
498
|
-
renderWithIntl({
|
|
499
|
-
metaEntities: { tags: { standard: [{ name: 'customer.name' }] } },
|
|
500
|
-
tags: [{ name: 'customer.name' }],
|
|
501
|
-
});
|
|
502
|
-
const changeButton = screen.getByTestId('trigger-content-change');
|
|
503
|
-
await act(async () => {
|
|
504
|
-
fireEvent.click(changeButton);
|
|
505
|
-
});
|
|
506
|
-
await waitFor(() => {
|
|
507
|
-
expect(capturedApiValidationErrorsRef.current).not.toBeNull();
|
|
508
|
-
const { liquidErrors, standardErrors } = capturedApiValidationErrorsRef.current;
|
|
509
|
-
expect(liquidErrors).toEqual([]);
|
|
510
|
-
expect(standardErrors).toContain('Unsupported tags are: customTag');
|
|
511
|
-
});
|
|
512
|
-
});
|
|
513
428
|
});
|
|
514
429
|
|
|
515
430
|
describe('Default Parameter Values (lines 60-63)', () => {
|
|
@@ -1106,7 +1021,7 @@ describe('EmailHTMLEditor', () => {
|
|
|
1106
1021
|
});
|
|
1107
1022
|
|
|
1108
1023
|
it('allows save when unsubscribe validation is on and tag is present', () => {
|
|
1109
|
-
|
|
1024
|
+
isEmailUnsubscribeTagOptional.mockReturnValue(false);
|
|
1110
1025
|
renderWithIntl({
|
|
1111
1026
|
isGetFormData: true,
|
|
1112
1027
|
subject: 'Valid Subject',
|
|
@@ -1116,49 +1031,44 @@ describe('EmailHTMLEditor', () => {
|
|
|
1116
1031
|
// Should proceed with save (validation passes)
|
|
1117
1032
|
});
|
|
1118
1033
|
|
|
1119
|
-
it('blocks save
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
missingTags: ['tag2'],
|
|
1034
|
+
it('blocks save when liquid API validation fails for Email', async () => {
|
|
1035
|
+
// Liquid validation runs in library mode (!isFullMode). Simulate API validation failure via mock onError.
|
|
1036
|
+
validateLiquidTemplateContent.mockImplementation((content, options) => {
|
|
1037
|
+
options.onError({ standardErrors: [], liquidErrors: ['Validation failed'] });
|
|
1038
|
+
return Promise.resolve(false);
|
|
1125
1039
|
});
|
|
1126
1040
|
const onValidationFail = jest.fn();
|
|
1127
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
1128
|
-
|
|
1129
|
-
// Set subject and content via component interactions
|
|
1130
1041
|
const { rerender } = renderWithIntl({
|
|
1131
1042
|
onValidationFail,
|
|
1132
1043
|
isGetFormData: false,
|
|
1044
|
+
isFullMode: false,
|
|
1133
1045
|
metaEntities: {
|
|
1134
1046
|
tags: {
|
|
1135
1047
|
standard: [{ name: 'customer.name' }],
|
|
1136
1048
|
},
|
|
1137
1049
|
},
|
|
1138
|
-
getLiquidTags:
|
|
1050
|
+
getLiquidTags: jest.fn((content, cb) => cb({ askAiraResponse: { data: [] }, isError: false })),
|
|
1139
1051
|
});
|
|
1140
1052
|
const input = screen.getByTestId('subject-input');
|
|
1141
1053
|
fireEvent.change(input, { target: { value: 'Valid Subject' } });
|
|
1142
1054
|
const changeButton = screen.getByTestId('trigger-content-change');
|
|
1143
1055
|
fireEvent.click(changeButton);
|
|
1144
|
-
// Now trigger save
|
|
1145
1056
|
rerender(
|
|
1146
1057
|
<IntlProvider locale="en" messages={{}}>
|
|
1147
1058
|
<EmailHTMLEditor
|
|
1148
1059
|
{...defaultProps}
|
|
1149
1060
|
onValidationFail={onValidationFail}
|
|
1150
1061
|
isGetFormData
|
|
1062
|
+
isFullMode={false}
|
|
1151
1063
|
metaEntities={{
|
|
1152
1064
|
tags: {
|
|
1153
1065
|
standard: [{ name: 'customer.name' }],
|
|
1154
1066
|
},
|
|
1155
1067
|
}}
|
|
1156
|
-
getLiquidTags={
|
|
1068
|
+
getLiquidTags={jest.fn((content, cb) => cb({ askAiraResponse: { data: [] }, isError: false }))} />
|
|
1157
1069
|
</IntlProvider>
|
|
1158
1070
|
);
|
|
1159
|
-
|
|
1160
1071
|
await waitFor(() => {
|
|
1161
|
-
expect(CapNotification.error).toHaveBeenCalled();
|
|
1162
1072
|
expect(onValidationFail).toHaveBeenCalled();
|
|
1163
1073
|
}, { timeout: 3000 });
|
|
1164
1074
|
});
|
|
@@ -1175,16 +1085,15 @@ describe('EmailHTMLEditor', () => {
|
|
|
1175
1085
|
// Ensure no HTML/Label/Liquid errors from HtmlEditor
|
|
1176
1086
|
mockGetAllIssues.mockReturnValue([]);
|
|
1177
1087
|
|
|
1178
|
-
//
|
|
1088
|
+
// Liquid validation runs only in library mode
|
|
1179
1089
|
const { rerender } = renderWithIntl({
|
|
1180
1090
|
isGetFormData: false,
|
|
1181
|
-
isFullMode:
|
|
1091
|
+
isFullMode: false,
|
|
1182
1092
|
metaEntities: {
|
|
1183
1093
|
tags: {
|
|
1184
1094
|
standard: [{ name: 'customer.name' }],
|
|
1185
1095
|
},
|
|
1186
1096
|
},
|
|
1187
|
-
isLiquidEnabled: true,
|
|
1188
1097
|
getLiquidTags,
|
|
1189
1098
|
});
|
|
1190
1099
|
const input = screen.getByTestId('subject-input');
|
|
@@ -1206,7 +1115,7 @@ describe('EmailHTMLEditor', () => {
|
|
|
1206
1115
|
<EmailHTMLEditor
|
|
1207
1116
|
{...defaultProps}
|
|
1208
1117
|
isGetFormData
|
|
1209
|
-
isFullMode
|
|
1118
|
+
isFullMode={false}
|
|
1210
1119
|
metaEntities={{
|
|
1211
1120
|
tags: {
|
|
1212
1121
|
standard: [{ name: 'customer.name' }],
|
|
@@ -1230,11 +1139,10 @@ describe('EmailHTMLEditor', () => {
|
|
|
1230
1139
|
// Ensure no HTML/Label/Liquid errors from HtmlEditor
|
|
1231
1140
|
mockGetAllIssues.mockReturnValue([]);
|
|
1232
1141
|
|
|
1233
|
-
//
|
|
1142
|
+
// Liquid validation runs only in library mode
|
|
1234
1143
|
const { rerender } = renderWithIntl({
|
|
1235
1144
|
isGetFormData: false,
|
|
1236
|
-
isFullMode:
|
|
1237
|
-
isLiquidEnabled: true,
|
|
1145
|
+
isFullMode: false,
|
|
1238
1146
|
getLiquidTags,
|
|
1239
1147
|
});
|
|
1240
1148
|
const input = screen.getByTestId('subject-input');
|
|
@@ -1253,7 +1161,7 @@ describe('EmailHTMLEditor', () => {
|
|
|
1253
1161
|
await act(async () => {
|
|
1254
1162
|
rerender(
|
|
1255
1163
|
<IntlProvider locale="en" messages={{}}>
|
|
1256
|
-
<EmailHTMLEditor {...defaultProps} isGetFormData isFullMode getLiquidTags={getLiquidTags} />
|
|
1164
|
+
<EmailHTMLEditor {...defaultProps} isGetFormData isFullMode={false} getLiquidTags={getLiquidTags} />
|
|
1257
1165
|
</IntlProvider>
|
|
1258
1166
|
);
|
|
1259
1167
|
});
|
|
@@ -1280,11 +1188,10 @@ describe('EmailHTMLEditor', () => {
|
|
|
1280
1188
|
// Ensure no HTML/Label/Liquid errors from HtmlEditor
|
|
1281
1189
|
mockGetAllIssues.mockReturnValue([]);
|
|
1282
1190
|
|
|
1283
|
-
//
|
|
1191
|
+
// Liquid validation runs only in library mode
|
|
1284
1192
|
const { rerender } = renderWithIntl({
|
|
1285
1193
|
isGetFormData: false,
|
|
1286
|
-
isFullMode:
|
|
1287
|
-
isLiquidEnabled: true,
|
|
1194
|
+
isFullMode: false,
|
|
1288
1195
|
getLiquidTags,
|
|
1289
1196
|
showLiquidErrorInFooter,
|
|
1290
1197
|
onValidationFail,
|
|
@@ -1305,7 +1212,7 @@ describe('EmailHTMLEditor', () => {
|
|
|
1305
1212
|
await act(async () => {
|
|
1306
1213
|
rerender(
|
|
1307
1214
|
<IntlProvider locale="en" messages={{}}>
|
|
1308
|
-
<EmailHTMLEditor {...defaultProps} isGetFormData isFullMode getLiquidTags={getLiquidTags} showLiquidErrorInFooter={showLiquidErrorInFooter} onValidationFail={onValidationFail} />
|
|
1215
|
+
<EmailHTMLEditor {...defaultProps} isGetFormData isFullMode={false} getLiquidTags={getLiquidTags} showLiquidErrorInFooter={showLiquidErrorInFooter} onValidationFail={onValidationFail} />
|
|
1309
1216
|
</IntlProvider>
|
|
1310
1217
|
);
|
|
1311
1218
|
});
|
|
@@ -1325,27 +1232,20 @@ describe('EmailHTMLEditor', () => {
|
|
|
1325
1232
|
return Promise.resolve(true);
|
|
1326
1233
|
});
|
|
1327
1234
|
|
|
1328
|
-
const
|
|
1329
|
-
...defaultProps.emailActions,
|
|
1330
|
-
transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
|
|
1331
|
-
createTemplate: jest.fn((obj, callback) => {
|
|
1332
|
-
callback({ templateId: { _id: '123', versions: {} } });
|
|
1333
|
-
}),
|
|
1334
|
-
};
|
|
1235
|
+
const getFormdata = jest.fn();
|
|
1335
1236
|
const getLiquidTags = jest.fn((content, callback) => {
|
|
1336
1237
|
callback({ askAiraResponse: { data: [] }, isError: false });
|
|
1337
1238
|
});
|
|
1338
1239
|
// Ensure no HTML/Label/Liquid errors from HtmlEditor
|
|
1339
1240
|
mockGetAllIssues.mockReturnValue([]);
|
|
1340
1241
|
|
|
1341
|
-
//
|
|
1242
|
+
// Liquid validation runs only in library mode; then performSave calls getFormdata
|
|
1342
1243
|
const { rerender } = renderWithIntl({
|
|
1343
1244
|
isGetFormData: false,
|
|
1344
|
-
isFullMode:
|
|
1345
|
-
isLiquidEnabled: true,
|
|
1245
|
+
isFullMode: false,
|
|
1346
1246
|
getLiquidTags,
|
|
1347
|
-
|
|
1348
|
-
|
|
1247
|
+
getFormdata,
|
|
1248
|
+
location: { query: { module: 'library' } },
|
|
1349
1249
|
});
|
|
1350
1250
|
const input = screen.getByTestId('subject-input');
|
|
1351
1251
|
await act(async () => {
|
|
@@ -1363,7 +1263,7 @@ describe('EmailHTMLEditor', () => {
|
|
|
1363
1263
|
await act(async () => {
|
|
1364
1264
|
rerender(
|
|
1365
1265
|
<IntlProvider locale="en" messages={{}}>
|
|
1366
|
-
<EmailHTMLEditor {...defaultProps} isGetFormData isFullMode getLiquidTags={getLiquidTags}
|
|
1266
|
+
<EmailHTMLEditor {...defaultProps} isGetFormData isFullMode={false} getLiquidTags={getLiquidTags} getFormdata={getFormdata} location={{ query: { module: 'library' } }} />
|
|
1367
1267
|
</IntlProvider>
|
|
1368
1268
|
);
|
|
1369
1269
|
});
|
|
@@ -1373,12 +1273,11 @@ describe('EmailHTMLEditor', () => {
|
|
|
1373
1273
|
}, { timeout: 5000 });
|
|
1374
1274
|
|
|
1375
1275
|
await waitFor(() => {
|
|
1376
|
-
expect(
|
|
1276
|
+
expect(getFormdata).toHaveBeenCalled();
|
|
1377
1277
|
}, { timeout: 5000 });
|
|
1378
1278
|
});
|
|
1379
1279
|
|
|
1380
1280
|
it('saves in full mode with create template', async () => {
|
|
1381
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1382
1281
|
const emailActions = {
|
|
1383
1282
|
...defaultProps.emailActions,
|
|
1384
1283
|
transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
|
|
@@ -1412,7 +1311,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1412
1311
|
});
|
|
1413
1312
|
|
|
1414
1313
|
it('saves in full mode with edit template', async () => {
|
|
1415
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1416
1314
|
const emailActions = {
|
|
1417
1315
|
...defaultProps.emailActions,
|
|
1418
1316
|
transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
|
|
@@ -1482,7 +1380,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1482
1380
|
});
|
|
1483
1381
|
|
|
1484
1382
|
it('handles create template error response', async () => {
|
|
1485
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1486
1383
|
const emailActions = {
|
|
1487
1384
|
...defaultProps.emailActions,
|
|
1488
1385
|
transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
|
|
@@ -1517,7 +1414,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1517
1414
|
});
|
|
1518
1415
|
|
|
1519
1416
|
it('handles create template success with getFormdata', async () => {
|
|
1520
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1521
1417
|
const emailActions = {
|
|
1522
1418
|
...defaultProps.emailActions,
|
|
1523
1419
|
transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
|
|
@@ -1554,7 +1450,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1554
1450
|
});
|
|
1555
1451
|
|
|
1556
1452
|
it('handles create template success without getFormdata', async () => {
|
|
1557
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1558
1453
|
const emailActions = {
|
|
1559
1454
|
...defaultProps.emailActions,
|
|
1560
1455
|
transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
|
|
@@ -1590,7 +1485,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1590
1485
|
});
|
|
1591
1486
|
|
|
1592
1487
|
it('saves in library mode with getFormdata', async () => {
|
|
1593
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1594
1488
|
const getFormdata = jest.fn();
|
|
1595
1489
|
|
|
1596
1490
|
// Set subject and content via component interactions
|
|
@@ -1618,7 +1512,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1618
1512
|
});
|
|
1619
1513
|
|
|
1620
1514
|
it('saves in library mode without library module', async () => {
|
|
1621
|
-
mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
|
|
1622
1515
|
const getFormdata = jest.fn();
|
|
1623
1516
|
|
|
1624
1517
|
// Set subject and content via component interactions
|
|
@@ -1758,7 +1651,6 @@ describe('EmailHTMLEditor', () => {
|
|
|
1758
1651
|
renderWithIntl({
|
|
1759
1652
|
getLiquidTags: null,
|
|
1760
1653
|
globalActions,
|
|
1761
|
-
isLiquidEnabled: true,
|
|
1762
1654
|
isGetFormData: true,
|
|
1763
1655
|
subject: 'Valid Subject',
|
|
1764
1656
|
htmlContent: '<p>Content</p>',
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
CapTooltip,
|
|
23
23
|
} from '@capillarytech/cap-ui-library';
|
|
24
24
|
import { FONT_SIZE_L } from '@capillarytech/cap-ui-library/styled/variables';
|
|
25
|
-
import _, { find, cloneDeep, findIndex, isEmpty, isEqual, filter,
|
|
25
|
+
import _, { find, cloneDeep, findIndex, isEmpty, isEqual, filter, replace } from 'lodash';
|
|
26
26
|
import * as actions from './actions';
|
|
27
27
|
import { makeSelectFTP, makeSelectMetaEntities } from './selectors';
|
|
28
28
|
import { makeSelectLoyaltyPromotionDisplay, setInjectedTags } from '../Cap/selectors';
|
|
@@ -33,7 +33,6 @@ import * as globalActions from '../Cap/actions';
|
|
|
33
33
|
import { TagList } from '../TagList';
|
|
34
34
|
import { NO_COMMUNICATION, CREATE, EDIT, PREVIEW } from '../App/constants';
|
|
35
35
|
import { getTreeStructuredTags } from '../../utils/common';
|
|
36
|
-
import { transformInjectedTags, checkIfSupportedTag, skipTags } from '../../utils/tagValidations';
|
|
37
36
|
import injectSaga from '../../utils/injectSaga';
|
|
38
37
|
import injectReducer from '../../utils/injectReducer';
|
|
39
38
|
|
|
@@ -235,63 +234,16 @@ export class FTP extends React.Component {
|
|
|
235
234
|
}));
|
|
236
235
|
};
|
|
237
236
|
|
|
238
|
-
getFlatTags = (tags) => {
|
|
239
|
-
const flatTags = [];
|
|
240
|
-
tags.forEach((tag) => {
|
|
241
|
-
if ((tag.children || []).length) {
|
|
242
|
-
const innerTags = this.getFlatTags(tag.children);
|
|
243
|
-
flatTags.push(innerTags);
|
|
244
|
-
} else {
|
|
245
|
-
flatTags.push(tag.value);
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
return flattenDeep(flatTags);
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
validateTags(content, tagsParam, injectedTagsParams) {
|
|
253
|
-
const tags = tagsParam;
|
|
254
|
-
const injectedTags = transformInjectedTags(injectedTagsParams);
|
|
255
|
-
const response = {};
|
|
256
|
-
response.valid = true;
|
|
257
|
-
response.unsupportedTags = [];
|
|
258
|
-
const flatTags = this.getFlatTags(tags);
|
|
259
|
-
if (flatTags && flatTags.length) {
|
|
260
|
-
const regex = /{{[(A-Z\w+(\s\w+)*$\(\)@!#$%^&*~.,/\\]+}}/g;
|
|
261
|
-
const matchedTags = [...content.matchAll(regex)];
|
|
262
|
-
matchedTags.forEach((tag) => {
|
|
263
|
-
let ifSupported = !!flatTags.find((t) => t === tag[0]);
|
|
264
|
-
const tagValue = tag[0].substring(this.indexOfEnd(tag[0], '{{'), tag[0].indexOf('}}'));
|
|
265
|
-
ifSupported = ifSupported || checkIfSupportedTag(tagValue, injectedTags) || skipTags(tagValue);
|
|
266
|
-
if (!ifSupported) {
|
|
267
|
-
response.unsupportedTags.push(tagValue);
|
|
268
|
-
response.valid = false;
|
|
269
|
-
}
|
|
270
|
-
if (response.unsupportedTags.length === 0) {
|
|
271
|
-
response.valid = true;
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
return response;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
237
|
indexOfEnd(targetString, string) {
|
|
279
238
|
const io = targetString.indexOf(string);
|
|
280
239
|
return io == -1 ? -1 : io + string.length;
|
|
281
240
|
}
|
|
282
241
|
|
|
283
242
|
getMessageContent = () => {
|
|
284
|
-
const { messageContent
|
|
243
|
+
const { messageContent } = this.state;
|
|
285
244
|
const { formatMessage } = this.props.intl;
|
|
286
245
|
const { metaEntities, selectedOfferDetails, injectedTags } = this.props;
|
|
287
246
|
const tagsRaw = metaEntities && metaEntities.tags ? metaEntities.tags.standard : [];
|
|
288
|
-
const validateTagResponse = !this.props?.isFullMode ? this.validateTags(messageContent, tagsTree, injectedTags) : { valid: true, unsupportedTags: [] };
|
|
289
|
-
let unsupportedTags = null;
|
|
290
|
-
let errorMessageText = '';
|
|
291
|
-
if (!validateTagResponse.valid) {
|
|
292
|
-
unsupportedTags = validateTagResponse.unsupportedTags.join(', ').toString();
|
|
293
|
-
errorMessageText = formatMessage(messages.unsupportedTagsValidationError, {unsupportedTags});
|
|
294
|
-
}
|
|
295
247
|
return (
|
|
296
248
|
<CapRow>
|
|
297
249
|
<CapColumn span={11}>
|
|
@@ -309,7 +261,6 @@ export class FTP extends React.Component {
|
|
|
309
261
|
label={formatMessage(messages.messageHeader)}
|
|
310
262
|
onChange={this.updateMessageBody}
|
|
311
263
|
value={messageContent}
|
|
312
|
-
errorMessage={errorMessageText}
|
|
313
264
|
/>
|
|
314
265
|
</div>
|
|
315
266
|
</CapColumn>
|
|
@@ -21,10 +21,6 @@ export default defineMessages({
|
|
|
21
21
|
id: 'creatives.containersV2.FTP.addColumn',
|
|
22
22
|
defaultMessage: 'Add column',
|
|
23
23
|
},
|
|
24
|
-
unsupportedTagsValidationError: {
|
|
25
|
-
id: 'creatives.containersV2.FTP.unsupportedTagsValidationError',
|
|
26
|
-
defaultMessage: 'Unsupported tags: {unsupportedTags}. Please remove them from this message.',
|
|
27
|
-
},
|
|
28
24
|
selectTag: {
|
|
29
25
|
id: 'creatives.containersV2.FTP.selectTag',
|
|
30
26
|
defaultMessage: 'Select tag',
|