@capillarytech/creatives-library 8.0.293 → 8.0.295

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 (40) hide show
  1. package/package.json +1 -1
  2. package/v2Components/CommonTestAndPreview/SendTestMessage.js +1 -51
  3. package/v2Components/CommonTestAndPreview/actions.js +0 -20
  4. package/v2Components/CommonTestAndPreview/constants.js +0 -10
  5. package/v2Components/CommonTestAndPreview/index.js +15 -148
  6. package/v2Components/CommonTestAndPreview/reducer.js +0 -47
  7. package/v2Components/CommonTestAndPreview/sagas.js +0 -60
  8. package/v2Components/CommonTestAndPreview/selectors.js +0 -51
  9. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -135
  10. package/v2Components/CommonTestAndPreview/tests/actions.test.js +0 -50
  11. package/v2Components/CommonTestAndPreview/tests/constants.test.js +0 -18
  12. package/v2Components/CommonTestAndPreview/tests/index.test.js +1 -342
  13. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -118
  14. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -145
  15. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -146
  16. package/v2Components/FormBuilder/index.js +1 -1
  17. package/v2Components/HtmlEditor/HTMLEditor.js +1 -0
  18. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1 -0
  19. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +132 -3
  20. package/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
  21. package/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
  22. package/v2Components/TestAndPreviewSlidebox/index.js +0 -14
  23. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +2 -2
  24. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +110 -18
  25. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -697
  26. package/v2Containers/SmsTrai/Edit/index.js +1 -5
  27. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -201
  28. package/v2Containers/Whatsapp/index.js +1 -1
  29. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4225 -26242
  30. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +0 -33
  31. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +0 -422
  32. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +0 -35
  33. package/v2Components/CommonTestAndPreview/DeliverySettings/TECH_DETAILING_DELIVERY_SETTINGS.md +0 -725
  34. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -92
  35. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +0 -251
  36. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -111
  37. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +0 -91
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +0 -889
  39. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +0 -222
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -235
@@ -35,13 +35,6 @@ import {
35
35
  GET_PREFILLED_VALUES_FAILURE,
36
36
  CLEAR_PREFILLED_VALUES,
37
37
  CLEAR_PREVIEW_ERRORS,
38
- GET_SENDER_DETAILS_REQUESTED,
39
- GET_SENDER_DETAILS_SUCCESS,
40
- GET_SENDER_DETAILS_FAILURE,
41
- GET_WECRM_ACCOUNTS_REQUESTED,
42
- GET_WECRM_ACCOUNTS_SUCCESS,
43
- GET_WECRM_ACCOUNTS_FAILURE,
44
- CHANNELS,
45
38
  } from '../constants';
46
39
 
47
40
  describe('previewAndTestReducer', () => {
@@ -87,15 +80,6 @@ describe('previewAndTestReducer', () => {
87
80
  isFetchingPrefilledValues: false,
88
81
  fetchPrefilledValuesError: null,
89
82
  fetchPrefilledValuesErrors: [],
90
- senderDetailsByChannel: fromJS({
91
- [CHANNELS.SMS]: [],
92
- [CHANNELS.EMAIL]: [],
93
- [CHANNELS.WHATSAPP]: [],
94
- }),
95
- wecrmAccounts: fromJS([]),
96
- isLoadingSenderDetails: false,
97
- fetchSenderDetailsError: null,
98
- fetchWeCrmAccountsError: null,
99
83
  });
100
84
  });
101
85
 
@@ -789,108 +773,6 @@ describe('previewAndTestReducer', () => {
789
773
  });
790
774
  });
791
775
 
