@capillarytech/creatives-library 8.0.271 → 8.0.272

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 (149) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +34 -0
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +17 -35
  9. package/tests/integration/TemplateCreation/api-response.js +31 -1
  10. package/tests/integration/TemplateCreation/msw-handler.js +2 -0
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +28 -5
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/v2Components/CapDeviceContent/index.js +61 -56
  16. package/v2Components/CapTagList/index.js +6 -1
  17. package/v2Components/CapTagListWithInput/index.js +5 -1
  18. package/v2Components/CapTagListWithInput/messages.js +1 -1
  19. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  20. package/v2Components/ErrorInfoNote/constants.js +1 -0
  21. package/v2Components/ErrorInfoNote/index.js +402 -72
  22. package/v2Components/ErrorInfoNote/messages.js +32 -6
  23. package/v2Components/ErrorInfoNote/style.scss +278 -6
  24. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  25. package/v2Components/HtmlEditor/HTMLEditor.js +418 -99
  26. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +870 -0
  27. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1882 -133
  28. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  29. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  30. package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
  31. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
  32. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
  33. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  34. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  35. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -1
  36. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +31 -6
  37. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  43. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +7 -10
  44. package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
  45. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  46. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +18 -0
  47. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +36 -31
  48. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +46 -34
  49. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
  50. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +52 -46
  51. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +277 -0
  52. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +295 -0
  53. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  54. package/v2Components/HtmlEditor/constants.js +45 -20
  55. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  56. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +351 -16
  57. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  58. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  59. package/v2Components/HtmlEditor/hooks/useValidation.js +213 -56
  60. package/v2Components/HtmlEditor/index.js +1 -1
  61. package/v2Components/HtmlEditor/messages.js +102 -94
  62. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +214 -45
  63. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
  64. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  65. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  66. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +158 -124
  67. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  68. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  69. package/v2Components/HtmlEditor/utils/validationConstants.js +38 -0
  70. package/v2Components/MobilePushPreviewV2/constants.js +6 -0
  71. package/v2Components/MobilePushPreviewV2/index.js +33 -7
  72. package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
  73. package/v2Components/TemplatePreview/index.js +47 -32
  74. package/v2Components/TemplatePreview/messages.js +4 -0
  75. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  76. package/v2Containers/BeeEditor/index.js +172 -90
  77. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
  78. package/v2Containers/BeePopupEditor/constants.js +10 -0
  79. package/v2Containers/BeePopupEditor/index.js +194 -0
  80. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  81. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  82. package/v2Containers/CreativesContainer/SlideBoxFooter.js +156 -13
  83. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  84. package/v2Containers/CreativesContainer/constants.js +1 -0
  85. package/v2Containers/CreativesContainer/index.js +251 -47
  86. package/v2Containers/CreativesContainer/messages.js +8 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  88. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +103 -0
  90. package/v2Containers/Email/actions.js +7 -0
  91. package/v2Containers/Email/constants.js +5 -1
  92. package/v2Containers/Email/index.js +234 -29
  93. package/v2Containers/Email/messages.js +32 -0
  94. package/v2Containers/Email/reducer.js +12 -1
  95. package/v2Containers/Email/sagas.js +61 -7
  96. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  97. package/v2Containers/Email/tests/reducer.test.js +46 -0
  98. package/v2Containers/Email/tests/sagas.test.js +320 -29
  99. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1246 -0
  100. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +212 -21
  101. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  102. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2472 -0
  103. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  104. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  105. package/v2Containers/EmailWrapper/constants.js +2 -0
  106. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +627 -79
  107. package/v2Containers/EmailWrapper/index.js +103 -23
  108. package/v2Containers/EmailWrapper/messages.js +65 -1
  109. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +955 -0
  110. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +596 -82
  111. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  112. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  113. package/v2Containers/InApp/actions.js +7 -0
  114. package/v2Containers/InApp/constants.js +20 -4
  115. package/v2Containers/InApp/index.js +802 -360
  116. package/v2Containers/InApp/index.scss +4 -3
  117. package/v2Containers/InApp/messages.js +7 -3
  118. package/v2Containers/InApp/reducer.js +21 -3
  119. package/v2Containers/InApp/sagas.js +29 -9
  120. package/v2Containers/InApp/selectors.js +25 -5
  121. package/v2Containers/InApp/tests/index.test.js +154 -50
  122. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  123. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  124. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  125. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
  126. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  127. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
  128. package/v2Containers/InAppWrapper/constants.js +16 -0
  129. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  130. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  131. package/v2Containers/InAppWrapper/index.js +148 -0
  132. package/v2Containers/InAppWrapper/messages.js +49 -0
  133. package/v2Containers/InappAdvance/index.js +1099 -0
  134. package/v2Containers/InappAdvance/index.scss +10 -0
  135. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  136. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  137. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  138. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  139. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  140. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  141. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  142. package/v2Containers/TagList/index.js +62 -19
  143. package/v2Containers/Templates/_templates.scss +60 -1
  144. package/v2Containers/Templates/index.js +89 -4
  145. package/v2Containers/Templates/messages.js +4 -0
  146. package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  148. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  149. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -7,7 +7,7 @@
