@capillarytech/creatives-library 8.0.303 → 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.
Files changed (58) hide show
  1. package/constants/unified.js +3 -1
  2. package/initialState.js +0 -2
  3. package/package.json +1 -1
  4. package/utils/common.js +5 -8
  5. package/utils/commonUtils.js +36 -93
  6. package/utils/tagValidations.js +83 -223
  7. package/utils/tests/commonUtil.test.js +147 -124
  8. package/utils/tests/tagValidations.test.js +441 -358
  9. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -3
  10. package/v2Components/ErrorInfoNote/index.js +2 -5
  11. package/v2Components/FormBuilder/index.js +137 -203
  12. package/v2Components/FormBuilder/messages.js +0 -8
  13. package/v2Components/HtmlEditor/HTMLEditor.js +0 -6
  14. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
  15. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +0 -16
  16. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +1 -2
  17. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +3 -132
  18. package/v2Components/HtmlEditor/hooks/useValidation.js +9 -12
  19. package/v2Components/HtmlEditor/utils/htmlValidator.js +2 -4
  20. package/v2Containers/Cap/mockData.js +0 -14
  21. package/v2Containers/Cap/reducer.js +3 -55
  22. package/v2Containers/Cap/tests/reducer.test.js +0 -102
  23. package/v2Containers/CreativesContainer/SlideBoxContent.js +5 -1
  24. package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -5
  25. package/v2Containers/CreativesContainer/constants.js +6 -0
  26. package/v2Containers/CreativesContainer/index.js +47 -7
  27. package/v2Containers/Email/index.js +1 -5
  28. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +23 -70
  29. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +19 -219
  30. package/v2Containers/FTP/index.js +2 -51
  31. package/v2Containers/FTP/messages.js +0 -4
  32. package/v2Containers/InApp/index.js +33 -106
  33. package/v2Containers/InApp/tests/index.test.js +17 -6
  34. package/v2Containers/InappAdvance/index.js +6 -110
  35. package/v2Containers/InappAdvance/tests/index.test.js +2 -0
  36. package/v2Containers/Line/Container/Text/index.js +0 -1
  37. package/v2Containers/MobilePush/Create/index.js +59 -19
  38. package/v2Containers/MobilePush/Edit/index.js +48 -20
  39. package/v2Containers/MobilePushNew/index.js +12 -32
  40. package/v2Containers/MobilepushWrapper/index.js +3 -1
  41. package/v2Containers/Rcs/index.js +12 -37
  42. package/v2Containers/Sms/Create/index.js +39 -3
  43. package/v2Containers/Sms/Create/messages.js +4 -0
  44. package/v2Containers/Sms/Edit/index.js +35 -3
  45. package/v2Containers/Sms/commonMethods.js +3 -6
  46. package/v2Containers/Sms/tests/commonMethods.test.js +122 -0
  47. package/v2Containers/SmsTrai/Edit/index.js +11 -47
  48. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
  49. package/v2Containers/SmsWrapper/index.js +2 -0
  50. package/v2Containers/TemplatesV2/index.js +28 -13
  51. package/v2Containers/Viber/index.js +0 -1
  52. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -3
  53. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
  54. package/v2Containers/WebPush/Create/index.js +2 -2
  55. package/v2Containers/WebPush/Create/utils/validation.js +17 -8
  56. package/v2Containers/WebPush/Create/utils/validation.test.js +44 -24
  57. package/v2Containers/Whatsapp/index.js +9 -17
  58. 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 { isEmailUnsubscribeTagMandatory } from '../../../../utils/common';
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
- hasLiquidSupportFeature: (...args) => mockHasLiquidSupportFeature(...args),
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
- isEmailUnsubscribeTagMandatory.mockReturnValue(false);
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
- isEmailUnsubscribeTagMandatory.mockReturnValue(false);
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 for non-liquid orgs when tag validation fails', async () => {
1120
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1121
- validateTags.mockReturnValue({
1122
- valid: false,
1123
- unsupportedTags: ['tag1'],
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: null, // No liquid tags for non-liquid org
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={null} />
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,8 +1085,7 @@ describe('EmailHTMLEditor', () => {
1175
1085
  // Ensure no HTML/Label/Liquid errors from HtmlEditor
1176
1086
  mockGetAllIssues.mockReturnValue([]);
1177
1087
 
1178
- // Set subject and content via component interactions
1179
- // Use isFullMode: false (library mode) to test liquid validation path
1088
+ // Liquid validation runs only in library mode
1180
1089
  const { rerender } = renderWithIntl({
1181
1090
  isGetFormData: false,
1182
1091
  isFullMode: false,
@@ -1185,7 +1094,6 @@ describe('EmailHTMLEditor', () => {
1185
1094
  standard: [{ name: 'customer.name' }],
1186
1095
  },
1187
1096
  },
1188
- isLiquidEnabled: true,
1189
1097
  getLiquidTags,
1190
1098
  });
1191
1099
  const input = screen.getByTestId('subject-input');
@@ -1231,12 +1139,10 @@ describe('EmailHTMLEditor', () => {
1231
1139
  // Ensure no HTML/Label/Liquid errors from HtmlEditor
1232
1140
  mockGetAllIssues.mockReturnValue([]);
1233
1141
 
1234
- // Set subject and content via component interactions
1235
- // Use isFullMode: false (library mode) to test liquid validation path
1142
+ // Liquid validation runs only in library mode
1236
1143
  const { rerender } = renderWithIntl({
1237
1144
  isGetFormData: false,
1238
1145
  isFullMode: false,
1239
- isLiquidEnabled: true,
1240
1146
  getLiquidTags,
1241
1147
  });
1242
1148
  const input = screen.getByTestId('subject-input');
@@ -1282,12 +1188,10 @@ describe('EmailHTMLEditor', () => {
1282
1188
  // Ensure no HTML/Label/Liquid errors from HtmlEditor
1283
1189
  mockGetAllIssues.mockReturnValue([]);
1284
1190
 
1285
- // Set subject and content via component interactions
1286
- // Use isFullMode: false (library mode) to test liquid validation path
1191
+ // Liquid validation runs only in library mode
1287
1192
  const { rerender } = renderWithIntl({
1288
1193
  isGetFormData: false,
1289
1194
  isFullMode: false,
1290
- isLiquidEnabled: true,
1291
1195
  getLiquidTags,
1292
1196
  showLiquidErrorInFooter,
1293
1197
  onValidationFail,
@@ -1335,14 +1239,13 @@ describe('EmailHTMLEditor', () => {
1335
1239
  // Ensure no HTML/Label/Liquid errors from HtmlEditor
1336
1240
  mockGetAllIssues.mockReturnValue([]);
1337
1241
 
1338
- // Set subject and content via component interactions
1339
- // Use isFullMode: false (library mode) to test liquid validation path
1242
+ // Liquid validation runs only in library mode; then performSave calls getFormdata
1340
1243
  const { rerender } = renderWithIntl({
1341
1244
  isGetFormData: false,
1342
1245
  isFullMode: false,
1343
1246
  getLiquidTags,
1344
1247
  getFormdata,
1345
- templateName: 'New Template',
1248
+ location: { query: { module: 'library' } },
1346
1249
  });
1347
1250
  const input = screen.getByTestId('subject-input');
1348
1251
  await act(async () => {
@@ -1360,7 +1263,7 @@ describe('EmailHTMLEditor', () => {
1360
1263
  await act(async () => {
1361
1264
  rerender(
1362
1265
  <IntlProvider locale="en" messages={{}}>
1363
- <EmailHTMLEditor {...defaultProps} isGetFormData isFullMode={false} getLiquidTags={getLiquidTags} getFormdata={getFormdata} templateName="New Template" />
1266
+ <EmailHTMLEditor {...defaultProps} isGetFormData isFullMode={false} getLiquidTags={getLiquidTags} getFormdata={getFormdata} location={{ query: { module: 'library' } }} />
1364
1267
  </IntlProvider>
1365
1268
  );
1366
1269
  });
@@ -1369,14 +1272,12 @@ describe('EmailHTMLEditor', () => {
1369
1272
  expect(validateLiquidTemplateContent).toHaveBeenCalled();
1370
1273
  }, { timeout: 5000 });
1371
1274
 
1372
- // In library mode (isFullMode=false), save proceeds via getFormdata
1373
1275
  await waitFor(() => {
1374
1276
  expect(getFormdata).toHaveBeenCalled();
1375
1277
  }, { timeout: 5000 });
1376
1278
  });
1377
1279
 
1378
1280
  it('saves in full mode with create template', async () => {
1379
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1380
1281
  const emailActions = {
1381
1282
  ...defaultProps.emailActions,
1382
1283
  transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
@@ -1410,7 +1311,6 @@ describe('EmailHTMLEditor', () => {
1410
1311
  });
1411
1312
 
1412
1313
  it('saves in full mode with edit template', async () => {
1413
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1414
1314
  const emailActions = {
1415
1315
  ...defaultProps.emailActions,
1416
1316
  transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
@@ -1480,7 +1380,6 @@ describe('EmailHTMLEditor', () => {
1480
1380
  });
1481
1381
 
1482
1382
  it('handles create template error response', async () => {
1483
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1484
1383
  const emailActions = {
1485
1384
  ...defaultProps.emailActions,
1486
1385
  transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
@@ -1515,7 +1414,6 @@ describe('EmailHTMLEditor', () => {
1515
1414
  });
1516
1415
 
1517
1416
  it('handles create template success with getFormdata', async () => {
1518
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1519
1417
  const emailActions = {
1520
1418
  ...defaultProps.emailActions,
1521
1419
  transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
@@ -1552,7 +1450,6 @@ describe('EmailHTMLEditor', () => {
1552
1450
  });
1553
1451
 
1554
1452
  it('handles create template success without getFormdata', async () => {
1555
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1556
1453
  const emailActions = {
1557
1454
  ...defaultProps.emailActions,
1558
1455
  transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
@@ -1588,7 +1485,6 @@ describe('EmailHTMLEditor', () => {
1588
1485
  });
1589
1486
 
1590
1487
  it('saves in library mode with getFormdata', async () => {
1591
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1592
1488
  const getFormdata = jest.fn();
1593
1489
 
1594
1490
  // Set subject and content via component interactions
@@ -1616,7 +1512,6 @@ describe('EmailHTMLEditor', () => {
1616
1512
  });
1617
1513
 
1618
1514
  it('saves in library mode without library module', async () => {
1619
- mockHasLiquidSupportFeature.mockReturnValue(false); // Non-liquid org
1620
1515
  const getFormdata = jest.fn();
1621
1516
 
1622
1517
  // Set subject and content via component interactions
@@ -1756,7 +1651,6 @@ describe('EmailHTMLEditor', () => {
1756
1651
  renderWithIntl({
1757
1652
  getLiquidTags: null,
1758
1653
  globalActions,
1759
- isLiquidEnabled: true,
1760
1654
  isGetFormData: true,
1761
1655
  subject: 'Valid Subject',
1762
1656
  htmlContent: '<p>Content</p>',
@@ -2709,98 +2603,4 @@ describe('EmailHTMLEditor', () => {
2709
2603
  expect(screen.getByTestId('html-editor')).toBeInTheDocument();
2710
2604
  });
2711
2605
  });
2712
-
2713
- describe('isFullMode - skip liquid validation on save', () => {
2714
- it('skips liquid validation when isFullMode is true', async () => {
2715
- validateLiquidTemplateContent.mockClear();
2716
- const getLiquidTags = jest.fn((content, callback) => {
2717
- callback({ askAiraResponse: { data: [] }, isError: false });
2718
- });
2719
- const getFormdata = jest.fn();
2720
- // Ensure no HTML/Label/Liquid errors from HtmlEditor
2721
- mockGetAllIssues.mockReturnValue([]);
2722
-
2723
- const { rerender } = renderWithIntl({
2724
- isGetFormData: false,
2725
- isFullMode: true,
2726
- isLiquidEnabled: true,
2727
- getLiquidTags,
2728
- getFormdata,
2729
- });
2730
- const input = screen.getByTestId('subject-input');
2731
- await act(async () => {
2732
- fireEvent.change(input, { target: { value: 'Valid Subject' } });
2733
- });
2734
- const changeButton = screen.getByTestId('trigger-content-change');
2735
- await act(async () => {
2736
- fireEvent.click(changeButton);
2737
- });
2738
- await act(async () => {
2739
- await new Promise((resolve) => setTimeout(resolve, 100));
2740
- });
2741
- // Trigger save
2742
- await act(async () => {
2743
- rerender(
2744
- <IntlProvider locale="en" messages={{}}>
2745
- <EmailHTMLEditor {...defaultProps} isGetFormData isFullMode getLiquidTags={getLiquidTags} getFormdata={getFormdata} />
2746
- </IntlProvider>
2747
- );
2748
- });
2749
-
2750
- // In full mode, liquid validation should be skipped entirely
2751
- await act(async () => {
2752
- await new Promise((resolve) => setTimeout(resolve, 200));
2753
- });
2754
- expect(validateLiquidTemplateContent).not.toHaveBeenCalled();
2755
- });
2756
-
2757
- it('proceeds directly to save without liquid validation in full mode', async () => {
2758
- validateLiquidTemplateContent.mockClear();
2759
- const getLiquidTags = jest.fn((content, callback) => {
2760
- callback({ askAiraResponse: { data: [] }, isError: false });
2761
- });
2762
- const emailActions = {
2763
- ...defaultProps.emailActions,
2764
- transformEmailTemplate: jest.fn((obj, callback) => callback(obj)),
2765
- createTemplate: jest.fn((obj, callback) => {
2766
- callback({ templateId: { _id: '123', versions: {} } });
2767
- }),
2768
- };
2769
- // Ensure no HTML/Label/Liquid errors from HtmlEditor
2770
- mockGetAllIssues.mockReturnValue([]);
2771
-
2772
- const { rerender } = renderWithIntl({
2773
- isGetFormData: false,
2774
- isFullMode: true,
2775
- getLiquidTags,
2776
- emailActions,
2777
- templateName: 'New Template',
2778
- });
2779
- const input = screen.getByTestId('subject-input');
2780
- await act(async () => {
2781
- fireEvent.change(input, { target: { value: 'Valid Subject' } });
2782
- });
2783
- const changeButton = screen.getByTestId('trigger-content-change');
2784
- await act(async () => {
2785
- fireEvent.click(changeButton);
2786
- });
2787
- await act(async () => {
2788
- await new Promise((resolve) => setTimeout(resolve, 100));
2789
- });
2790
- // Trigger save
2791
- await act(async () => {
2792
- rerender(
2793
- <IntlProvider locale="en" messages={{}}>
2794
- <EmailHTMLEditor {...defaultProps} isGetFormData isFullMode getLiquidTags={getLiquidTags} emailActions={emailActions} templateName="New Template" />
2795
- </IntlProvider>
2796
- );
2797
- });
2798
-
2799
- // Should skip liquid validation and proceed to save directly
2800
- await waitFor(() => {
2801
- expect(emailActions.createTemplate).toHaveBeenCalled();
2802
- }, { timeout: 5000 });
2803
- expect(validateLiquidTemplateContent).not.toHaveBeenCalled();
2804
- });
2805
- });
2806
2606
  });
@@ -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, flattenDeep, replace } from 'lodash';
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, tagsTree = []} = this.state;
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',