792
- describe('GET_SENDER_DETAILS_REQUESTED', () => {
793
- it('should set isLoadingSenderDetails to true and clear fetchSenderDetailsError', () => {
794
- const action = {
795
- type: GET_SENDER_DETAILS_REQUESTED,
796
- payload: { channel: CHANNELS.SMS, orgUnitId: 123 },
797
- };
798
- const result = previewAndTestReducer(initialState, action);
799
-
800
- expect(result.get('isLoadingSenderDetails')).toBe(true);
801
- expect(result.get('fetchSenderDetailsError')).toBeNull();
802
- });
803
- });
804
-
805
- describe('GET_SENDER_DETAILS_SUCCESS', () => {
806
- it('should store domains for channel and set loading to false', () => {
807
- const domains = [
808
- { domainId: 1, domainName: 'Domain 1', gsmSenders: [] },
809
- ];
810
- const action = {
811
- type: GET_SENDER_DETAILS_SUCCESS,
812
- payload: { channel: CHANNELS.SMS, domains },
813
- };
814
- const result = previewAndTestReducer(initialState, action);
815
-
816
- expect(result.getIn(['senderDetailsByChannel', CHANNELS.SMS])).toEqual(fromJS(domains));
817
- expect(result.get('isLoadingSenderDetails')).toBe(false);
818
- expect(result.get('fetchSenderDetailsError')).toBeNull();
819
- });
820
-
821
- it('should return state unchanged when channel is missing', () => {
822
- const action = {
823
- type: GET_SENDER_DETAILS_SUCCESS,
824
- payload: { domains: [] },
825
- };
826
- const result = previewAndTestReducer(initialState, action);
827
-
828
- expect(result).toEqual(initialState);
829
- });
830
- });
831
-
832
- describe('GET_SENDER_DETAILS_FAILURE', () => {
833
- it('should set loading to false and store error', () => {
834
- const action = {
835
- type: GET_SENDER_DETAILS_FAILURE,
836
- payload: { channel: CHANNELS.EMAIL, error: 'Failed to fetch' },
837
- };
838
- const result = previewAndTestReducer(initialState, action);
839
-
840
- expect(result.get('isLoadingSenderDetails')).toBe(false);
841
- expect(result.get('fetchSenderDetailsError')).toBe('Failed to fetch');
842
- });
843
- });
844
-
845
- describe('GET_WECRM_ACCOUNTS_REQUESTED', () => {
846
- it('should clear fetchWeCrmAccountsError', () => {
847
- const stateWithError = initialState.set('fetchWeCrmAccountsError', 'Error');
848
- const action = {
849
- type: GET_WECRM_ACCOUNTS_REQUESTED,
850
- payload: { sourceName: 'WHATSAPP' },
851
- };
852
- const result = previewAndTestReducer(stateWithError, action);
853
-
854
- expect(result.get('fetchWeCrmAccountsError')).toBeNull();
855
- });
856
- });
857
-
858
- describe('GET_WECRM_ACCOUNTS_SUCCESS', () => {
859
- it('should store wecrm accounts and clear error', () => {
860
- const accounts = [{ name: 'Account 1', sourceAccountIdentifier: 'acc-1' }];
861
- const action = {
862
- type: GET_WECRM_ACCOUNTS_SUCCESS,
863
- payload: { accounts },
864
- };
865
- const result = previewAndTestReducer(initialState, action);
866
-
867
- expect(result.get('wecrmAccounts')).toEqual(fromJS(accounts));
868
- expect(result.get('fetchWeCrmAccountsError')).toBeNull();
869
- });
870
-
871
- it('should handle empty accounts', () => {
872
- const action = {
873
- type: GET_WECRM_ACCOUNTS_SUCCESS,
874
- payload: { accounts: [] },
875
- };
876
- const result = previewAndTestReducer(initialState, action);
877
-
878
- expect(result.get('wecrmAccounts')).toEqual(fromJS([]));
879
- });
880
- });
881
-
882
- describe('GET_WECRM_ACCOUNTS_FAILURE', () => {
883
- it('should store fetchWeCrmAccountsError', () => {
884
- const action = {
885
- type: GET_WECRM_ACCOUNTS_FAILURE,
886
- payload: { error: 'WeCRM fetch failed' },
887
- };
888
- const result = previewAndTestReducer(initialState, action);
889
-
890
- expect(result.get('fetchWeCrmAccountsError')).toBe('WeCRM fetch failed');
891
- });
892
- });
893
-
894
776
  describe('CLEAR_PREVIEW_ERRORS', () => {
895
777
  it('should clear all preview-related errors', () => {
896
778
  const stateWithErrors = initialState
@@ -25,17 +25,9 @@ import {
25
25
  watchFetchTestGroups,
26
26
  watchCreateMessageMeta,
27
27
  watchGetPrefilledValues,
28
- getSenderDetailsSaga,
29
- getWeCrmAccountsSaga,
30
- watchGetSenderDetails,
31
- watchGetWeCrmAccounts,
32
28
  commonTestAndPreviewSaga,
33
29
  } from '../sagas';
34
30
  import * as Api from '../../../services/api';
35
- jest.mock('../DeliverySettings/utils/parseSenderDetailsResponse', () => ({
36
- parseSenderDetailsResponse: jest.fn((channel, response) => ({ domains: response?.entity?.[channel] || [] })),
37
- }));
38
- import { parseSenderDetailsResponse } from '../DeliverySettings/utils/parseSenderDetailsResponse';
39
31
  import {
40
32
  SEARCH_CUSTOMERS_SUCCESS,
41
33
  SEARCH_CUSTOMERS_FAILURE,
@@ -53,10 +45,6 @@ import {
53
45
  CREATE_MESSAGE_META_FAILURE,
54
46
  GET_PREFILLED_VALUES_SUCCESS,
55
47
  GET_PREFILLED_VALUES_FAILURE,
56
- GET_SENDER_DETAILS_SUCCESS,
57
- GET_SENDER_DETAILS_FAILURE,
58
- GET_WECRM_ACCOUNTS_SUCCESS,
59
- GET_WECRM_ACCOUNTS_FAILURE,
60
48
  } from '../constants';
61
49
 
62
50
  describe('CommonTestAndPreview Sagas', () => {
@@ -1706,137 +1694,6 @@ describe('CommonTestAndPreview Sagas', () => {
1706
1694
  );
1707
1695
  expect(generator.next().done).toBe(true);
1708
1696
  });
1709
-
1710
- it('should watch for GET_SENDER_DETAILS_REQUESTED', () => {
1711
- const generator = watchGetSenderDetails();
1712
- expect(generator.next().value).toEqual(
1713
- takeLatest('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED', getSenderDetailsSaga)
1714
- );
1715
- expect(generator.next().done).toBe(true);
1716
- });
1717
-
1718
- it('should watch for GET_WECRM_ACCOUNTS_REQUESTED', () => {
1719
- const generator = watchGetWeCrmAccounts();
1720
- expect(generator.next().value).toEqual(
1721
- takeLatest('app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_REQUESTED', getWeCrmAccountsSaga)
1722
- );
1723
- expect(generator.next().done).toBe(true);
1724
- });
1725
- });
1726
-
1727
- describe('getSenderDetailsSaga', () => {
1728
- it('should fetch sender details and put SUCCESS with parsed domains', () => {
1729
- const action = { payload: { channel: 'SMS', orgUnitId: 100 } };
1730
- const apiResponse = { entity: { SMS: [{ id: 1, domainProperties: {} }] } };
1731
- parseSenderDetailsResponse.mockReturnValue({ domains: [{ domainId: 1 }] });
1732
-
1733
- const generator = getSenderDetailsSaga(action);
1734
-
1735
- expect(generator.next().value).toEqual(call(Api.getSenderDetails, 'SMS', 100));
1736
- expect(generator.next(apiResponse).value).toEqual(
1737
- put({ type: GET_SENDER_DETAILS_SUCCESS, payload: { channel: 'SMS', domains: [{ domainId: 1 }] } })
1738
- );
1739
- expect(generator.next().done).toBe(true);
1740
- });
1741
-
1742
- it('should use orgUnitId -1 when not provided', () => {
1743
- const action = { payload: { channel: 'EMAIL' } };
1744
- const apiResponse = { entity: { EMAIL: [] } };
1745
- parseSenderDetailsResponse.mockReturnValue({ domains: [] });
1746
-
1747
- const generator = getSenderDetailsSaga(action);
1748
-
1749
- expect(generator.next().value).toEqual(call(Api.getSenderDetails, 'EMAIL', -1));
1750
- generator.next(apiResponse);
1751
- expect(generator.next().done).toBe(true);
1752
- });
1753
-
1754
- it('should return early when channel is missing', () => {
1755
- const action = { payload: {} };
1756
- const generator = getSenderDetailsSaga(action);
1757
- expect(generator.next().done).toBe(true);
1758
- });
1759
-
1760
- it('should put FAILURE when response has errors', () => {
1761
- const action = { payload: { channel: 'SMS' } };
1762
- const apiResponse = { errors: [{ message: 'Forbidden' }] };
1763
-
1764
- const generator = getSenderDetailsSaga(action);
1765
- generator.next();
1766
- expect(generator.next(apiResponse).value).toEqual(
1767
- put({
1768
- type: GET_SENDER_DETAILS_FAILURE,
1769
- payload: { channel: 'SMS', error: [{ message: 'Forbidden' }] },
1770
- })
1771
- );
1772
- expect(generator.next().done).toBe(true);
1773
- });
1774
-
1775
- it('should put FAILURE on throw', () => {
1776
- const action = { payload: { channel: 'WHATSAPP' } };
1777
- const generator = getSenderDetailsSaga(action);
1778
- generator.next();
1779
- const err = generator.throw(new Error('Network error'));
1780
- expect(err.value).toEqual(
1781
- put({
1782
- type: GET_SENDER_DETAILS_FAILURE,
1783
- payload: { channel: 'WHATSAPP', error: 'Network error' },
1784
- })
1785
- );
1786
- expect(generator.next().done).toBe(true);
1787
- });
1788
- });
1789
-
1790
- describe('getWeCrmAccountsSaga', () => {
1791
- it('should fetch wecrm accounts and put SUCCESS with list from response', () => {
1792
- const action = { payload: { sourceName: 'WHATSAPP' } };
1793
- const apiResult = { response: [{ name: 'WABA1', sourceAccountIdentifier: 'waba-1' }] };
1794
-
1795
- const generator = getWeCrmAccountsSaga(action);
1796
-
1797
- expect(generator.next().value).toEqual(call(Api.fetchWeCrmAccounts, 'WHATSAPP'));
1798
- expect(generator.next(apiResult).value).toEqual(
1799
- put({ type: GET_WECRM_ACCOUNTS_SUCCESS, payload: { accounts: [{ name: 'WABA1', sourceAccountIdentifier: 'waba-1' }] } })
1800
- );
1801
- expect(generator.next().done).toBe(true);
1802
- });
1803
-
1804
- it('should use WHATSAPP when sourceName not provided', () => {
1805
- const action = { payload: {} };
1806
- const generator = getWeCrmAccountsSaga(action);
1807
- expect(generator.next().value).toEqual(call(Api.fetchWeCrmAccounts, 'WHATSAPP'));
1808
- });
1809
-
1810
- it('should use entity when response not present', () => {
1811
- const action = { payload: {} };
1812
- const apiResult = { entity: [{ name: 'Acc1' }] };
1813
-
1814
- const generator = getWeCrmAccountsSaga(action);
1815
- generator.next();
1816
- expect(generator.next(apiResult).value).toEqual(
1817
- put({ type: GET_WECRM_ACCOUNTS_SUCCESS, payload: { accounts: [{ name: 'Acc1' }] } })
1818
- );
1819
- });
1820
-
1821
- it('should put empty array when result has no list', () => {
1822
- const action = { payload: {} };
1823
- const generator = getWeCrmAccountsSaga(action);
1824
- generator.next();
1825
- expect(generator.next({}).value).toEqual(
1826
- put({ type: GET_WECRM_ACCOUNTS_SUCCESS, payload: { accounts: [] } })
1827
- );
1828
- });
1829
-
1830
- it('should put FAILURE on throw', () => {
1831
- const action = { payload: {} };
1832
- const generator = getWeCrmAccountsSaga(action);
1833
- generator.next();
1834
- const err = generator.throw(new Error('API failed'));
1835
- expect(err.value).toEqual(
1836
- put({ type: GET_WECRM_ACCOUNTS_FAILURE, payload: { error: 'API failed' } })
1837
- );
1838
- expect(generator.next().done).toBe(true);
1839
- });
1840
1697
  });
1841
1698
 
1842
1699
  describe('commonTestAndPreviewSaga', () => {
@@ -1853,8 +1710,6 @@ describe('CommonTestAndPreview Sagas', () => {
1853
1710
  watchFetchTestGroups(),
1854
1711
  watchCreateMessageMeta(),
1855
1712
  watchGetPrefilledValues(),
1856
- watchGetSenderDetails(),
1857
- watchGetWeCrmAccounts(),
1858
1713
  ])
1859
1714
  );
1860
1715
 
@@ -32,12 +32,6 @@ import {
32
32
  makeSelectUpdatePreviewErrors,
33
33
  makeSelectFetchPrefilledValuesError,
34
34
  makeSelectFetchPrefilledValuesErrors,
35
- makeSelectSenderDetailsForChannel,
36
- makeSelectSenderDetailsByChannel,
37
- makeSelectWeCrmAccounts,
38
- makeSelectIsLoadingSenderDetails,
39
- makeSelectFetchSenderDetailsError,
40
- makeSelectFetchWeCrmAccountsError,
41
35
  } from '../selectors';
42
36
 
43
37
  describe('CommonTestAndPreview Selectors', () => {
@@ -107,19 +101,6 @@ describe('CommonTestAndPreview Selectors', () => {
107
101
  fetchPrefilledValuesErrors: [
108
102
  { code: 7078, message: 'User profile not found' },
109
103
  ],
110
- senderDetailsByChannel: {
111
- SMS: [
112
- { domainId: 1, domainName: 'SMS Domain', gsmSenders: [{ value: 'sender1' }] },
113
- ],
114
- EMAIL: [],
115
- WHATSAPP: [],
116
- },
117
- wecrmAccounts: [
118
- { name: 'WeCRM Account 1', sourceAccountIdentifier: 'waba-1' },
119
- ],
120
- isLoadingSenderDetails: false,
121
- fetchSenderDetailsError: null,
122
- fetchWeCrmAccountsError: null,
123
104
  },
124
105
  });
125
106
 
@@ -569,133 +550,6 @@ describe('CommonTestAndPreview Selectors', () => {
569
550
  });
570
551
  });
571
552
 
572
- describe('makeSelectSenderDetailsForChannel', () => {
573
- it('should select sender details for given channel as JS array', () => {
574
- const selector = makeSelectSenderDetailsForChannel('SMS');
575
- const result = selector(mockState);
576
-
577
- expect(result).toBeDefined();
578
- expect(Array.isArray(result)).toBe(true);
579
- expect(result.length).toBe(1);
580
- expect(result[0].domainName).toBe('SMS Domain');
581
- });
582
-
583
- it('should return empty array for channel with no data', () => {
584
- const selector = makeSelectSenderDetailsForChannel('EMAIL');
585
- const result = selector(mockState);
586
-
587
- expect(result).toEqual([]);
588
- });
589
-
590
- it('should return empty array when substate or byChannel missing', () => {
591
- const emptyState = fromJS({});
592
- const selector = makeSelectSenderDetailsForChannel('SMS');
593
- const result = selector(emptyState);
594
-
595
- expect(result).toEqual([]);
596
- });
597
- });
598
-
599
- describe('makeSelectSenderDetailsByChannel', () => {
600
- it('should select senderDetailsByChannel and convert to JS object', () => {
601
- const selector = makeSelectSenderDetailsByChannel();
602
- const result = selector(mockState);
603
-
604
- expect(result).toBeDefined();
605
- expect(result.SMS).toBeDefined();
606
- expect(Array.isArray(result.SMS)).toBe(true);
607
- expect(result.SMS[0].domainName).toBe('SMS Domain');
608
- expect(result.EMAIL).toEqual([]);
609
- expect(result.WHATSAPP).toEqual([]);
610
- });
611
-
612
- it('should return empty object when domain is missing', () => {
613
- const emptyState = fromJS({});
614
- const selector = makeSelectSenderDetailsByChannel();
615
- const result = selector(emptyState);
616
-
617
- expect(result).toEqual({});
618
- });
619
- });
620
-
621
- describe('makeSelectWeCrmAccounts', () => {
622
- it('should select wecrmAccounts and convert to JS array', () => {
623
- const selector = makeSelectWeCrmAccounts();
624
- const result = selector(mockState);
625
-
626
- expect(result).toBeDefined();
627
- expect(Array.isArray(result)).toBe(true);
628
- expect(result.length).toBe(1);
629
- expect(result[0].name).toBe('WeCRM Account 1');
630
- expect(result[0].sourceAccountIdentifier).toBe('waba-1');
631
- });
632
-
633
- it('should return empty array when wecrmAccounts is missing', () => {
634
- const emptyState = fromJS({ commonTestAndPreview: {} });
635
- const selector = makeSelectWeCrmAccounts();
636
- const result = selector(emptyState);
637
-
638
- expect(result).toEqual([]);
639
- });
640
- });
641
-
642
- describe('makeSelectIsLoadingSenderDetails', () => {
643
- it('should select isLoadingSenderDetails flag', () => {
644
- const selector = makeSelectIsLoadingSenderDetails();
645
- const result = selector(mockState);
646
-
647
- expect(result).toBe(false);
648
- });
649
-
650
- it('should return true when loading', () => {
651
- const loadingState = fromJS({
652
- commonTestAndPreview: { isLoadingSenderDetails: true },
653
- });
654
- const selector = makeSelectIsLoadingSenderDetails();
655
- const result = selector(loadingState);
656
-
657
- expect(result).toBe(true);
658
- });
659
- });
660
-
661
- describe('makeSelectFetchSenderDetailsError', () => {
662
- it('should select fetchSenderDetailsError from state', () => {
663
- const selector = makeSelectFetchSenderDetailsError();
664
- const result = selector(mockState);
665
-
666
- expect(result).toBeNull();
667
- });
668
-
669
- it('should return error when present', () => {
670
- const errorState = fromJS({
671
- commonTestAndPreview: { fetchSenderDetailsError: 'Sender fetch failed' },
672
- });
673
- const selector = makeSelectFetchSenderDetailsError();
674
- const result = selector(errorState);
675
-
676
- expect(result).toBe('Sender fetch failed');
677
- });
678
- });
679
-
680
- describe('makeSelectFetchWeCrmAccountsError', () => {
681
- it('should select fetchWeCrmAccountsError from state', () => {
682
- const selector = makeSelectFetchWeCrmAccountsError();
683
- const result = selector(mockState);
684
-
685
- expect(result).toBeNull();
686
- });
687
-
688
- it('should return error when present', () => {
689
- const errorState = fromJS({
690
- commonTestAndPreview: { fetchWeCrmAccountsError: 'WeCRM fetch failed' },
691
- });
692
- const selector = makeSelectFetchWeCrmAccountsError();
693
- const result = selector(errorState);
694
-
695
- expect(result).toBe('WeCRM fetch failed');
696
- });
697
- });
698
-
699
553
  describe('Edge Cases', () => {
700
554
  it('should handle undefined state gracefully', () => {
701
555
  const selector = makeSelectCustomers();
@@ -1331,7 +1331,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1331
1331
  }
1332
1332
  onSubmitWrapper = (args) => {
1333
1333
  const {singleTab = null} = args || {};
1334
- if (this.liquidFlow()) {
1334
+ if (this.liquidFlow() && !this.props?.isFullMode) {
1335
1335
  // For MPUSH, we need to validate both Android and iOS content separately
1336
1336
  if (this.props.channel === MOBILE_PUSH || this.props?.schema?.channel?.toUpperCase() === MOBILE_PUSH) {
1337
1337
  this.validateFormBuilderMPush(this.state.formData, singleTab);
@@ -294,6 +294,7 @@ const HTMLEditor = forwardRef(({
294
294
  enableSanitization: true,
295
295
  securityLevel: 'standard',
296
296
  apiValidationErrors, // Pass API validation errors to merge with client-side validation
297
+ isFullMode, // Skip liquid validation in standalone/full mode
297
298
  }, formatSanitizerMessage, formatValidatorMessage);
298
299
 
299
300
  // Expose validation and content state via ref
@@ -1375,6 +1375,7 @@ describe('HTMLEditor', () => {
1375
1375
  debounceMs: 500,
1376
1376
  enableSanitization: true,
1377
1377
  securityLevel: 'standard',
1378
+ isFullMode: true,
1378
1379
  },
1379
1380
  expect.any(Function),
1380
1381
  expect.any(Function)
@@ -155,7 +155,7 @@ describe('useValidation', () => {
155
155
  await Promise.resolve();
156
156
  });
157
157
 
158
- expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'email', null);
158
+ expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'email', null, { skipLiquidValidation: false });
159
159
  });
160
160
 
161
161
  it('updates validation when content changes', async () => {
@@ -472,7 +472,7 @@ describe('useValidation', () => {
472
472
  await Promise.resolve();
473
473
  });
474
474
 
475
- expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'inapp', null);
475
+ expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'inapp', null, { skipLiquidValidation: false });
476
476
  });
477
477
 
478
478
  it('defaults to email variant', async () => {
@@ -487,7 +487,7 @@ describe('useValidation', () => {
487
487
  await Promise.resolve();
488
488
  });
489
489
 
490
- expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'email', null);
490
+ expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'email', null, { skipLiquidValidation: false });
491
491
  });
492
492
  });
493
493
 
@@ -922,4 +922,133 @@ describe('useValidation', () => {
922
922
  });
923
923
  });
924
924
  });