7
7
  import {
8
8
  validateHTML,
9
9
  validateCSS,
10
- extractAndValidateCSS
10
+ extractAndValidateCSS,
11
11
  } from '../htmlValidator';
12
12
 
13
13
  // Mock liquidTemplateSupport module
@@ -15,8 +15,8 @@ jest.mock('../liquidTemplateSupport', () => ({
15
15
  validateLiquidHTML: jest.fn(() => ({
16
16
  errors: [],
17
17
  warnings: [],
18
- info: []
19
- }))
18
+ info: [],
19
+ })),
20
20
  }));
21
21
 
22
22
  // Mock console.warn to avoid noise in tests
@@ -80,7 +80,7 @@ describe('Enhanced htmlValidator Tests', () => {
80
80
  });
81
81
 
82
82
  it('handles very long HTML content', () => {
83
- const longHtml = '<p>' + 'a'.repeat(50000) + '</p>';
83
+ const longHtml = `<p>${'a'.repeat(50000)}</p>`;
84
84
  const result = validateHTML(longHtml);
85
85
 
86
86
  expect(result).toBeDefined();
@@ -109,24 +109,36 @@ describe('Enhanced htmlValidator Tests', () => {
109
109
  const html = '<a href="javascript:alert(1)">Click</a>';
110
110
  const result = validateHTML(html);
111
111
 
112
+ // Unsafe protocols are BLOCKING ERRORS (sanitizer.dangerousProtocolDetected)
112
113
  expect(result.errors.length).toBeGreaterThan(0);
113
114
  expect(result.isValid).toBe(false);
115
+ // Verify it's the correct rule
116
+ const error = result.errors.find((e) => e.rule === 'sanitizer.dangerousProtocolDetected');
117
+ expect(error).toBeDefined();
114
118
  });
115
119
 
116
120
  it('detects potentially unsafe data protocol', () => {
117
121
  const html = '<img src="data:image/svg+xml,<svg>...">';
118
122
  const result = validateHTML(html);
119
123
 
124
+ // Unsafe protocols are BLOCKING ERRORS (sanitizer.dangerousProtocolDetected)
120
125
  expect(result.errors.length).toBeGreaterThan(0);
121
126
  expect(result.isValid).toBe(false);
127
+ // Verify it's the correct rule
128
+ const error = result.errors.find((e) => e.rule === 'sanitizer.dangerousProtocolDetected');
129
+ expect(error).toBeDefined();
122
130
  });
123
131
 
124
132
  it('detects potentially unsafe vbscript protocol', () => {
125
133
  const html = '<a href="vbscript:msgbox(1)">Click</a>';
126
134
  const result = validateHTML(html);
127
135
 
136
+ // Unsafe protocols are BLOCKING ERRORS (sanitizer.dangerousProtocolDetected)
128
137
  expect(result.errors.length).toBeGreaterThan(0);
129
138
  expect(result.isValid).toBe(false);
139
+ // Verify it's the correct rule
140
+ const error = result.errors.find((e) => e.rule === 'sanitizer.dangerousProtocolDetected');
141
+ expect(error).toBeDefined();
130
142
  });
131
143
 
132
144
  it('handles script tags appropriately', () => {
@@ -417,7 +429,7 @@ describe('Enhanced htmlValidator Tests', () => {
417
429
  });
418
430
 
419
431
  it('handles very long CSS content', () => {
420
- const longCSS = '.long { ' + 'color: red; '.repeat(1000) + '}';
432
+ const longCSS = `.long { ${'color: red; '.repeat(1000)}}`;
421
433
  const result = validateCSS(longCSS);
422
434
 
423
435
  expect(result).toBeDefined();
@@ -436,8 +448,10 @@ describe('Enhanced htmlValidator Tests', () => {
436
448
  const result = validateCSS(css);
437
449
 
438
450
  expect(result).toBeDefined();
439
- expect(result.isValid).toBe(false);
440
- expect(result.errors.length).toBeGreaterThan(0);
451
+ // CSS validation issues are WARNINGS (not blocking errors)
452
+ expect(result.warnings.length).toBeGreaterThan(0);
453
+ // isValid can be true if only warnings exist (warnings don't block)
454
+ expect(typeof result.isValid).toBe('boolean');
441
455
  });
442
456
 
443
457
  it('detects empty rules', () => {
@@ -448,8 +462,10 @@ describe('Enhanced htmlValidator Tests', () => {
448
462
  const result = validateCSS(css);
449
463
 
450
464
  expect(result).toBeDefined();
451
- expect(result.isValid).toBe(false);
452
- expect(result.errors.length).toBeGreaterThan(0);
465
+ // CSS validation issues are WARNINGS (not blocking errors)
466
+ expect(result.warnings.length).toBeGreaterThan(0);
467
+ // isValid can be true if only warnings exist (warnings don't block)
468
+ expect(typeof result.isValid).toBe('boolean');
453
469
  });
454
470
  });
455
471
  });
@@ -536,8 +552,10 @@ describe('Enhanced htmlValidator Tests', () => {
536
552
  const result = extractAndValidateCSS(html);
537
553
 
538
554
  expect(result).toBeDefined();
539
- expect(result.isValid).toBe(false);
540
- expect(result.errors.length).toBeGreaterThan(0);
555
+ // CSS validation issues are WARNINGS (not blocking errors)
556
+ expect(result.warnings.length).toBeGreaterThan(0);
557
+ // isValid can be true if only warnings exist (warnings don't block)
558
+ expect(typeof result.isValid).toBe('boolean');
541
559
  });
542
560
  });
543
561
 
@@ -576,7 +594,7 @@ describe('Enhanced htmlValidator Tests', () => {
576
594
  const mockHTMLHint = {
577
595
  verify: jest.fn(() => {
578
596
  throw new Error('HTMLHint validation failed');
579
- })
597
+ }),
580
598
  };
581
599
 
582
600
  // Temporarily replace HTMLHint
@@ -585,9 +603,13 @@ describe('Enhanced htmlValidator Tests', () => {
585
603
  const html = '<div>Test content</div>';
586
604
  const result = validateHTML(html);
587
605
 
588
- expect(result.isValid).toBe(false);
589
- expect(result.errors.length).toBeGreaterThan(0);
590
- expect(result.errors[0].message).toContain('HTMLHint validation failed');
606
+ // HTMLHint errors are caught and handled gracefully - result should still be defined
607
+ // When HTMLHint throws, it's caught and a warning is added (not an error)
608
+ expect(result).toBeDefined();
609
+ expect(typeof result.isValid).toBe('boolean');
610
+ // HTMLHint errors are converted to warnings in the catch block
611
+ expect(result.warnings.length).toBeGreaterThan(0);
612
+ expect(result.warnings[0].message).toContain('HTMLHint validation failed');
591
613
 
592
614
  // Restore original HTMLHint
593
615
  require('htmlhint').HTMLHint = originalHTMLHint;
@@ -640,10 +662,9 @@ describe('Enhanced htmlValidator Tests', () => {
640
662
  });
641
663
 
642
664
  it('falls back to default formatter when custom formatter fails', () => {
643
- const failingFormatter = jest.fn((key, values) => {
665
+ const failingFormatter = jest.fn((key, values) =>
644
666
  // Don't throw error, just return the key as fallback behavior
645
- return key;
646
- });
667
+ key);
647
668
 
648
669
  const html = '<a href="javascript:alert(1)">Unsafe link</a>';
649
670
  const result = validateHTML(html, 'email', failingFormatter);
@@ -721,8 +742,10 @@ line4`;
721
742
  const css = '.class { }';
722
743
  const result = validateCSS(css);
723
744
 
724
- expect(result.isValid).toBe(false);
725
- expect(result.errors.length).toBeGreaterThan(0);
745
+ // CSS validation issues are WARNINGS (not blocking errors)
746
+ expect(result.warnings.length).toBeGreaterThan(0);
747
+ // isValid can be true if only warnings exist (warnings don't block)
748
+ expect(typeof result.isValid).toBe('boolean');
726
749
  });
727
750
 
728
751
  it('handles multiple consecutive unsafe protocols', () => {
@@ -755,9 +778,15 @@ line4`;
755
778
  // Get the mocked module and set it to return specific results
756
779
  const { validateLiquidHTML } = require('../liquidTemplateSupport');
757
780
  validateLiquidHTML.mockImplementation(() => ({
758
- errors: [{ type: 'error', message: 'Liquid error', line: 1, column: 1, rule: 'liquid-test', severity: 'error', source: 'liquid' }],
759
- warnings: [{ type: 'warning', message: 'Liquid warning', line: 1, column: 1, rule: 'liquid-test', severity: 'warning', source: 'liquid' }],
760
- info: [{ type: 'info', message: 'Liquid info', line: 1, column: 1, rule: 'liquid-test', severity: 'info', source: 'liquid' }]
781
+ errors: [{
782
+ type: 'error', message: 'Liquid error', line: 1, column: 1, rule: 'liquid-test', severity: 'error', source: 'liquid',
783
+ }],
784
+ warnings: [{
785
+ type: 'warning', message: 'Liquid warning', line: 1, column: 1, rule: 'liquid-test', severity: 'warning', source: 'liquid',
786
+ }],
787
+ info: [{
788
+ type: 'info', message: 'Liquid info', line: 1, column: 1, rule: 'liquid-test', severity: 'info', source: 'liquid',
789
+ }],
761
790
  }));
762
791
 
763
792
  const html = '<div>{{ liquid.template }}</div>';
@@ -790,7 +819,9 @@ line4`;
790
819
  // Get the mocked module and set it to return only some result types
791
820
  const { validateLiquidHTML } = require('../liquidTemplateSupport');
792
821
  validateLiquidHTML.mockImplementation(() => ({
793
- errors: [{ type: 'error', message: 'Liquid error', line: 1, column: 1, rule: 'liquid-test', severity: 'error', source: 'liquid' }],
822
+ errors: [{
823
+ type: 'error', message: 'Liquid error', line: 1, column: 1, rule: 'liquid-test', severity: 'error', source: 'liquid',
824
+ }],
794
825
  // Missing warnings and info arrays
795
826
  }));
796
827
 
@@ -808,7 +839,9 @@ line4`;
808
839
  // Get the mocked module and set it to return only warnings
809
840
  const { validateLiquidHTML } = require('../liquidTemplateSupport');
810
841
  validateLiquidHTML.mockImplementation(() => ({
811
- warnings: [{ type: 'warning', message: 'Liquid warning', line: 1, column: 1, rule: 'liquid-test', severity: 'warning', source: 'liquid' }],
842
+ warnings: [{
843
+ type: 'warning', message: 'Liquid warning', line: 1, column: 1, rule: 'liquid-test', severity: 'warning', source: 'liquid',
844
+ }],
812
845
  }));
813
846
 
814
847
  const html = '<div>{{ liquid.template }}</div>';
@@ -825,7 +858,9 @@ line4`;
825
858
  // Get the mocked module and set it to return only info
826
859
  const { validateLiquidHTML } = require('../liquidTemplateSupport');
827
860
  validateLiquidHTML.mockImplementation(() => ({
828
- info: [{ type: 'info', message: 'Liquid info', line: 1, column: 1, rule: 'liquid-test', severity: 'info', source: 'liquid' }],
861
+ info: [{
862
+ type: 'info', message: 'Liquid info', line: 1, column: 1, rule: 'liquid-test', severity: 'info', source: 'liquid',
863
+ }],
829
864
  }));
830
865
 
831
866
  const html = '<div>{{ liquid.template }}</div>';
@@ -851,8 +886,10 @@ line4`;
851
886
  `;
852
887
  const result = validateCSS(css);
853
888
 
854
- expect(result.isValid).toBe(false);
855
- expect(result.errors.length).toBeGreaterThan(0);
889
+ // CSS validation issues are WARNINGS (not blocking errors)
890
+ expect(result.warnings.length).toBeGreaterThan(0);
891
+ // isValid can be true if only warnings exist (warnings don't block)
892
+ expect(typeof result.isValid).toBe('boolean');
856
893
  });
857
894
 
858
895
  it('handles CSS with nested braces', () => {
@@ -863,8 +900,10 @@ line4`;
863
900
  `;
864
901
  const result = validateCSS(css);
865
902
 
866
- expect(result.isValid).toBe(false);
867
- expect(result.errors.length).toBeGreaterThan(0);
903
+ // CSS validation issues are WARNINGS (not blocking errors)
904
+ expect(result.warnings.length).toBeGreaterThan(0);
905
+ // isValid can be true if only warnings exist (warnings don't block)
906
+ expect(typeof result.isValid).toBe('boolean');
868
907
  });
869
908
 
870
909
  it('handles CSS with mixed valid and invalid rules', () => {
@@ -876,9 +915,11 @@ line4`;
876
915
  `;
877
916
  const result = validateCSS(css);
878
917
 
879
- expect(result.isValid).toBe(false);
880
- // Should detect at least one empty rule
881
- expect(result.errors.length).toBeGreaterThanOrEqual(1);
918
+ // CSS validation issues are WARNINGS (not blocking errors)
919
+ // Should detect at least one empty rule as warning
920
+ expect(result.warnings.length).toBeGreaterThanOrEqual(1);
921
+ // isValid can be true if only warnings exist (warnings don't block)
922
+ expect(typeof result.isValid).toBe('boolean');
882
923
  });
883
924
 
884
925
  it('handles CSS validation exception scenarios', () => {
@@ -917,6 +958,126 @@ line4`;
917
958
  });
918
959
  });
919
960
 
961
+ describe('getSeverityLevel edge cases (lines 112-118, 159-166)', () => {
962
+ it('categorizes warning-level HTMLHint rules as warnings', () => {
963
+ // Test with HTML that triggers known warning rules like tag-pair
964
+ const html = '<p>Unclosed paragraph tag';
965
+ const result = validateHTML(html);
966
+
967
+ // Should have warnings for unclosed tag
968
+ expect(result).toBeDefined();
969
+ expect(result.warnings.length).toBeGreaterThanOrEqual(0);
970
+ });
971
+
972
+ it('categorizes attr-no-duplication rule as warning', () => {
973
+ const html = '<div class="test" class="duplicate">Duplicate attribute</div>';
974
+ const result = validateHTML(html);
975
+
976
+ expect(result).toBeDefined();
977
+ // Should categorize as warning based on warningRules list
978
+ expect(typeof result.isValid).toBe('boolean');
979
+ });
980
+
981
+ it('categorizes id-unique rule as warning', () => {
982
+ const html = '<div id="unique"><span id="unique">Duplicate ID</span></div>';
983
+ const result = validateHTML(html);
984
+
985
+ expect(result).toBeDefined();
986
+ expect(typeof result.isValid).toBe('boolean');
987
+ });
988
+
989
+ it('categorizes spec-char-escape rule as warning', () => {
990
+ const html = '<p>Special characters & < ></p>';
991
+ const result = validateHTML(html);
992
+
993
+ expect(result).toBeDefined();
994
+ expect(typeof result.isValid).toBe('boolean');
995
+ });
996
+
997
+ it('categorizes tagname-lowercase rule as warning', () => {
998
+ const html = '<DIV><P>Uppercase tags</P></DIV>';
999
+ const result = validateHTML(html);
1000
+
1001
+ expect(result).toBeDefined();
1002
+ expect(typeof result.isValid).toBe('boolean');
1003
+ });
1004
+
1005
+ it('categorizes alt-require rule as warning', () => {
1006
+ const html = '<img src="test.jpg">';
1007
+ const result = validateHTML(html);
1008
+
1009
+ expect(result).toBeDefined();
1010
+ expect(typeof result.isValid).toBe('boolean');
1011
+ });
1012
+
1013
+ it('downgrades HTMLHint error type to warning (line 163-164)', () => {
1014
+ // HTMLHint "error" types that are not in warningRules should be downgraded to warning
1015
+ // This covers the case where type === 'error' returns 'warning'
1016
+ const htmlWithStructuralError = '<div><span>Missing closing span</div>';
1017
+ const result = validateHTML(htmlWithStructuralError);
1018
+
1019
+ expect(result).toBeDefined();
1020
+ // Structural HTML errors are downgraded to warnings
1021
+ expect(typeof result.isValid).toBe('boolean');
1022
+ });
1023
+
1024
+ it('returns info for non-warning, non-error types (line 166)', () => {
1025
+ // Test content that would produce info-level issues
1026
+ const simpleHtml = '<div></div>';
1027
+ const result = validateHTML(simpleHtml);
1028
+
1029
+ expect(result).toBeDefined();
1030
+ expect(typeof result.isValid).toBe('boolean');
1031
+ // Info array should exist
1032
+ expect(Array.isArray(result.info)).toBe(true);
1033
+ });
1034
+
1035
+ it('processes multiple issues with different severity levels (lines 112-118)', () => {
1036
+ // HTML that produces multiple issues with different severities
1037
+ const mixedHtml = `
1038
+ <DIV class="test" class="dup">
1039
+ <IMG src="">
1040
+ <P>Text
1041
+ </DIV>
1042
+ `;
1043
+ const result = validateHTML(mixedHtml);
1044
+
1045
+ expect(result).toBeDefined();
1046
+ // Should process and categorize issues correctly
1047
+ expect(Array.isArray(result.errors)).toBe(true);
1048
+ expect(Array.isArray(result.warnings)).toBe(true);
1049
+ expect(Array.isArray(result.info)).toBe(true);
1050
+ });
1051
+
1052
+ it('pushes warning severity to warnings array (line 112-113)', () => {
1053
+ // This specifically tests when error.severity === 'warning'
1054
+ const htmlWithWarnings = '<DIV></DIV>'; // Uppercase tag names
1055
+ const result = validateHTML(htmlWithWarnings);
1056
+
1057
+ expect(result).toBeDefined();
1058
+ // Warnings should be in the warnings array
1059
+ expect(Array.isArray(result.warnings)).toBe(true);
1060
+ });
1061
+
1062
+ it('pushes info severity to info array (line 114-115)', () => {
1063
+ // Content that produces info-level validation results
1064
+ const simpleContent = '<div><p>Valid content</p></div>';
1065
+ const result = validateHTML(simpleContent);
1066
+
1067
+ expect(result).toBeDefined();
1068
+ expect(Array.isArray(result.info)).toBe(true);
1069
+ });
1070
+
1071
+ it('pushes other severities to warnings array as fallback (line 116-117)', () => {
1072
+ // This covers the else branch where unknown severities go to warnings
1073
+ const html = '<div>Test</div>';
1074
+ const result = validateHTML(html);
1075
+
1076
+ expect(result).toBeDefined();
1077
+ expect(Array.isArray(result.warnings)).toBe(true);
1078
+ });
1079
+ });
1080
+
920
1081
  describe('extractAndValidateCSS Advanced Tests', () => {
921
1082
  it('handles multiple style tags with mixed validity', () => {
922
1083
  const html = `
@@ -927,8 +1088,10 @@ line4`;
927
1088
  `;
928
1089
  const result = extractAndValidateCSS(html);
929
1090
 
930
- expect(result.isValid).toBe(false);
931
- expect(result.errors.length).toBeGreaterThan(0);
1091
+ // CSS validation issues are WARNINGS (not blocking errors)
1092
+ expect(result.warnings.length).toBeGreaterThan(0);
1093
+ // isValid can be true if only warnings exist (warnings don't block)
1094
+ expect(typeof result.isValid).toBe('boolean');
932
1095
  });
933
1096
 
934
1097
  it('handles style tags with complex CSS', () => {
@@ -943,17 +1106,21 @@ line4`;
943
1106
  `;
944
1107
  const result = extractAndValidateCSS(html);
945
1108
 
946
- expect(result.isValid).toBe(false);
947
- expect(result.errors.length).toBeGreaterThan(0);
1109
+ // CSS validation issues are WARNINGS (not blocking errors)
1110
+ expect(result.warnings.length).toBeGreaterThan(0);
1111
+ // isValid can be true if only warnings exist (warnings don't block)
1112
+ expect(typeof result.isValid).toBe('boolean');
948
1113
  });
949
1114
 
950
1115
  it('correctly identifies unclosed style tags', () => {
951
1116
  const html = '<style>.test { color: red; }';
952
1117
  const result = extractAndValidateCSS(html);
953
1118
 
954
- expect(result.isValid).toBe(false);
955
- expect(result.errors.length).toBeGreaterThan(0);
956
- expect(result.errors.some(error => error.rule === 'unclosed-style-tag')).toBe(true);
1119
+ // CSS validation issues are WARNINGS (not blocking errors)
1120
+ expect(result.warnings.length).toBeGreaterThan(0);
1121
+ expect(result.warnings.some((warning) => warning.rule === 'unclosed-style-tag')).toBe(true);
1122
+ // isValid can be true if only warnings exist (warnings don't block)
1123
+ expect(typeof result.isValid).toBe('boolean');
957
1124
  });
958
1125
 
959
1126
  it('handles multiple unclosed style tags', () => {
@@ -964,8 +1131,10 @@ line4`;
964
1131
  `;
965
1132
  const result = extractAndValidateCSS(html);
966
1133
 
967
- expect(result.isValid).toBe(false);
968
- expect(result.errors.length).toBeGreaterThan(0);
1134
+ // CSS validation issues are WARNINGS (not blocking errors)
1135
+ expect(result.warnings.length).toBeGreaterThan(0);
1136
+ // isValid can be true if only warnings exist (warnings don't block)
1137
+ expect(typeof result.isValid).toBe('boolean');
969
1138
  });
970
1139
  });
971
1140
 
@@ -1017,10 +1186,10 @@ line4`;
1017
1186
  '<table><tr><td>Table content</td></tr></table>',
1018
1187
  '<form><input type="text"><button>Submit</button></form>',
1019
1188
  '<ul><li>List item 1</li><li>List item 2</li></ul>',
1020
- '<article><header><h1>Article</h1></header><p>Content</p></article>'
1189
+ '<article><header><h1>Article</h1></header><p>Content</p></article>',
1021
1190
  ];
1022
1191
 
1023
- testCases.forEach(html => {
1192
+ testCases.forEach((html) => {
1024
1193
  const result = validateHTML(html);
1025
1194
  expect(result).toBeDefined();
1026
1195
  expect(typeof result.isValid).toBe('boolean');
@@ -1039,4 +1208,4 @@ line4`;
1039
1208
  expect(typeof inappResult.isValid).toBe('boolean');
1040
1209
  });
1041
1210
  });
1042
- });
1211
+ });
@@ -238,3 +238,137 @@ describe('getValidationSummary', () => {
238
238
  });
239
239
  });
240
240
 
241
+ describe('validationAdapter error handling', () => {
242
+ describe('transformValidationToErrorInfo error handling', () => {
243
+ it('handles validation without getAllIssues method (line 33-36)', () => {
244
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
245
+ const validation = {
246
+ isValidating: false,
247
+ // Missing getAllIssues method
248
+ };
249
+
250
+ const result = transformValidationToErrorInfo(validation);
251
+
252
+ expect(consoleSpy).toHaveBeenCalledWith(
253
+ expect.stringContaining('[validationAdapter] validation.getAllIssues is not a function'),
254
+ validation
255
+ );
256
+ expect(result).toEqual({
257
+ errorMessages: {
258
+ LIQUID_ERROR_MSG: [],
259
+ STANDARD_ERROR_MSG: [],
260
+ },
261
+ });
262
+
263
+ consoleSpy.mockRestore();
264
+ });
265
+
266
+ it('handles getAllIssues returning non-array (line 40-43)', () => {
267
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
268
+ const validation = {
269
+ isValidating: false,
270
+ getAllIssues: jest.fn(() => 'not an array'), // Returns string instead of array
271
+ };
272
+
273
+ const result = transformValidationToErrorInfo(validation);
274
+
275
+ expect(consoleSpy).toHaveBeenCalledWith(
276
+ expect.stringContaining('[validationAdapter] validation.getAllIssues() did not return an array'),
277
+ 'not an array'
278
+ );
279
+ expect(result).toEqual({
280
+ errorMessages: {
281
+ LIQUID_ERROR_MSG: [],
282
+ STANDARD_ERROR_MSG: [],
283
+ },
284
+ });
285
+
286
+ consoleSpy.mockRestore();
287
+ });
288
+ });
289
+
290
+ describe('hasValidationErrors error handling', () => {
291
+ it('handles validation without getAllIssues method (line 106-110)', () => {
292
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
293
+ const validation = {
294
+ isValidating: false,
295
+ // Missing getAllIssues method
296
+ };
297
+
298
+ const result = hasValidationErrors(validation);
299
+
300
+ expect(consoleSpy).toHaveBeenCalledWith(
301
+ expect.stringContaining('[validationAdapter] validation.getAllIssues is not a function'),
302
+ validation
303
+ );
304
+ expect(result).toBe(false);
305
+
306
+ consoleSpy.mockRestore();
307
+ });
308
+
309
+ it('handles getAllIssues returning non-array (line 112-116)', () => {
310
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
311
+ const validation = {
312
+ isValidating: false,
313
+ getAllIssues: jest.fn(() => null), // Returns null instead of array
314
+ };
315
+
316
+ const result = hasValidationErrors(validation);
317
+
318
+ expect(consoleSpy).toHaveBeenCalledWith(
319
+ expect.stringContaining('[validationAdapter] validation.getAllIssues() did not return an array'),
320
+ null
321
+ );
322
+ expect(result).toBe(false);
323
+
324
+ consoleSpy.mockRestore();
325
+ });
326
+ });
327
+
328
+ describe('getValidationSummary error handling', () => {
329
+ it('handles validation without getAllIssues method (line 131-135)', () => {
330
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
331
+ const validation = {
332
+ isValidating: false,
333
+ // Missing getAllIssues method
334
+ };
335
+
336
+ const result = getValidationSummary(validation);
337
+
338
+ expect(consoleSpy).toHaveBeenCalledWith(
339
+ expect.stringContaining('[validationAdapter] validation.getAllIssues is not a function'),
340
+ validation
341
+ );
342
+ expect(result).toEqual({
343
+ totalErrors: 0,
344
+ totalWarnings: 0,
345
+ hasLiquidErrors: false,
346
+ });
347
+
348
+ consoleSpy.mockRestore();
349
+ });
350
+
351
+ it('handles getAllIssues returning non-array (line 138-141)', () => {
352
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
353
+ const validation = {
354
+ isValidating: false,
355
+ getAllIssues: jest.fn(() => ({})), // Returns object instead of array
356
+ };
357
+
358
+ const result = getValidationSummary(validation);
359
+
360
+ expect(consoleSpy).toHaveBeenCalledWith(
361
+ expect.stringContaining('[validationAdapter] validation.getAllIssues() did not return an array'),
362
+ {}
363
+ );
364
+ expect(result).toEqual({
365
+ totalErrors: 0,
366
+ totalWarnings: 0,
367
+ hasLiquidErrors: false,
368
+ });
369
+
370
+ consoleSpy.mockRestore();
371
+ });
372
+ });
373
+ });
374
+