925
+
926
+ describe('isFullMode - skip liquid validation', () => {
927
+ it('passes skipLiquidValidation: true to validateHTML when isFullMode is true', async () => {
928
+ const { validateHTML } = require('../../utils/htmlValidator');
929
+
930
+ render(<TestComponent content="<p>Test</p>" options={{ isFullMode: true }} />);
931
+
932
+ await act(async () => {
933
+ jest.advanceTimersByTime(500);
934
+ await Promise.resolve();
935
+ await Promise.resolve();
936
+ await Promise.resolve();
937
+ });
938
+
939
+ expect(validateHTML).toHaveBeenCalledWith('<p>Test</p>', 'email', null, { skipLiquidValidation: true });
940
+ });
941
+
942
+ it('excludes API liquid errors from getAllIssues when isFullMode is true', async () => {
943
+ let validationState;
944
+ const onStateChange = (state) => { validationState = state; };
945
+
946
+ const apiValidationErrors = {
947
+ liquidErrors: ['Unsupported tag: points_balance'],
948
+ standardErrors: [],
949
+ };
950
+
951
+ render(
952
+ <TestComponent
953
+ content="<p>Test</p>"
954
+ options={{ apiValidationErrors, isFullMode: true }}
955
+ onStateChange={onStateChange}
956
+ />
957
+ );
958
+
959
+ await act(async () => {
960
+ jest.advanceTimersByTime(500);
961
+ await Promise.resolve();
962
+ await Promise.resolve();
963
+ await Promise.resolve();
964
+ });
965
+
966
+ await waitFor(() => {
967
+ expect(validationState).toBeDefined();
968
+ });
969
+
970
+ const issues = validationState.getAllIssues();
971
+ const liquidIssues = issues.filter((i) => i.source === 'liquid-validator');
972
+ expect(liquidIssues).toHaveLength(0);
973
+ });
974
+
975
+ it('returns isClean true when only liquid errors exist and isFullMode is true', () => {
976
+ const apiValidationErrors = {
977
+ liquidErrors: ['Unsupported tag: points_balance'],
978
+ standardErrors: [],
979
+ };
980
+
981
+ render(
982
+ <TestComponent
983
+ content=""
984
+ options={{ apiValidationErrors, isFullMode: true }}
985
+ />
986
+ );
987
+
988
+ // Before validation runs, isClean should be true because liquid errors are ignored in full mode
989
+ expect(screen.getByTestId('is-clean')).toHaveTextContent('true');
990
+ });
991
+
992
+ it('does not treat liquid errors as blocking when isFullMode is true', async () => {
993
+ let validationState;
994
+ const onStateChange = (state) => { validationState = state; };
995
+
996
+ const apiValidationErrors = {
997
+ liquidErrors: ['Unsupported tag: points_balance'],
998
+ standardErrors: [],
999
+ };
1000
+
1001
+ render(
1002
+ <TestComponent
1003
+ content="<p>Valid</p>"
1004
+ options={{ apiValidationErrors, isFullMode: true }}
1005
+ onStateChange={onStateChange}
1006
+ />
1007
+ );
1008
+
1009
+ await act(async () => {
1010
+ jest.advanceTimersByTime(500);
1011
+ await Promise.resolve();
1012
+ await Promise.resolve();
1013
+ await Promise.resolve();
1014
+ });
1015
+
1016
+ await waitFor(() => {
1017
+ expect(validationState).toBeDefined();
1018
+ });
1019
+
1020
+ expect(validationState.hasBlockingErrors).toBe(false);
1021
+ });
1022
+
1023
+ it('still treats standard API errors as blocking when isFullMode is true', async () => {
1024
+ let validationState;
1025
+ const onStateChange = (state) => { validationState = state; };
1026
+
1027
+ const apiValidationErrors = {
1028
+ liquidErrors: ['Liquid error'],
1029
+ standardErrors: ['Standard error'],
1030
+ };
1031
+
1032
+ render(
1033
+ <TestComponent
1034
+ content="<p>Valid</p>"
1035
+ options={{ apiValidationErrors, isFullMode: true }}
1036
+ onStateChange={onStateChange}
1037
+ />
1038
+ );
1039
+
1040
+ await act(async () => {
1041
+ jest.advanceTimersByTime(500);
1042
+ await Promise.resolve();
1043
+ await Promise.resolve();
1044
+ await Promise.resolve();
1045
+ });
1046
+
1047
+ await waitFor(() => {
1048
+ expect(validationState).toBeDefined();
1049
+ });
1050
+
1051
+ expect(validationState.hasBlockingErrors).toBe(true);
1052
+ });
1053
+ });
925
1054
  });