@capillarytech/creatives-library 8.0.263 → 8.0.265
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/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +1 -3
- package/initialReducer.js +0 -2
- package/package.json +1 -1
- package/services/api.js +0 -15
- package/services/tests/api.test.js +0 -34
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +35 -17
- package/tests/integration/TemplateCreation/api-response.js +1 -31
- package/tests/integration/TemplateCreation/msw-handler.js +0 -2
- package/utils/common.js +0 -11
- package/utils/commonUtils.js +5 -28
- package/utils/tests/commonUtil.test.js +0 -224
- package/utils/tests/transformerUtils.test.js +0 -297
- package/utils/transformTemplateConfig.js +10 -0
- package/utils/transformerUtils.js +0 -40
- package/v2Components/CapDeviceContent/index.js +56 -61
- package/v2Components/CapImageUpload/constants.js +0 -2
- package/v2Components/CapImageUpload/index.js +16 -65
- package/v2Components/CapImageUpload/index.scss +1 -4
- package/v2Components/CapImageUpload/messages.js +1 -5
- package/v2Components/CapTagList/index.js +1 -6
- package/v2Components/CapTagListWithInput/index.js +1 -5
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
- package/v2Components/ErrorInfoNote/index.js +72 -402
- package/v2Components/ErrorInfoNote/messages.js +6 -32
- package/v2Components/ErrorInfoNote/style.scss +6 -278
- package/v2Components/FormBuilder/tests/index.test.js +4 -13
- package/v2Components/HtmlEditor/HTMLEditor.js +99 -418
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1882
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
- package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -9
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +6 -31
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +10 -7
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -18
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -36
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -46
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +46 -52
- package/v2Components/HtmlEditor/constants.js +20 -45
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -351
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
- package/v2Components/HtmlEditor/hooks/useValidation.js +56 -213
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +94 -102
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -214
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
- package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +124 -158
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
- package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
- package/v2Components/MobilePushPreviewV2/index.js +7 -33
- package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
- package/v2Components/TemplatePreview/index.js +32 -47
- package/v2Components/TemplatePreview/messages.js +0 -4
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/BeeEditor/index.js +90 -172
- package/v2Containers/CreativesContainer/SlideBoxContent.js +53 -184
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -3
- package/v2Containers/CreativesContainer/constants.js +0 -4
- package/v2Containers/CreativesContainer/index.js +46 -408
- package/v2Containers/CreativesContainer/messages.js +0 -12
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -342
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -103
- package/v2Containers/Email/actions.js +0 -7
- package/v2Containers/Email/constants.js +1 -5
- package/v2Containers/Email/index.js +36 -237
- package/v2Containers/Email/messages.js +0 -32
- package/v2Containers/Email/reducer.js +1 -12
- package/v2Containers/Email/sagas.js +7 -61
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
- package/v2Containers/Email/tests/reducer.test.js +0 -46
- package/v2Containers/Email/tests/sagas.test.js +29 -320
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
- package/v2Containers/EmailWrapper/constants.js +0 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
- package/v2Containers/EmailWrapper/index.js +23 -103
- package/v2Containers/EmailWrapper/messages.js +1 -65
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
- package/v2Containers/InApp/actions.js +0 -7
- package/v2Containers/InApp/constants.js +4 -20
- package/v2Containers/InApp/index.js +359 -802
- package/v2Containers/InApp/index.scss +3 -4
- package/v2Containers/InApp/messages.js +3 -7
- package/v2Containers/InApp/reducer.js +3 -21
- package/v2Containers/InApp/sagas.js +9 -29
- package/v2Containers/InApp/selectors.js +5 -25
- package/v2Containers/InApp/tests/index.test.js +50 -154
- package/v2Containers/InApp/tests/reducer.test.js +0 -34
- package/v2Containers/InApp/tests/sagas.test.js +9 -61
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
- package/v2Containers/TagList/index.js +19 -62
- package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
- package/v2Containers/Templates/_templates.scss +1 -265
- package/v2Containers/Templates/actions.js +1 -2
- package/v2Containers/Templates/constants.js +0 -1
- package/v2Containers/Templates/index.js +38 -363
- package/v2Containers/Templates/messages.js +0 -28
- package/v2Containers/Templates/reducer.js +0 -2
- package/v2Containers/Templates/tests/index.test.js +0 -10
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +2 -4
- package/v2Containers/TemplatesV2/index.js +7 -15
- package/v2Containers/TemplatesV2/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -34
- package/utils/imageUrlUpload.js +0 -141
- package/v2Components/CapImageUrlUpload/constants.js +0 -26
- package/v2Components/CapImageUrlUpload/index.js +0 -365
- package/v2Components/CapImageUrlUpload/index.scss +0 -35
- package/v2Components/CapImageUrlUpload/messages.js +0 -47
- package/v2Components/ErrorInfoNote/constants.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -870
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -281
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -295
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
- package/v2Components/HtmlEditor/utils/validationConstants.js +0 -38
- package/v2Components/MobilePushPreviewV2/constants.js +0 -6
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
- package/v2Containers/BeePopupEditor/constants.js +0 -10
- package/v2Containers/BeePopupEditor/index.js +0 -194
- package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1246
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -2472
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -956
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
- package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
- package/v2Containers/InApp/tests/selectors.test.js +0 -612
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
- package/v2Containers/InAppWrapper/constants.js +0 -16
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
- package/v2Containers/InAppWrapper/index.js +0 -148
- package/v2Containers/InAppWrapper/messages.js +0 -49
- package/v2Containers/InappAdvance/index.js +0 -1099
- package/v2Containers/InappAdvance/index.scss +0 -10
- package/v2Containers/InappAdvance/tests/index.test.js +0 -448
- package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
- package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
- package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
- package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
- package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
- package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
- package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
- package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
- package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
- package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
- package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
- package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
- package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
- package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
- package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
- package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
- package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
- package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
- package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
- package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
- package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
- package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
- package/v2Containers/WebPush/Create/index.js +0 -1148
- package/v2Containers/WebPush/Create/index.scss +0 -134
- package/v2Containers/WebPush/Create/messages.js +0 -211
- package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
- package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
- package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
- package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
- package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
- package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
- package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
- package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
- package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
- package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
- package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
- package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
- package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
- package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
- package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
- package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
- package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
- package/v2Containers/WebPush/Create/preview/constants.js +0 -637
- package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
- package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
- package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
- package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
- package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
- package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
- package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
- package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
- package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
- package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
- package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
- package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
- package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
- package/v2Containers/WebPush/Create/utils/validation.js +0 -75
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
- package/v2Containers/WebPush/actions.js +0 -60
- package/v2Containers/WebPush/constants.js +0 -132
- package/v2Containers/WebPush/index.js +0 -2
- package/v2Containers/WebPush/reducer.js +0 -104
- package/v2Containers/WebPush/sagas.js +0 -119
- package/v2Containers/WebPush/selectors.js +0 -65
- package/v2Containers/WebPush/tests/reducer.test.js +0 -863
- package/v2Containers/WebPush/tests/sagas.test.js +0 -566
- package/v2Containers/WebPush/tests/selectors.test.js +0 -960
|
@@ -5,51 +5,33 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from 'react';
|
|
8
|
-
import {
|
|
9
|
-
render, screen, fireEvent, act, waitFor,
|
|
10
|
-
} from '@testing-library/react';
|
|
8
|
+
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
|
|
11
9
|
import '@testing-library/jest-dom';
|
|
12
10
|
import { IntlProvider } from 'react-intl';
|
|
13
11
|
import HTMLEditor from '../HTMLEditor';
|
|
14
12
|
|
|
15
|
-
// Options to control CodeEditorPane mock behavior
|
|
16
|
-
const mockCodeEditorOptions = {
|
|
17
|
-
includeInsertText: true,
|
|
18
|
-
insertTextThrows: false,
|
|
19
|
-
setRef: true,
|
|
20
|
-
navigateToLineThrows: false,
|
|
21
|
-
includeNavigateToLine: true,
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
|
|
25
13
|
// Mock useLayoutEffect to behave like useEffect in tests
|
|
26
14
|
React.useLayoutEffect = React.useEffect;
|
|
27
15
|
|
|
28
16
|
// Mock browser APIs
|
|
29
17
|
global.IntersectionObserver = class IntersectionObserver {
|
|
30
|
-
constructor() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
observe() { }
|
|
35
|
-
|
|
36
|
-
unobserve() { }
|
|
18
|
+
constructor() {}
|
|
19
|
+
disconnect() {}
|
|
20
|
+
observe() {}
|
|
21
|
+
unobserve() {}
|
|
37
22
|
};
|
|
38
23
|
|
|
39
24
|
global.ResizeObserver = class ResizeObserver {
|
|
40
|
-
constructor() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
observe() { }
|
|
45
|
-
|
|
46
|
-
unobserve() { }
|
|
25
|
+
constructor() {}
|
|
26
|
+
disconnect() {}
|
|
27
|
+
observe() {}
|
|
28
|
+
unobserve() {}
|
|
47
29
|
};
|
|
48
30
|
|
|
49
31
|
// Setup window.matchMedia mock
|
|
50
32
|
Object.defineProperty(window, 'matchMedia', {
|
|
51
33
|
writable: true,
|
|
52
|
-
value: jest.fn().mockImplementation(
|
|
34
|
+
value: jest.fn().mockImplementation(query => ({
|
|
53
35
|
matches: false,
|
|
54
36
|
media: query,
|
|
55
37
|
onchange: null,
|
|
@@ -106,97 +88,60 @@ window.getComputedStyle = jest.fn(() => ({
|
|
|
106
88
|
jest.mock('@capillarytech/cap-ui-library/CapNotification', () => ({
|
|
107
89
|
warning: jest.fn(),
|
|
108
90
|
error: jest.fn(),
|
|
109
|
-
success: jest.fn()
|
|
91
|
+
success: jest.fn()
|
|
110
92
|
}));
|
|
111
93
|
|
|
112
94
|
// Mock components
|
|
113
|
-
jest.mock('../components/EditorToolbar', () =>
|
|
114
|
-
return (
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
|
|
95
|
+
jest.mock('../components/EditorToolbar', () => {
|
|
96
|
+
return function MockEditorToolbar(props) {
|
|
97
|
+
return (
|
|
98
|
+
<div data-testid="editor-toolbar">
|
|
99
|
+
<button onClick={props.onToggleFullscreen}>Toggle Fullscreen</button>
|
|
100
|
+
<button onClick={() => props.onLabelInsert && props.onLabelInsert('test-label', 10)}>
|
|
118
101
|
Insert Label
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Insert Label (Null Position)
|
|
122
|
-
</button>
|
|
123
|
-
<button onClick={() => props.onSave && props.onSave()}>
|
|
102
|
+
</button>
|
|
103
|
+
<button onClick={() => props.onSave && props.onSave()}>
|
|
124
104
|
Save
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
128
109
|
});
|
|
129
110
|
|
|
130
|
-
jest.mock('../components/DeviceToggle', () =>
|
|
131
|
-
return (
|
|
132
|
-
|
|
133
|
-
<
|
|
111
|
+
jest.mock('../components/DeviceToggle', () => {
|
|
112
|
+
return function MockDeviceToggle(props) {
|
|
113
|
+
return (
|
|
114
|
+
<div data-testid="device-toggle">
|
|
115
|
+
<button onClick={() => props.onDeviceChange && props.onDeviceChange('android')}>
|
|
134
116
|
Android
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
</button>
|
|
118
|
+
<button onClick={() => props.onDeviceChange && props.onDeviceChange('ios')}>
|
|
137
119
|
iOS
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
120
|
+
</button>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
141
124
|
});
|
|
142
125
|
|
|
143
|
-
jest.mock('../components/SplitContainer', () =>
|
|
144
|
-
return
|
|
126
|
+
jest.mock('../components/SplitContainer', () => {
|
|
127
|
+
return function MockSplitContainer({ children }) {
|
|
128
|
+
return <div data-testid="split-container">{children}</div>;
|
|
129
|
+
};
|
|
145
130
|
});
|
|
146
131
|
|
|
147
|
-
|
|
148
132
|
jest.mock('../components/CodeEditorPane', () => {
|
|
149
133
|
const React = require('react');
|
|
150
|
-
|
|
151
|
-
return React.forwardRef((props, ref) => {
|
|
134
|
+
return React.forwardRef(function MockCodeEditorPane(props, ref) {
|
|
152
135
|
const [value, setValue] = React.useState('');
|
|
153
|
-
// Get validation from context
|
|
154
|
-
let validation = null;
|
|
155
|
-
try {
|
|
156
|
-
const context = useEditorContext();
|
|
157
|
-
validation = context?.validation;
|
|
158
|
-
} catch (e) {
|
|
159
|
-
// Context not available, use props or null
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
React.useImperativeHandle(ref, () => {
|
|
163
|
-
if (!mockCodeEditorOptions.setRef) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const methods = {
|
|
168
|
-
focus: jest.fn(),
|
|
169
|
-
getCursor: jest.fn(() => 0),
|
|
170
|
-
getValue: jest.fn(() => value),
|
|
171
|
-
setValue: jest.fn((newValue) => setValue(newValue)),
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
if (mockCodeEditorOptions.includeNavigateToLine) {
|
|
175
|
-
methods.navigateToLine = jest.fn(() => {
|
|
176
|
-
if (mockCodeEditorOptions.navigateToLineThrows) {
|
|
177
|
-
throw new Error('Navigation failed');
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (mockCodeEditorOptions.includeInsertText) {
|
|
183
|
-
methods.insertText = jest.fn(() => {
|
|
184
|
-
if (mockCodeEditorOptions.insertTextThrows) {
|
|
185
|
-
throw new Error('Insert failed');
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return methods;
|
|
191
|
-
});
|
|
192
136
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
137
|
+
React.useImperativeHandle(ref, () => ({
|
|
138
|
+
insertText: jest.fn(),
|
|
139
|
+
focus: jest.fn(),
|
|
140
|
+
getCursor: jest.fn(() => 0),
|
|
141
|
+
getValue: jest.fn(() => value),
|
|
142
|
+
setValue: jest.fn((newValue) => setValue(newValue)),
|
|
143
|
+
navigateToLine: jest.fn(),
|
|
144
|
+
}));
|
|
200
145
|
|
|
201
146
|
return (
|
|
202
147
|
<div data-testid="code-editor-pane">
|
|
@@ -210,47 +155,36 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
210
155
|
}}
|
|
211
156
|
data-testid="editor-textarea"
|
|
212
157
|
/>
|
|
213
|
-
<button
|
|
214
|
-
onClick={() => props.onContextChange && props.onContextChange('test-context')}
|
|
215
|
-
data-testid="trigger-context-change"
|
|
216
|
-
>
|
|
217
|
-
Trigger Context Change
|
|
218
|
-
</button>
|
|
219
|
-
{validation && (
|
|
220
|
-
<ValidationErrorDisplay
|
|
221
|
-
validation={validation}
|
|
222
|
-
onErrorClick={props.onErrorClick}
|
|
223
|
-
/>
|
|
224
|
-
)}
|
|
225
158
|
</div>
|
|
226
159
|
);
|
|
227
160
|
});
|
|
228
161
|
});
|
|
229
162
|
|
|
230
|
-
jest.mock('../components/PreviewPane', () =>
|
|
231
|
-
return (
|
|
232
|
-
|
|
163
|
+
jest.mock('../components/PreviewPane', () => {
|
|
164
|
+
return function MockPreviewPane(props) {
|
|
165
|
+
return (
|
|
166
|
+
<div data-testid="preview-pane" data-fullscreen={props.isFullscreenMode}>
|
|
233
167
|
Preview Content
|
|
234
|
-
|
|
235
|
-
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
236
171
|
});
|
|
237
172
|
|
|
238
|
-
jest.mock('../components/ValidationErrorDisplay', () =>
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
<div>Validation Errors</div>
|
|
246
|
-
<button onClick={() => onErrorClick && onErrorClick({ line: 5, column: 10 })}>
|
|
173
|
+
jest.mock('../components/ValidationErrorDisplay', () => {
|
|
174
|
+
return function MockValidationErrorDisplay({ validation, onErrorClick }) {
|
|
175
|
+
if (!validation || validation.isClean?.()) return null;
|
|
176
|
+
return (
|
|
177
|
+
<div data-testid="validation-error-display">
|
|
178
|
+
<div>Validation Errors</div>
|
|
179
|
+
<button onClick={() => onErrorClick && onErrorClick({ line: 5, column: 10 })}>
|
|
247
180
|
Error at Line 5
|
|
248
|
-
|
|
249
|
-
|
|
181
|
+
</button>
|
|
182
|
+
<button onClick={() => onErrorClick && onErrorClick({ line: null })}>
|
|
250
183
|
Error without Line
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
184
|
+
</button>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
254
188
|
});
|
|
255
189
|
|
|
256
190
|
// Mock hooks - Return complete hook objects with all required methods
|
|
@@ -263,8 +197,8 @@ jest.mock('../hooks/useEditorContent', () => ({
|
|
|
263
197
|
isLoading: false,
|
|
264
198
|
isDirty: false,
|
|
265
199
|
hasContent: true,
|
|
266
|
-
getContentSize: jest.fn(() => 20)
|
|
267
|
-
})
|
|
200
|
+
getContentSize: jest.fn(() => 20)
|
|
201
|
+
})
|
|
268
202
|
}));
|
|
269
203
|
|
|
270
204
|
jest.mock('../hooks/useInAppContent', () => ({
|
|
@@ -272,7 +206,7 @@ jest.mock('../hooks/useInAppContent', () => ({
|
|
|
272
206
|
content: '<p>Android content</p>',
|
|
273
207
|
deviceContent: {
|
|
274
208
|
android: '<p>Android content</p>',
|
|
275
|
-
ios: '<p>iOS content</p>'
|
|
209
|
+
ios: '<p>iOS content</p>'
|
|
276
210
|
},
|
|
277
211
|
activeDevice: 'android',
|
|
278
212
|
keepContentSame: false,
|
|
@@ -286,8 +220,8 @@ jest.mock('../hooks/useInAppContent', () => ({
|
|
|
286
220
|
getContentSize: () => 20,
|
|
287
221
|
isLoading: false,
|
|
288
222
|
isDirty: false,
|
|
289
|
-
hasContent: true
|
|
290
|
-
})
|
|
223
|
+
hasContent: true
|
|
224
|
+
})
|
|
291
225
|
}));
|
|
292
226
|
|
|
293
227
|
jest.mock('../hooks/useLayoutState', () => ({
|
|
@@ -319,8 +253,8 @@ jest.mock('../hooks/useLayoutState', () => ({
|
|
|
319
253
|
// Layout constraints
|
|
320
254
|
minPaneSize: 20,
|
|
321
255
|
maxPaneSize: 80,
|
|
322
|
-
gutterSize: 10
|
|
323
|
-
})
|
|
256
|
+
gutterSize: 10
|
|
257
|
+
})
|
|
324
258
|
}));
|
|
325
259
|
|
|
326
260
|
// Mock useValidation - need to use a variable that gets assigned in the factory
|
|
@@ -331,11 +265,11 @@ jest.mock('../hooks/useValidation', () => {
|
|
|
331
265
|
isValidating: false,
|
|
332
266
|
getAllIssues: () => [],
|
|
333
267
|
isClean: () => true,
|
|
334
|
-
summary: { totalErrors: 0, totalWarnings: 0 }
|
|
268
|
+
summary: { totalErrors: 0, totalWarnings: 0 }
|
|
335
269
|
}));
|
|
336
270
|
|
|
337
271
|
return {
|
|
338
|
-
useValidation: (...args) => mockUseValidationImpl(...args)
|
|
272
|
+
useValidation: (...args) => mockUseValidationImpl(...args)
|
|
339
273
|
};
|
|
340
274
|
});
|
|
341
275
|
|
|
@@ -363,7 +297,7 @@ describe('HTMLEditor', () => {
|
|
|
363
297
|
initialContent: '<p>Initial content</p>',
|
|
364
298
|
onChange: jest.fn(),
|
|
365
299
|
onSave: jest.fn(),
|
|
366
|
-
variant: 'email'
|
|
300
|
+
variant: 'email'
|
|
367
301
|
};
|
|
368
302
|
|
|
369
303
|
describe('Loading State', () => {
|
|
@@ -433,7 +367,7 @@ describe('HTMLEditor', () => {
|
|
|
433
367
|
|
|
434
368
|
// Context should reflect fullscreen mode change
|
|
435
369
|
const previewPanes = screen.getAllByTestId('preview-pane');
|
|
436
|
-
expect(previewPanes.some(
|
|
370
|
+
expect(previewPanes.some(pane => pane.getAttribute('data-fullscreen') === 'true')).toBe(true);
|
|
437
371
|
});
|
|
438
372
|
});
|
|
439
373
|
|
|
@@ -539,44 +473,10 @@ describe('HTMLEditor', () => {
|
|
|
539
473
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
540
474
|
});
|
|
541
475
|
|
|
542
|
-
it('converts string initialContent to device-specific format for inapp variant', () => {
|
|
543
|
-
// This test covers lines 104-109 in HTMLEditor.js
|
|
544
|
-
const mockUseInAppContent = require('../hooks/useInAppContent').useInAppContent;
|
|
545
|
-
const originalUseInAppContent = jest.requireActual('../hooks/useInAppContent').useInAppContent;
|
|
546
|
-
|
|
547
|
-
let capturedInitialContent = null;
|
|
548
|
-
jest.spyOn(require('../hooks/useInAppContent'), 'useInAppContent').mockImplementation((initialContent) => {
|
|
549
|
-
capturedInitialContent = initialContent;
|
|
550
|
-
return originalUseInAppContent(initialContent, {});
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
render(
|
|
554
|
-
<TestWrapper>
|
|
555
|
-
<HTMLEditor
|
|
556
|
-
{...defaultProps}
|
|
557
|
-
variant="inapp"
|
|
558
|
-
initialContent="<p>String content</p>"
|
|
559
|
-
/>
|
|
560
|
-
</TestWrapper>
|
|
561
|
-
);
|
|
562
|
-
|
|
563
|
-
act(() => {
|
|
564
|
-
jest.runAllTimers();
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// Verify that string content was converted to device-specific format
|
|
568
|
-
expect(capturedInitialContent).toEqual({
|
|
569
|
-
android: '<p>String content</p>',
|
|
570
|
-
ios: '<p>String content</p>',
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
jest.restoreAllMocks();
|
|
574
|
-
});
|
|
575
|
-
|
|
576
476
|
it('handles object initialContent for inapp variant', () => {
|
|
577
477
|
const deviceContent = {
|
|
578
478
|
android: '<p>Android content</p>',
|
|
579
|
-
ios: '<p>iOS content</p>'
|
|
479
|
+
ios: '<p>iOS content</p>'
|
|
580
480
|
};
|
|
581
481
|
|
|
582
482
|
render(
|
|
@@ -596,42 +496,6 @@ describe('HTMLEditor', () => {
|
|
|
596
496
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
597
497
|
});
|
|
598
498
|
|
|
599
|
-
it('uses provided device-specific content for inapp variant', () => {
|
|
600
|
-
// This test covers lines 110-113 in HTMLEditor.js
|
|
601
|
-
const deviceContent = {
|
|
602
|
-
android: '<p>Android content</p>',
|
|
603
|
-
ios: '<p>iOS content</p>',
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
const mockUseInAppContent = require('../hooks/useInAppContent').useInAppContent;
|
|
607
|
-
const originalUseInAppContent = jest.requireActual('../hooks/useInAppContent').useInAppContent;
|
|
608
|
-
|
|
609
|
-
let capturedInitialContent = null;
|
|
610
|
-
jest.spyOn(require('../hooks/useInAppContent'), 'useInAppContent').mockImplementation((initialContent) => {
|
|
611
|
-
capturedInitialContent = initialContent;
|
|
612
|
-
return originalUseInAppContent(initialContent, {});
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
render(
|
|
616
|
-
<TestWrapper>
|
|
617
|
-
<HTMLEditor
|
|
618
|
-
{...defaultProps}
|
|
619
|
-
variant="inapp"
|
|
620
|
-
initialContent={deviceContent}
|
|
621
|
-
/>
|
|
622
|
-
</TestWrapper>
|
|
623
|
-
);
|
|
624
|
-
|
|
625
|
-
act(() => {
|
|
626
|
-
jest.runAllTimers();
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
// Verify that object content was passed as-is
|
|
630
|
-
expect(capturedInitialContent).toEqual(deviceContent);
|
|
631
|
-
|
|
632
|
-
jest.restoreAllMocks();
|
|
633
|
-
});
|
|
634
|
-
|
|
635
499
|
it('handles inapp variant with layoutType', () => {
|
|
636
500
|
render(
|
|
637
501
|
<TestWrapper>
|
|
@@ -686,7 +550,7 @@ describe('HTMLEditor', () => {
|
|
|
686
550
|
// Should open fullscreen modal - now there are 2 preview panes (main + fullscreen modal)
|
|
687
551
|
const previewPanes = screen.getAllByTestId('preview-pane');
|
|
688
552
|
expect(previewPanes.length).toBeGreaterThan(1);
|
|
689
|
-
expect(previewPanes.some(
|
|
553
|
+
expect(previewPanes.some(pane => pane.getAttribute('data-fullscreen') === 'true')).toBe(true);
|
|
690
554
|
});
|
|
691
555
|
|
|
692
556
|
it('renders fullscreen modal with correct components', () => {
|
|
@@ -704,7 +568,7 @@ describe('HTMLEditor', () => {
|
|
|
704
568
|
fireEvent.click(toggleButton);
|
|
705
569
|
|
|
706
570
|
const previewPanes = screen.getAllByTestId('preview-pane');
|
|
707
|
-
expect(previewPanes.some(
|
|
571
|
+
expect(previewPanes.some(pane => pane.getAttribute('data-fullscreen') === 'true')).toBe(true);
|
|
708
572
|
});
|
|
709
573
|
|
|
710
574
|
it('renders fullscreen modal for inapp variant', () => {
|
|
@@ -742,7 +606,7 @@ describe('HTMLEditor', () => {
|
|
|
742
606
|
fireEvent.click(toggleButton);
|
|
743
607
|
|
|
744
608
|
// Should have multiple preview panes
|
|
745
|
-
|
|
609
|
+
let previewPanes = screen.getAllByTestId('preview-pane');
|
|
746
610
|
expect(previewPanes.length).toBeGreaterThan(1);
|
|
747
611
|
|
|
748
612
|
// Close fullscreen (button should still be available to close)
|
|
@@ -803,7 +667,7 @@ describe('HTMLEditor', () => {
|
|
|
803
667
|
isValidating: false,
|
|
804
668
|
getAllIssues: () => [{ message: 'Test error', severity: 'error' }],
|
|
805
669
|
isClean: () => false,
|
|
806
|
-
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
670
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
807
671
|
});
|
|
808
672
|
|
|
809
673
|
render(
|
|
@@ -824,7 +688,7 @@ describe('HTMLEditor', () => {
|
|
|
824
688
|
isValidating: false,
|
|
825
689
|
getAllIssues: () => [],
|
|
826
690
|
isClean: () => true,
|
|
827
|
-
summary: { totalErrors: 0, totalWarnings: 0 }
|
|
691
|
+
summary: { totalErrors: 0, totalWarnings: 0 }
|
|
828
692
|
});
|
|
829
693
|
|
|
830
694
|
render(
|
|
@@ -845,7 +709,7 @@ describe('HTMLEditor', () => {
|
|
|
845
709
|
it('handles readOnly prop', () => {
|
|
846
710
|
render(
|
|
847
711
|
<TestWrapper>
|
|
848
|
-
<HTMLEditor {...defaultProps} readOnly />
|
|
712
|
+
<HTMLEditor {...defaultProps} readOnly={true} />
|
|
849
713
|
</TestWrapper>
|
|
850
714
|
);
|
|
851
715
|
|
|
@@ -993,7 +857,7 @@ describe('HTMLEditor', () => {
|
|
|
993
857
|
describe('Error Handling', () => {
|
|
994
858
|
it('handles missing editorRef gracefully', () => {
|
|
995
859
|
// Mock console.warn to avoid noise in tests
|
|
996
|
-
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {
|
|
860
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
997
861
|
|
|
998
862
|
render(
|
|
999
863
|
<TestWrapper>
|
|
@@ -1020,7 +884,7 @@ describe('HTMLEditor', () => {
|
|
|
1020
884
|
focus: jest.fn(),
|
|
1021
885
|
getCursor: jest.fn(() => 0),
|
|
1022
886
|
// Missing insertText method
|
|
1023
|
-
}
|
|
887
|
+
}
|
|
1024
888
|
};
|
|
1025
889
|
|
|
1026
890
|
// Mock the ref to return an editor without insertText
|
|
@@ -1055,7 +919,7 @@ describe('HTMLEditor', () => {
|
|
|
1055
919
|
insertText: jest.fn(() => { throw new Error('Insert failed'); }),
|
|
1056
920
|
focus: jest.fn(),
|
|
1057
921
|
getCursor: jest.fn(() => 0),
|
|
1058
|
-
}
|
|
922
|
+
}
|
|
1059
923
|
};
|
|
1060
924
|
|
|
1061
925
|
const TestComponent = () => {
|
|
@@ -1121,7 +985,7 @@ describe('HTMLEditor', () => {
|
|
|
1121
985
|
current: {
|
|
1122
986
|
navigateToLine: jest.fn(() => { throw new Error('Navigation failed'); }),
|
|
1123
987
|
focus: jest.fn(),
|
|
1124
|
-
}
|
|
988
|
+
}
|
|
1125
989
|
};
|
|
1126
990
|
|
|
1127
991
|
const TestComponent = () => {
|
|
@@ -1206,8 +1070,8 @@ describe('HTMLEditor', () => {
|
|
|
1206
1070
|
onContentChange={jest.fn()}
|
|
1207
1071
|
className="test-class"
|
|
1208
1072
|
readOnly={false}
|
|
1209
|
-
showFullscreenButton
|
|
1210
|
-
autoSave
|
|
1073
|
+
showFullscreenButton={true}
|
|
1074
|
+
autoSave={true}
|
|
1211
1075
|
autoSaveInterval={30000}
|
|
1212
1076
|
/>
|
|
1213
1077
|
</TestWrapper>
|
|
@@ -1239,7 +1103,7 @@ describe('HTMLEditor', () => {
|
|
|
1239
1103
|
<TestWrapper>
|
|
1240
1104
|
<HTMLEditor
|
|
1241
1105
|
variant="inapp"
|
|
1242
|
-
autoSave
|
|
1106
|
+
autoSave={true}
|
|
1243
1107
|
autoSaveInterval={15000}
|
|
1244
1108
|
onSave={jest.fn()}
|
|
1245
1109
|
onContentChange={jest.fn()}
|
|
@@ -1273,14 +1137,13 @@ describe('HTMLEditor', () => {
|
|
|
1273
1137
|
expect.any(String),
|
|
1274
1138
|
expect.any(String),
|
|
1275
1139
|
expect.objectContaining({
|
|
1276
|
-
apiValidationErrors: null,
|
|
1277
1140
|
enableRealTime: true,
|
|
1278
1141
|
debounceMs: 500,
|
|
1279
1142
|
enableSanitization: true,
|
|
1280
|
-
securityLevel: 'standard'
|
|
1143
|
+
securityLevel: 'standard'
|
|
1281
1144
|
}),
|
|
1282
1145
|
expect.any(Function), // formatSanitizerMessage
|
|
1283
|
-
expect.any(Function)
|
|
1146
|
+
expect.any(Function) // formatValidatorMessage
|
|
1284
1147
|
);
|
|
1285
1148
|
});
|
|
1286
1149
|
|
|
@@ -1300,14 +1163,13 @@ describe('HTMLEditor', () => {
|
|
|
1300
1163
|
expect.any(String),
|
|
1301
1164
|
expect.any(String),
|
|
1302
1165
|
expect.objectContaining({
|
|
1303
|
-
apiValidationErrors: null,
|
|
1304
1166
|
enableRealTime: true,
|
|
1305
1167
|
debounceMs: 500,
|
|
1306
1168
|
enableSanitization: true,
|
|
1307
|
-
securityLevel: 'standard'
|
|
1169
|
+
securityLevel: 'standard'
|
|
1308
1170
|
}),
|
|
1309
1171
|
expect.any(Function), // formatSanitizerMessage
|
|
1310
|
-
expect.any(Function)
|
|
1172
|
+
expect.any(Function) // formatValidatorMessage
|
|
1311
1173
|
);
|
|
1312
1174
|
});
|
|
1313
1175
|
|
|
@@ -1370,11 +1232,10 @@ describe('HTMLEditor', () => {
|
|
|
1370
1232
|
expect.any(String), // currentContent
|
|
1371
1233
|
'email', // variant
|
|
1372
1234
|
{
|
|
1373
|
-
apiValidationErrors: null,
|
|
1374
1235
|
enableRealTime: true,
|
|
1375
1236
|
debounceMs: 500,
|
|
1376
1237
|
enableSanitization: true,
|
|
1377
|
-
securityLevel: 'standard'
|
|
1238
|
+
securityLevel: 'standard'
|
|
1378
1239
|
},
|
|
1379
1240
|
expect.any(Function),
|
|
1380
1241
|
expect.any(Function)
|
|
@@ -1465,8 +1326,8 @@ describe('HTMLEditor', () => {
|
|
|
1465
1326
|
<div className="html-editor html-editor--email">
|
|
1466
1327
|
<CustomToolbar
|
|
1467
1328
|
onLabelInsert={handleLabelInsert}
|
|
1468
|
-
onToggleFullscreen={() => {
|
|
1469
|
-
onSave={() => {
|
|
1329
|
+
onToggleFullscreen={() => {}}
|
|
1330
|
+
onSave={() => {}}
|
|
1470
1331
|
/>
|
|
1471
1332
|
<div data-testid="split-container">
|
|
1472
1333
|
<div data-testid="code-editor-pane" ref={editorRef}>
|
|
@@ -1564,7 +1425,7 @@ describe('HTMLEditor', () => {
|
|
|
1564
1425
|
isValidating: false,
|
|
1565
1426
|
getAllIssues: () => [{ message: 'Test error', severity: 'error' }],
|
|
1566
1427
|
isClean: () => false,
|
|
1567
|
-
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1428
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1568
1429
|
});
|
|
1569
1430
|
|
|
1570
1431
|
// Create a custom validation display that triggers the error click handler
|
|
@@ -1640,7 +1501,7 @@ describe('HTMLEditor', () => {
|
|
|
1640
1501
|
current: {
|
|
1641
1502
|
navigateToLine: jest.fn(),
|
|
1642
1503
|
focus: jest.fn(),
|
|
1643
|
-
}
|
|
1504
|
+
}
|
|
1644
1505
|
};
|
|
1645
1506
|
|
|
1646
1507
|
// Mock validation to show errors
|
|
@@ -1648,7 +1509,7 @@ describe('HTMLEditor', () => {
|
|
|
1648
1509
|
isValidating: false,
|
|
1649
1510
|
getAllIssues: () => [{ message: 'Test error', severity: 'error' }],
|
|
1650
1511
|
isClean: () => false,
|
|
1651
|
-
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1512
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1652
1513
|
});
|
|
1653
1514
|
|
|
1654
1515
|
const TestComponent = () => {
|
|
@@ -1682,7 +1543,7 @@ describe('HTMLEditor', () => {
|
|
|
1682
1543
|
current: {
|
|
1683
1544
|
focus: jest.fn(),
|
|
1684
1545
|
// Missing navigateToLine method
|
|
1685
|
-
}
|
|
1546
|
+
}
|
|
1686
1547
|
};
|
|
1687
1548
|
|
|
1688
1549
|
const TestComponent = () => {
|
|
@@ -1762,64 +1623,41 @@ describe('HTMLEditor', () => {
|
|
|
1762
1623
|
});
|
|
1763
1624
|
|
|
1764
1625
|
describe('Loading State Coverage (Lines 343-349)', () => {
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
toggleFullscreen: jest.fn(),
|
|
1782
|
-
resetLayout: jest.fn(),
|
|
1783
|
-
setResizingState: jest.fn(),
|
|
1784
|
-
handleResize: jest.fn(),
|
|
1785
|
-
handleKeyboardShortcut: jest.fn(),
|
|
1786
|
-
isMobileView: false,
|
|
1787
|
-
isDesktopView: true,
|
|
1788
|
-
minPaneSize: 20,
|
|
1789
|
-
maxPaneSize: 80,
|
|
1790
|
-
gutterSize: 10,
|
|
1791
|
-
}));
|
|
1626
|
+
// Create separate test components to isolate mock effects
|
|
1627
|
+
const LoadingTestComponent = ({ mockContent = null, mockLayout = null }) => {
|
|
1628
|
+
// Mock the hooks inline for this specific test
|
|
1629
|
+
const useEditorContentMock = jest.fn(() => mockContent);
|
|
1630
|
+
const useInAppContentMock = jest.fn(() => mockContent);
|
|
1631
|
+
const useLayoutStateMock = jest.fn(() => mockLayout);
|
|
1632
|
+
|
|
1633
|
+
// Replace the hooks temporarily
|
|
1634
|
+
React.useMemo(() => {
|
|
1635
|
+
require('../hooks/useEditorContent').useEditorContent = useEditorContentMock;
|
|
1636
|
+
require('../hooks/useInAppContent').useInAppContent = useInAppContentMock;
|
|
1637
|
+
require('../hooks/useLayoutState').useLayoutState = useLayoutStateMock;
|
|
1638
|
+
}, []);
|
|
1639
|
+
|
|
1640
|
+
return <HTMLEditor {...defaultProps} />;
|
|
1641
|
+
};
|
|
1792
1642
|
|
|
1643
|
+
it('shows loading state when content is null', () => {
|
|
1793
1644
|
render(
|
|
1794
1645
|
<TestWrapper>
|
|
1795
|
-
<
|
|
1646
|
+
<LoadingTestComponent mockContent={null} mockLayout={{ splitSizes: [50, 50] }} />
|
|
1796
1647
|
</TestWrapper>
|
|
1797
1648
|
);
|
|
1798
1649
|
|
|
1799
1650
|
// Should show loading state
|
|
1800
1651
|
expect(screen.getByText('Initializing HTML Editor...')).toBeInTheDocument();
|
|
1801
|
-
|
|
1802
|
-
// Restore original mock
|
|
1803
|
-
require('../hooks/useEditorContent').useEditorContent = originalUseEditorContent;
|
|
1804
1652
|
});
|
|
1805
1653
|
|
|
1806
1654
|
it('shows loading state when layout is null', () => {
|
|
1807
|
-
// Temporarily override the mock to return null layout
|
|
1808
|
-
require('../hooks/useLayoutState').useLayoutState = jest.fn(() => null);
|
|
1809
|
-
require('../hooks/useEditorContent').useEditorContent = jest.fn(() => ({
|
|
1810
|
-
content: '<p>Test</p>',
|
|
1811
|
-
updateContent: jest.fn(),
|
|
1812
|
-
saveContent: jest.fn(),
|
|
1813
|
-
markAsSaved: jest.fn(),
|
|
1814
|
-
isLoading: false,
|
|
1815
|
-
isDirty: false,
|
|
1816
|
-
hasContent: true,
|
|
1817
|
-
getContentSize: jest.fn(() => 20),
|
|
1818
|
-
}));
|
|
1819
|
-
|
|
1820
1655
|
render(
|
|
1821
1656
|
<TestWrapper>
|
|
1822
|
-
<
|
|
1657
|
+
<LoadingTestComponent
|
|
1658
|
+
mockContent={{ content: '<p>Test</p>' }}
|
|
1659
|
+
mockLayout={null}
|
|
1660
|
+
/>
|
|
1823
1661
|
</TestWrapper>
|
|
1824
1662
|
);
|
|
1825
1663
|
|
|
@@ -1828,12 +1666,9 @@ describe('HTMLEditor', () => {
|
|
|
1828
1666
|
});
|
|
1829
1667
|
|
|
1830
1668
|
it('shows loading state when both content and layout are null', () => {
|
|
1831
|
-
require('../hooks/useEditorContent').useEditorContent = jest.fn(() => null);
|
|
1832
|
-
require('../hooks/useLayoutState').useLayoutState = jest.fn(() => null);
|
|
1833
|
-
|
|
1834
1669
|
render(
|
|
1835
1670
|
<TestWrapper>
|
|
1836
|
-
<
|
|
1671
|
+
<LoadingTestComponent mockContent={null} mockLayout={null} />
|
|
1837
1672
|
</TestWrapper>
|
|
1838
1673
|
);
|
|
1839
1674
|
|
|
@@ -1841,43 +1676,12 @@ describe('HTMLEditor', () => {
|
|
|
1841
1676
|
});
|
|
1842
1677
|
|
|
1843
1678
|
it('renders normally when both content and layout are available', () => {
|
|
1844
|
-
require('../hooks/useEditorContent').useEditorContent = jest.fn(() => ({
|
|
1845
|
-
content: '<p>Test</p>',
|
|
1846
|
-
updateContent: jest.fn(),
|
|
1847
|
-
saveContent: jest.fn(),
|
|
1848
|
-
markAsSaved: jest.fn(),
|
|
1849
|
-
isLoading: false,
|
|
1850
|
-
isDirty: false,
|
|
1851
|
-
hasContent: true,
|
|
1852
|
-
getContentSize: jest.fn(() => 20),
|
|
1853
|
-
}));
|
|
1854
|
-
require('../hooks/useLayoutState').useLayoutState = jest.fn(() => ({
|
|
1855
|
-
splitSizes: [50, 50],
|
|
1856
|
-
splitSize: 50,
|
|
1857
|
-
viewMode: 'desktop',
|
|
1858
|
-
mobileWidth: 375,
|
|
1859
|
-
isFullscreen: false,
|
|
1860
|
-
isResizing: false,
|
|
1861
|
-
updateSplitSizes: jest.fn(),
|
|
1862
|
-
setSplitSize: jest.fn(),
|
|
1863
|
-
setViewMode: jest.fn(),
|
|
1864
|
-
toggleViewMode: jest.fn(),
|
|
1865
|
-
setMobileWidth: jest.fn(),
|
|
1866
|
-
toggleFullscreen: jest.fn(),
|
|
1867
|
-
resetLayout: jest.fn(),
|
|
1868
|
-
setResizingState: jest.fn(),
|
|
1869
|
-
handleResize: jest.fn(),
|
|
1870
|
-
handleKeyboardShortcut: jest.fn(),
|
|
1871
|
-
isMobileView: false,
|
|
1872
|
-
isDesktopView: true,
|
|
1873
|
-
minPaneSize: 20,
|
|
1874
|
-
maxPaneSize: 80,
|
|
1875
|
-
gutterSize: 10,
|
|
1876
|
-
}));
|
|
1877
|
-
|
|
1878
1679
|
render(
|
|
1879
1680
|
<TestWrapper>
|
|
1880
|
-
<
|
|
1681
|
+
<LoadingTestComponent
|
|
1682
|
+
mockContent={{ content: '<p>Test</p>' }}
|
|
1683
|
+
mockLayout={{ splitSizes: [50, 50] }}
|
|
1684
|
+
/>
|
|
1881
1685
|
</TestWrapper>
|
|
1882
1686
|
);
|
|
1883
1687
|
|
|
@@ -1898,7 +1702,7 @@ describe('HTMLEditor', () => {
|
|
|
1898
1702
|
isValidating: false,
|
|
1899
1703
|
getAllIssues: () => [{ message: 'Test error', severity: 'error' }],
|
|
1900
1704
|
isClean: () => false,
|
|
1901
|
-
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1705
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1902
1706
|
});
|
|
1903
1707
|
|
|
1904
1708
|
render(
|
|
@@ -1930,7 +1734,7 @@ describe('HTMLEditor', () => {
|
|
|
1930
1734
|
isValidating: false,
|
|
1931
1735
|
getAllIssues: () => [{ message: 'Test error', severity: 'error' }],
|
|
1932
1736
|
isClean: () => false,
|
|
1933
|
-
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1737
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
1934
1738
|
});
|
|
1935
1739
|
|
|
1936
1740
|
render(
|
|
@@ -2001,1558 +1805,5 @@ describe('HTMLEditor', () => {
|
|
|
2001
1805
|
unmount();
|
|
2002
1806
|
});
|
|
2003
1807
|
});
|
|
1808
|
+
});
|
|
2004
1809
|
|
|
2005
|
-
describe('handleContextChange Coverage', () => {
|
|
2006
|
-
it('calls onContextChange when provided instead of making API call', () => {
|
|
2007
|
-
const onContextChange = jest.fn();
|
|
2008
|
-
const globalActions = {
|
|
2009
|
-
fetchSchemaForEntity: jest.fn(),
|
|
2010
|
-
};
|
|
2011
|
-
|
|
2012
|
-
render(
|
|
2013
|
-
<TestWrapper>
|
|
2014
|
-
<HTMLEditor
|
|
2015
|
-
{...defaultProps}
|
|
2016
|
-
onContextChange={onContextChange}
|
|
2017
|
-
globalActions={globalActions}
|
|
2018
|
-
location={{ query: { type: 'embedded' } }}
|
|
2019
|
-
/>
|
|
2020
|
-
</TestWrapper>
|
|
2021
|
-
);
|
|
2022
|
-
|
|
2023
|
-
act(() => {
|
|
2024
|
-
jest.runAllTimers();
|
|
2025
|
-
});
|
|
2026
|
-
|
|
2027
|
-
// Wait for component to render
|
|
2028
|
-
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2029
|
-
if (!codeEditorPane) {
|
|
2030
|
-
// Component might be in loading state, wait a bit more
|
|
2031
|
-
act(() => {
|
|
2032
|
-
jest.runAllTimers();
|
|
2033
|
-
});
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
// The CodeEditorPane would call onContextChange
|
|
2037
|
-
// We verify that onContextChange is passed and would be called
|
|
2038
|
-
expect(onContextChange).toBeDefined();
|
|
2039
|
-
|
|
2040
|
-
// If onContextChange is provided, globalActions.fetchSchemaForEntity should not be called
|
|
2041
|
-
// This is tested by ensuring onContextChange is used instead
|
|
2042
|
-
});
|
|
2043
|
-
|
|
2044
|
-
it('makes API call when onContextChange is not provided but globalActions is available', () => {
|
|
2045
|
-
const globalActions = {
|
|
2046
|
-
fetchSchemaForEntity: jest.fn(),
|
|
2047
|
-
};
|
|
2048
|
-
|
|
2049
|
-
render(
|
|
2050
|
-
<TestWrapper>
|
|
2051
|
-
<HTMLEditor
|
|
2052
|
-
{...defaultProps}
|
|
2053
|
-
onContextChange={null}
|
|
2054
|
-
globalActions={globalActions}
|
|
2055
|
-
location={{ query: { type: 'embedded' } }}
|
|
2056
|
-
/>
|
|
2057
|
-
</TestWrapper>
|
|
2058
|
-
);
|
|
2059
|
-
|
|
2060
|
-
act(() => {
|
|
2061
|
-
jest.runAllTimers();
|
|
2062
|
-
});
|
|
2063
|
-
|
|
2064
|
-
// Wait for component to render
|
|
2065
|
-
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
2066
|
-
if (!toolbar) {
|
|
2067
|
-
act(() => {
|
|
2068
|
-
jest.runAllTimers();
|
|
2069
|
-
});
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
|
-
// The handleContextChange would be called by CodeEditorPane
|
|
2073
|
-
// We verify globalActions is available
|
|
2074
|
-
expect(globalActions.fetchSchemaForEntity).toBeDefined();
|
|
2075
|
-
});
|
|
2076
|
-
|
|
2077
|
-
it('does not make API call when globalActions is not available', () => {
|
|
2078
|
-
render(
|
|
2079
|
-
<TestWrapper>
|
|
2080
|
-
<HTMLEditor
|
|
2081
|
-
{...defaultProps}
|
|
2082
|
-
onContextChange={null}
|
|
2083
|
-
globalActions={null}
|
|
2084
|
-
location={{ query: { type: 'embedded' } }}
|
|
2085
|
-
/>
|
|
2086
|
-
</TestWrapper>
|
|
2087
|
-
);
|
|
2088
|
-
|
|
2089
|
-
act(() => {
|
|
2090
|
-
jest.runAllTimers();
|
|
2091
|
-
});
|
|
2092
|
-
|
|
2093
|
-
// Wait for component to render - might be in loading state
|
|
2094
|
-
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
2095
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2096
|
-
|
|
2097
|
-
// Component should render (either loaded or loading)
|
|
2098
|
-
expect(toolbar || loading).toBeTruthy();
|
|
2099
|
-
});
|
|
2100
|
-
|
|
2101
|
-
it('uses SMS layout for INAPP variant in handleContextChange', () => {
|
|
2102
|
-
const globalActions = {
|
|
2103
|
-
fetchSchemaForEntity: jest.fn(),
|
|
2104
|
-
};
|
|
2105
|
-
|
|
2106
|
-
render(
|
|
2107
|
-
<TestWrapper>
|
|
2108
|
-
<HTMLEditor
|
|
2109
|
-
{...defaultProps}
|
|
2110
|
-
variant="inapp"
|
|
2111
|
-
onContextChange={null}
|
|
2112
|
-
globalActions={globalActions}
|
|
2113
|
-
location={{ query: { type: 'embedded' } }}
|
|
2114
|
-
/>
|
|
2115
|
-
</TestWrapper>
|
|
2116
|
-
);
|
|
2117
|
-
|
|
2118
|
-
act(() => {
|
|
2119
|
-
jest.runAllTimers();
|
|
2120
|
-
});
|
|
2121
|
-
|
|
2122
|
-
// Wait for component to render
|
|
2123
|
-
const deviceToggle = screen.queryByTestId('device-toggle');
|
|
2124
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2125
|
-
|
|
2126
|
-
// Component should render (either loaded or loading)
|
|
2127
|
-
expect(deviceToggle || loading).toBeTruthy();
|
|
2128
|
-
});
|
|
2129
|
-
|
|
2130
|
-
it('handles context change with ALL context type', () => {
|
|
2131
|
-
const globalActions = {
|
|
2132
|
-
fetchSchemaForEntity: jest.fn(),
|
|
2133
|
-
};
|
|
2134
|
-
|
|
2135
|
-
render(
|
|
2136
|
-
<TestWrapper>
|
|
2137
|
-
<HTMLEditor
|
|
2138
|
-
{...defaultProps}
|
|
2139
|
-
onContextChange={null}
|
|
2140
|
-
globalActions={globalActions}
|
|
2141
|
-
location={{ query: { type: 'embedded' } }}
|
|
2142
|
-
/>
|
|
2143
|
-
</TestWrapper>
|
|
2144
|
-
);
|
|
2145
|
-
|
|
2146
|
-
act(() => {
|
|
2147
|
-
jest.runAllTimers();
|
|
2148
|
-
});
|
|
2149
|
-
|
|
2150
|
-
// Component should render
|
|
2151
|
-
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
2152
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2153
|
-
expect(toolbar || loading).toBeTruthy();
|
|
2154
|
-
});
|
|
2155
|
-
|
|
2156
|
-
it('handles context change with embedded type', () => {
|
|
2157
|
-
const globalActions = {
|
|
2158
|
-
fetchSchemaForEntity: jest.fn(),
|
|
2159
|
-
};
|
|
2160
|
-
|
|
2161
|
-
render(
|
|
2162
|
-
<TestWrapper>
|
|
2163
|
-
<HTMLEditor
|
|
2164
|
-
{...defaultProps}
|
|
2165
|
-
onContextChange={null}
|
|
2166
|
-
globalActions={globalActions}
|
|
2167
|
-
location={{ query: { type: 'embedded', module: 'test' } }}
|
|
2168
|
-
/>
|
|
2169
|
-
</TestWrapper>
|
|
2170
|
-
);
|
|
2171
|
-
|
|
2172
|
-
act(() => {
|
|
2173
|
-
jest.runAllTimers();
|
|
2174
|
-
});
|
|
2175
|
-
|
|
2176
|
-
// Component should render
|
|
2177
|
-
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
2178
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2179
|
-
expect(toolbar || loading).toBeTruthy();
|
|
2180
|
-
});
|
|
2181
|
-
});
|
|
2182
|
-
|
|
2183
|
-
describe('handleLabelInsert Coverage', () => {
|
|
2184
|
-
it('handles label insert when position is null and editor is ready', () => {
|
|
2185
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2186
|
-
|
|
2187
|
-
render(
|
|
2188
|
-
<TestWrapper>
|
|
2189
|
-
<HTMLEditor {...defaultProps} />
|
|
2190
|
-
</TestWrapper>
|
|
2191
|
-
);
|
|
2192
|
-
|
|
2193
|
-
act(() => {
|
|
2194
|
-
jest.runAllTimers();
|
|
2195
|
-
});
|
|
2196
|
-
|
|
2197
|
-
// Wait for component to render
|
|
2198
|
-
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2199
|
-
if (insertButton) {
|
|
2200
|
-
fireEvent.click(insertButton);
|
|
2201
|
-
|
|
2202
|
-
// Should attempt to insert via editor ref
|
|
2203
|
-
// The mock editor has insertText method, so it should work
|
|
2204
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2208
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2209
|
-
expect(codeEditorPane || loading).toBeTruthy();
|
|
2210
|
-
});
|
|
2211
|
-
|
|
2212
|
-
it('shows warning when editor is not available for label insert', () => {
|
|
2213
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2214
|
-
|
|
2215
|
-
// Mock CodeEditorPane to return null ref
|
|
2216
|
-
jest.doMock('../components/CodeEditorPane', () => {
|
|
2217
|
-
const React = require('react');
|
|
2218
|
-
return React.forwardRef(() => {
|
|
2219
|
-
// Return null ref
|
|
2220
|
-
React.useImperativeHandle(null, () => null);
|
|
2221
|
-
return <div data-testid="code-editor-pane">Editor</div>;
|
|
2222
|
-
});
|
|
2223
|
-
});
|
|
2224
|
-
|
|
2225
|
-
render(
|
|
2226
|
-
<TestWrapper>
|
|
2227
|
-
<HTMLEditor {...defaultProps} />
|
|
2228
|
-
</TestWrapper>
|
|
2229
|
-
);
|
|
2230
|
-
|
|
2231
|
-
act(() => {
|
|
2232
|
-
jest.runAllTimers();
|
|
2233
|
-
});
|
|
2234
|
-
|
|
2235
|
-
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2236
|
-
if (insertButton) {
|
|
2237
|
-
fireEvent.click(insertButton);
|
|
2238
|
-
// Should show warning when editor is null
|
|
2239
|
-
expect(CapNotification.warning).toHaveBeenCalled();
|
|
2240
|
-
}
|
|
2241
|
-
});
|
|
2242
|
-
|
|
2243
|
-
it('shows error when editor does not have insertText method', () => {
|
|
2244
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2245
|
-
|
|
2246
|
-
// Mock CodeEditorPane to return editor without insertText
|
|
2247
|
-
jest.doMock('../components/CodeEditorPane', () => {
|
|
2248
|
-
const React = require('react');
|
|
2249
|
-
return React.forwardRef((props, ref) => {
|
|
2250
|
-
React.useImperativeHandle(ref, () => ({
|
|
2251
|
-
focus: jest.fn(),
|
|
2252
|
-
getCursor: jest.fn(() => 0),
|
|
2253
|
-
// No insertText method
|
|
2254
|
-
}));
|
|
2255
|
-
return <div data-testid="code-editor-pane">Editor</div>;
|
|
2256
|
-
});
|
|
2257
|
-
});
|
|
2258
|
-
|
|
2259
|
-
render(
|
|
2260
|
-
<TestWrapper>
|
|
2261
|
-
<HTMLEditor {...defaultProps} />
|
|
2262
|
-
</TestWrapper>
|
|
2263
|
-
);
|
|
2264
|
-
|
|
2265
|
-
act(() => {
|
|
2266
|
-
jest.runAllTimers();
|
|
2267
|
-
});
|
|
2268
|
-
|
|
2269
|
-
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2270
|
-
if (insertButton) {
|
|
2271
|
-
fireEvent.click(insertButton);
|
|
2272
|
-
// Should show error when insertText is not available
|
|
2273
|
-
expect(CapNotification.error).toHaveBeenCalled();
|
|
2274
|
-
}
|
|
2275
|
-
});
|
|
2276
|
-
|
|
2277
|
-
it('handles label insert when position is provided (already inserted)', () => {
|
|
2278
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2279
|
-
|
|
2280
|
-
render(
|
|
2281
|
-
<TestWrapper>
|
|
2282
|
-
<HTMLEditor {...defaultProps} />
|
|
2283
|
-
</TestWrapper>
|
|
2284
|
-
);
|
|
2285
|
-
|
|
2286
|
-
act(() => {
|
|
2287
|
-
jest.runAllTimers();
|
|
2288
|
-
});
|
|
2289
|
-
|
|
2290
|
-
// Simulate label insert with position (already inserted by CodeEditorPane)
|
|
2291
|
-
// This would call handleLabelInsert with a valid position
|
|
2292
|
-
const insertButton = screen.queryByText('Insert Label');
|
|
2293
|
-
if (insertButton) {
|
|
2294
|
-
fireEvent.click(insertButton);
|
|
2295
|
-
// Should show success notification
|
|
2296
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
2297
|
-
}
|
|
2298
|
-
});
|
|
2299
|
-
|
|
2300
|
-
it('handles error during label insert', () => {
|
|
2301
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2302
|
-
|
|
2303
|
-
// Mock CodeEditorPane to throw error on insertText
|
|
2304
|
-
jest.doMock('../components/CodeEditorPane', () => {
|
|
2305
|
-
const React = require('react');
|
|
2306
|
-
return React.forwardRef((props, ref) => {
|
|
2307
|
-
React.useImperativeHandle(ref, () => ({
|
|
2308
|
-
insertText: jest.fn(() => {
|
|
2309
|
-
throw new Error('Insert failed');
|
|
2310
|
-
}),
|
|
2311
|
-
focus: jest.fn(),
|
|
2312
|
-
getCursor: jest.fn(() => 0),
|
|
2313
|
-
}));
|
|
2314
|
-
return <div data-testid="code-editor-pane">Editor</div>;
|
|
2315
|
-
});
|
|
2316
|
-
});
|
|
2317
|
-
|
|
2318
|
-
render(
|
|
2319
|
-
<TestWrapper>
|
|
2320
|
-
<HTMLEditor {...defaultProps} />
|
|
2321
|
-
</TestWrapper>
|
|
2322
|
-
);
|
|
2323
|
-
|
|
2324
|
-
act(() => {
|
|
2325
|
-
jest.runAllTimers();
|
|
2326
|
-
});
|
|
2327
|
-
|
|
2328
|
-
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2329
|
-
if (insertButton) {
|
|
2330
|
-
fireEvent.click(insertButton);
|
|
2331
|
-
// Should show error notification
|
|
2332
|
-
expect(CapNotification.error).toHaveBeenCalled();
|
|
2333
|
-
}
|
|
2334
|
-
});
|
|
2335
|
-
});
|
|
2336
|
-
|
|
2337
|
-
describe('handleSave Coverage', () => {
|
|
2338
|
-
it('calls onSave callback when save is triggered', () => {
|
|
2339
|
-
const onSave = jest.fn();
|
|
2340
|
-
|
|
2341
|
-
render(
|
|
2342
|
-
<TestWrapper>
|
|
2343
|
-
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
2344
|
-
</TestWrapper>
|
|
2345
|
-
);
|
|
2346
|
-
|
|
2347
|
-
act(() => {
|
|
2348
|
-
jest.runAllTimers();
|
|
2349
|
-
});
|
|
2350
|
-
|
|
2351
|
-
const saveButton = screen.queryByText('Save');
|
|
2352
|
-
if (saveButton) {
|
|
2353
|
-
fireEvent.click(saveButton);
|
|
2354
|
-
// onSave should be called
|
|
2355
|
-
expect(onSave).toHaveBeenCalled();
|
|
2356
|
-
} else {
|
|
2357
|
-
// Component might be in loading state
|
|
2358
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2359
|
-
expect(loading).toBeTruthy();
|
|
2360
|
-
}
|
|
2361
|
-
});
|
|
2362
|
-
|
|
2363
|
-
it('shows success notification on save', () => {
|
|
2364
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2365
|
-
const onSave = jest.fn();
|
|
2366
|
-
|
|
2367
|
-
render(
|
|
2368
|
-
<TestWrapper>
|
|
2369
|
-
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
2370
|
-
</TestWrapper>
|
|
2371
|
-
);
|
|
2372
|
-
|
|
2373
|
-
act(() => {
|
|
2374
|
-
jest.runAllTimers();
|
|
2375
|
-
});
|
|
2376
|
-
|
|
2377
|
-
const saveButton = screen.queryByText('Save');
|
|
2378
|
-
if (saveButton) {
|
|
2379
|
-
fireEvent.click(saveButton);
|
|
2380
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
2381
|
-
}
|
|
2382
|
-
});
|
|
2383
|
-
|
|
2384
|
-
it('handles save error gracefully', () => {
|
|
2385
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2386
|
-
const onSave = jest.fn(() => {
|
|
2387
|
-
throw new Error('Save failed');
|
|
2388
|
-
});
|
|
2389
|
-
|
|
2390
|
-
render(
|
|
2391
|
-
<TestWrapper>
|
|
2392
|
-
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
2393
|
-
</TestWrapper>
|
|
2394
|
-
);
|
|
2395
|
-
|
|
2396
|
-
act(() => {
|
|
2397
|
-
jest.runAllTimers();
|
|
2398
|
-
});
|
|
2399
|
-
|
|
2400
|
-
const saveButton = screen.queryByText('Save');
|
|
2401
|
-
if (saveButton) {
|
|
2402
|
-
fireEvent.click(saveButton);
|
|
2403
|
-
expect(CapNotification.error).toHaveBeenCalled();
|
|
2404
|
-
}
|
|
2405
|
-
});
|
|
2406
|
-
});
|
|
2407
|
-
|
|
2408
|
-
describe('handleValidationErrorClick Coverage', () => {
|
|
2409
|
-
it('navigates to error line when editor has navigateToLine method', () => {
|
|
2410
|
-
mockUseValidationImpl.mockReturnValueOnce({
|
|
2411
|
-
isValidating: false,
|
|
2412
|
-
getAllIssues: () => [{
|
|
2413
|
-
message: 'Test error', line: 5, column: 10, severity: 'error',
|
|
2414
|
-
}],
|
|
2415
|
-
isClean: () => false,
|
|
2416
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
2417
|
-
});
|
|
2418
|
-
|
|
2419
|
-
render(
|
|
2420
|
-
<TestWrapper>
|
|
2421
|
-
<HTMLEditor {...defaultProps} />
|
|
2422
|
-
</TestWrapper>
|
|
2423
|
-
);
|
|
2424
|
-
|
|
2425
|
-
act(() => {
|
|
2426
|
-
jest.runAllTimers();
|
|
2427
|
-
});
|
|
2428
|
-
|
|
2429
|
-
// Click on error
|
|
2430
|
-
const errorButton = screen.queryByText('Error at Line 5');
|
|
2431
|
-
if (errorButton) {
|
|
2432
|
-
fireEvent.click(errorButton);
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2435
|
-
// Should attempt to navigate to line
|
|
2436
|
-
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2437
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2438
|
-
expect(codeEditorPane || loading).toBeTruthy();
|
|
2439
|
-
});
|
|
2440
|
-
|
|
2441
|
-
it('focuses editor when navigateToLine is not available', () => {
|
|
2442
|
-
mockCodeEditorOptions.includeNavigateToLine = false;
|
|
2443
|
-
|
|
2444
|
-
mockUseValidationImpl.mockReturnValueOnce({
|
|
2445
|
-
isValidating: false,
|
|
2446
|
-
getAllIssues: () => [{
|
|
2447
|
-
message: 'Test error', line: 5, column: 10, severity: 'error',
|
|
2448
|
-
}],
|
|
2449
|
-
isClean: () => false,
|
|
2450
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
2451
|
-
});
|
|
2452
|
-
|
|
2453
|
-
render(
|
|
2454
|
-
<TestWrapper>
|
|
2455
|
-
<HTMLEditor {...defaultProps} />
|
|
2456
|
-
</TestWrapper>
|
|
2457
|
-
);
|
|
2458
|
-
|
|
2459
|
-
act(() => {
|
|
2460
|
-
jest.runAllTimers();
|
|
2461
|
-
});
|
|
2462
|
-
|
|
2463
|
-
// Component should render
|
|
2464
|
-
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2465
|
-
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2466
|
-
expect(codeEditorPane || loading).toBeTruthy();
|
|
2467
|
-
});
|
|
2468
|
-
});
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
describe('Additional Coverage Tests', () => {
|
|
2472
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2473
|
-
|
|
2474
|
-
beforeEach(() => {
|
|
2475
|
-
// Reset options to default
|
|
2476
|
-
mockCodeEditorOptions.includeInsertText = true;
|
|
2477
|
-
mockCodeEditorOptions.insertTextThrows = false;
|
|
2478
|
-
mockCodeEditorOptions.setRef = true;
|
|
2479
|
-
mockCodeEditorOptions.navigateToLineThrows = false;
|
|
2480
|
-
mockCodeEditorOptions.includeNavigateToLine = true;
|
|
2481
|
-
|
|
2482
|
-
// Clear specific mocks instead of all to avoid breaking other mocks
|
|
2483
|
-
CapNotification.warning.mockClear();
|
|
2484
|
-
CapNotification.error.mockClear();
|
|
2485
|
-
CapNotification.success.mockClear();
|
|
2486
|
-
|
|
2487
|
-
// Restore hooks that might have been corrupted by Loading State Coverage tests
|
|
2488
|
-
// This is necessary because Loading State Coverage tests modify the require cache
|
|
2489
|
-
// and do not restore the original mocks
|
|
2490
|
-
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
2491
|
-
content: '<p>Test content</p>',
|
|
2492
|
-
updateContent: jest.fn(),
|
|
2493
|
-
saveContent: jest.fn(),
|
|
2494
|
-
markAsSaved: jest.fn(),
|
|
2495
|
-
isLoading: false,
|
|
2496
|
-
isDirty: false,
|
|
2497
|
-
hasContent: true,
|
|
2498
|
-
getContentSize: jest.fn(() => 20),
|
|
2499
|
-
});
|
|
2500
|
-
|
|
2501
|
-
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
2502
|
-
content: '<p>Android content</p>',
|
|
2503
|
-
deviceContent: {
|
|
2504
|
-
android: '<p>Android content</p>',
|
|
2505
|
-
ios: '<p>iOS content</p>',
|
|
2506
|
-
},
|
|
2507
|
-
activeDevice: 'android',
|
|
2508
|
-
keepContentSame: false,
|
|
2509
|
-
updateContent: jest.fn(),
|
|
2510
|
-
saveContent: jest.fn(),
|
|
2511
|
-
markAsSaved: jest.fn(),
|
|
2512
|
-
switchDevice: jest.fn(),
|
|
2513
|
-
toggleContentSync: jest.fn(),
|
|
2514
|
-
getDeviceContent: (device) => `<p>${device} content</p>`,
|
|
2515
|
-
setDeviceContent: jest.fn(),
|
|
2516
|
-
getContentSize: () => 20,
|
|
2517
|
-
isLoading: false,
|
|
2518
|
-
isDirty: false,
|
|
2519
|
-
hasContent: true,
|
|
2520
|
-
});
|
|
2521
|
-
|
|
2522
|
-
require('../hooks/useLayoutState').useLayoutState = () => ({
|
|
2523
|
-
splitSizes: [50, 50],
|
|
2524
|
-
splitSize: 50,
|
|
2525
|
-
viewMode: 'desktop',
|
|
2526
|
-
mobileWidth: 375,
|
|
2527
|
-
isFullscreen: false,
|
|
2528
|
-
isResizing: false,
|
|
2529
|
-
updateSplitSizes: jest.fn(),
|
|
2530
|
-
setSplitSize: jest.fn(),
|
|
2531
|
-
setViewMode: jest.fn(),
|
|
2532
|
-
toggleViewMode: jest.fn(),
|
|
2533
|
-
setMobileWidth: jest.fn(),
|
|
2534
|
-
toggleFullscreen: jest.fn(),
|
|
2535
|
-
resetLayout: jest.fn(),
|
|
2536
|
-
setResizingState: jest.fn(),
|
|
2537
|
-
handleResize: jest.fn(),
|
|
2538
|
-
handleKeyboardShortcut: jest.fn(),
|
|
2539
|
-
isMobileView: false,
|
|
2540
|
-
isDesktopView: true,
|
|
2541
|
-
minPaneSize: 20,
|
|
2542
|
-
maxPaneSize: 80,
|
|
2543
|
-
gutterSize: 10,
|
|
2544
|
-
});
|
|
2545
|
-
});
|
|
2546
|
-
|
|
2547
|
-
describe('handleContextChange', () => {
|
|
2548
|
-
it('calls onContextChange prop when provided', () => {
|
|
2549
|
-
const onContextChange = jest.fn();
|
|
2550
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
2551
|
-
|
|
2552
|
-
render(
|
|
2553
|
-
<TestWrapper>
|
|
2554
|
-
<HTMLEditor
|
|
2555
|
-
{...defaultProps}
|
|
2556
|
-
onContextChange={onContextChange}
|
|
2557
|
-
globalActions={globalActions}
|
|
2558
|
-
/>
|
|
2559
|
-
</TestWrapper>
|
|
2560
|
-
);
|
|
2561
|
-
|
|
2562
|
-
act(() => {
|
|
2563
|
-
jest.runAllTimers();
|
|
2564
|
-
});
|
|
2565
|
-
|
|
2566
|
-
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
2567
|
-
|
|
2568
|
-
expect(onContextChange).toHaveBeenCalledWith('test-context');
|
|
2569
|
-
expect(globalActions.fetchSchemaForEntity).not.toHaveBeenCalled();
|
|
2570
|
-
});
|
|
2571
|
-
|
|
2572
|
-
it('calls globalActions.fetchSchemaForEntity when onContextChange is NOT provided', () => {
|
|
2573
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
2574
|
-
const location = { query: { type: 'embedded' } };
|
|
2575
|
-
|
|
2576
|
-
render(
|
|
2577
|
-
<TestWrapper>
|
|
2578
|
-
<HTMLEditor
|
|
2579
|
-
{...defaultProps}
|
|
2580
|
-
globalActions={globalActions}
|
|
2581
|
-
location={location}
|
|
2582
|
-
variant="email"
|
|
2583
|
-
/>
|
|
2584
|
-
</TestWrapper>
|
|
2585
|
-
);
|
|
2586
|
-
|
|
2587
|
-
act(() => {
|
|
2588
|
-
jest.runAllTimers();
|
|
2589
|
-
});
|
|
2590
|
-
|
|
2591
|
-
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
2592
|
-
|
|
2593
|
-
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
2594
|
-
layout: 'EMAIL',
|
|
2595
|
-
type: 'TAG',
|
|
2596
|
-
context: 'test-context',
|
|
2597
|
-
embedded: 'embedded',
|
|
2598
|
-
});
|
|
2599
|
-
});
|
|
2600
|
-
|
|
2601
|
-
it('handles INAPP variant in handleContextChange', () => {
|
|
2602
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
2603
|
-
const location = { query: { type: 'full' } };
|
|
2604
|
-
|
|
2605
|
-
render(
|
|
2606
|
-
<TestWrapper>
|
|
2607
|
-
<HTMLEditor
|
|
2608
|
-
{...defaultProps}
|
|
2609
|
-
globalActions={globalActions}
|
|
2610
|
-
location={location}
|
|
2611
|
-
variant="inapp"
|
|
2612
|
-
/>
|
|
2613
|
-
</TestWrapper>
|
|
2614
|
-
);
|
|
2615
|
-
|
|
2616
|
-
act(() => {
|
|
2617
|
-
jest.runAllTimers();
|
|
2618
|
-
});
|
|
2619
|
-
|
|
2620
|
-
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
2621
|
-
|
|
2622
|
-
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
2623
|
-
layout: 'SMS', // INAPP uses SMS layout
|
|
2624
|
-
type: 'TAG',
|
|
2625
|
-
context: 'test-context',
|
|
2626
|
-
embedded: 'full',
|
|
2627
|
-
});
|
|
2628
|
-
});
|
|
2629
|
-
|
|
2630
|
-
it('handles missing globalActions or location', () => {
|
|
2631
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
2632
|
-
|
|
2633
|
-
// Case 1: No globalActions
|
|
2634
|
-
const { unmount } = render(
|
|
2635
|
-
<TestWrapper>
|
|
2636
|
-
<HTMLEditor
|
|
2637
|
-
{...defaultProps}
|
|
2638
|
-
globalActions={null}
|
|
2639
|
-
location={{}}
|
|
2640
|
-
/>
|
|
2641
|
-
</TestWrapper>
|
|
2642
|
-
);
|
|
2643
|
-
|
|
2644
|
-
act(() => {
|
|
2645
|
-
jest.runAllTimers();
|
|
2646
|
-
});
|
|
2647
|
-
|
|
2648
|
-
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
2649
|
-
expect(globalActions.fetchSchemaForEntity).not.toHaveBeenCalled();
|
|
2650
|
-
unmount();
|
|
2651
|
-
|
|
2652
|
-
// Case 2: No location
|
|
2653
|
-
render(
|
|
2654
|
-
<TestWrapper>
|
|
2655
|
-
<HTMLEditor
|
|
2656
|
-
{...defaultProps}
|
|
2657
|
-
globalActions={globalActions}
|
|
2658
|
-
location={null}
|
|
2659
|
-
/>
|
|
2660
|
-
</TestWrapper>
|
|
2661
|
-
);
|
|
2662
|
-
|
|
2663
|
-
act(() => {
|
|
2664
|
-
jest.runAllTimers();
|
|
2665
|
-
});
|
|
2666
|
-
|
|
2667
|
-
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
2668
|
-
expect(globalActions.fetchSchemaForEntity).not.toHaveBeenCalled();
|
|
2669
|
-
});
|
|
2670
|
-
});
|
|
2671
|
-
|
|
2672
|
-
describe('handleLabelInsert', () => {
|
|
2673
|
-
it('shows warning when editor is not ready (position null, no editor)', () => {
|
|
2674
|
-
mockCodeEditorOptions.setRef = false;
|
|
2675
|
-
|
|
2676
|
-
render(
|
|
2677
|
-
<TestWrapper>
|
|
2678
|
-
<HTMLEditor {...defaultProps} />
|
|
2679
|
-
</TestWrapper>
|
|
2680
|
-
);
|
|
2681
|
-
|
|
2682
|
-
act(() => {
|
|
2683
|
-
jest.runAllTimers();
|
|
2684
|
-
});
|
|
2685
|
-
|
|
2686
|
-
// Click the button that passes null position
|
|
2687
|
-
const insertButton = screen.getByText('Insert Label (Null Position)');
|
|
2688
|
-
fireEvent.click(insertButton);
|
|
2689
|
-
|
|
2690
|
-
expect(CapNotification.warning).toHaveBeenCalledWith(
|
|
2691
|
-
expect.objectContaining({
|
|
2692
|
-
message: 'Failed to insert label',
|
|
2693
|
-
description: 'Editor is not ready. Please try again.',
|
|
2694
|
-
})
|
|
2695
|
-
);
|
|
2696
|
-
});
|
|
2697
|
-
|
|
2698
|
-
it('shows error when editor method insertText is not available', () => {
|
|
2699
|
-
mockCodeEditorOptions.includeInsertText = false;
|
|
2700
|
-
|
|
2701
|
-
render(
|
|
2702
|
-
<TestWrapper>
|
|
2703
|
-
<HTMLEditor {...defaultProps} />
|
|
2704
|
-
</TestWrapper>
|
|
2705
|
-
);
|
|
2706
|
-
|
|
2707
|
-
act(() => {
|
|
2708
|
-
jest.runAllTimers();
|
|
2709
|
-
});
|
|
2710
|
-
|
|
2711
|
-
const insertButton = screen.getByText('Insert Label (Null Position)');
|
|
2712
|
-
fireEvent.click(insertButton);
|
|
2713
|
-
|
|
2714
|
-
// Should show error when method is missing
|
|
2715
|
-
expect(CapNotification.error).toHaveBeenCalledWith(
|
|
2716
|
-
expect.objectContaining({
|
|
2717
|
-
message: 'Failed to insert label',
|
|
2718
|
-
})
|
|
2719
|
-
);
|
|
2720
|
-
});
|
|
2721
|
-
|
|
2722
|
-
it('successfully inserts label when position is null', () => {
|
|
2723
|
-
mockCodeEditorOptions.includeInsertText = true;
|
|
2724
|
-
|
|
2725
|
-
render(
|
|
2726
|
-
<TestWrapper>
|
|
2727
|
-
<HTMLEditor {...defaultProps} />
|
|
2728
|
-
</TestWrapper>
|
|
2729
|
-
);
|
|
2730
|
-
|
|
2731
|
-
act(() => {
|
|
2732
|
-
jest.runAllTimers();
|
|
2733
|
-
});
|
|
2734
|
-
|
|
2735
|
-
const insertButton = screen.getByText('Insert Label (Null Position)');
|
|
2736
|
-
fireEvent.click(insertButton);
|
|
2737
|
-
|
|
2738
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
2739
|
-
});
|
|
2740
|
-
|
|
2741
|
-
it('shows error when insertText throws', () => {
|
|
2742
|
-
mockCodeEditorOptions.includeInsertText = true;
|
|
2743
|
-
mockCodeEditorOptions.insertTextThrows = true;
|
|
2744
|
-
|
|
2745
|
-
render(
|
|
2746
|
-
<TestWrapper>
|
|
2747
|
-
<HTMLEditor {...defaultProps} />
|
|
2748
|
-
</TestWrapper>
|
|
2749
|
-
);
|
|
2750
|
-
|
|
2751
|
-
act(() => {
|
|
2752
|
-
jest.runAllTimers();
|
|
2753
|
-
});
|
|
2754
|
-
|
|
2755
|
-
const insertButton = screen.getByText('Insert Label (Null Position)');
|
|
2756
|
-
fireEvent.click(insertButton);
|
|
2757
|
-
|
|
2758
|
-
expect(CapNotification.error).toHaveBeenCalledWith(expect.objectContaining({
|
|
2759
|
-
description: 'Insert failed',
|
|
2760
|
-
}));
|
|
2761
|
-
});
|
|
2762
|
-
|
|
2763
|
-
it('shows success notification when position is provided (handled by CodeEditorPane)', () => {
|
|
2764
|
-
render(
|
|
2765
|
-
<TestWrapper>
|
|
2766
|
-
<HTMLEditor {...defaultProps} />
|
|
2767
|
-
</TestWrapper>
|
|
2768
|
-
);
|
|
2769
|
-
|
|
2770
|
-
act(() => {
|
|
2771
|
-
jest.runAllTimers();
|
|
2772
|
-
});
|
|
2773
|
-
|
|
2774
|
-
// Click the button that passes a position
|
|
2775
|
-
const insertButton = screen.getByText('Insert Label');
|
|
2776
|
-
fireEvent.click(insertButton);
|
|
2777
|
-
|
|
2778
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
2779
|
-
});
|
|
2780
|
-
});
|
|
2781
|
-
|
|
2782
|
-
describe('handleValidationErrorClick', () => {
|
|
2783
|
-
it('handles error when navigateToLine throws', () => {
|
|
2784
|
-
mockCodeEditorOptions.navigateToLineThrows = true;
|
|
2785
|
-
|
|
2786
|
-
mockUseValidationImpl.mockReturnValueOnce({
|
|
2787
|
-
isValidating: false,
|
|
2788
|
-
getAllIssues: () => [{
|
|
2789
|
-
message: 'Test error', line: 5, column: 10, severity: 'error',
|
|
2790
|
-
}],
|
|
2791
|
-
isClean: () => false,
|
|
2792
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
2793
|
-
});
|
|
2794
|
-
|
|
2795
|
-
render(
|
|
2796
|
-
<TestWrapper>
|
|
2797
|
-
<HTMLEditor {...defaultProps} />
|
|
2798
|
-
</TestWrapper>
|
|
2799
|
-
);
|
|
2800
|
-
|
|
2801
|
-
act(() => {
|
|
2802
|
-
jest.runAllTimers();
|
|
2803
|
-
});
|
|
2804
|
-
|
|
2805
|
-
// Click error button
|
|
2806
|
-
const errorButton = screen.getByText('Error at Line 5');
|
|
2807
|
-
fireEvent.click(errorButton);
|
|
2808
|
-
});
|
|
2809
|
-
});
|
|
2810
|
-
});
|
|
2811
|
-
|
|
2812
|
-
describe('Ref methods', () => {
|
|
2813
|
-
// Note: Ref method tests are complex due to async nature and ref timing
|
|
2814
|
-
// These methods are tested indirectly through component behavior
|
|
2815
|
-
// Direct ref testing requires complex async setup that can cause timeouts
|
|
2816
|
-
|
|
2817
|
-
describe('onValidationChange callback', () => {
|
|
2818
|
-
it('should call onValidationChange when validation state is available', () => {
|
|
2819
|
-
const onValidationChange = jest.fn();
|
|
2820
|
-
mockUseValidationImpl.mockReturnValue({
|
|
2821
|
-
isValidating: false,
|
|
2822
|
-
hasBlockingErrors: true,
|
|
2823
|
-
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'Error' }],
|
|
2824
|
-
isClean: () => false,
|
|
2825
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
2826
|
-
});
|
|
2827
|
-
|
|
2828
|
-
render(
|
|
2829
|
-
<TestWrapper>
|
|
2830
|
-
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
2831
|
-
</TestWrapper>
|
|
2832
|
-
);
|
|
2833
|
-
|
|
2834
|
-
act(() => {
|
|
2835
|
-
jest.runAllTimers();
|
|
2836
|
-
});
|
|
2837
|
-
|
|
2838
|
-
// onValidationChange should be called when component mounts and validation state is available
|
|
2839
|
-
expect(onValidationChange).toHaveBeenCalled();
|
|
2840
|
-
});
|
|
2841
|
-
});
|
|
2842
|
-
|
|
2843
|
-
describe('useImperativeHandle methods (lines 272-419)', () => {
|
|
2844
|
-
// Note: These methods are tested indirectly through component behavior
|
|
2845
|
-
// Direct ref testing requires complex async setup that can cause timeouts
|
|
2846
|
-
// The methods are covered through integration tests and actual usage
|
|
2847
|
-
// The code paths for getIssueCounts and getValidationState (lines 272-419) are covered
|
|
2848
|
-
// through the validation logic and onValidationChange callback tests
|
|
2849
|
-
|
|
2850
|
-
it('should expose ref methods when component is mounted', async () => {
|
|
2851
|
-
const ref = React.createRef();
|
|
2852
|
-
mockUseValidationImpl.mockReturnValue({
|
|
2853
|
-
isValidating: false,
|
|
2854
|
-
getAllIssues: () => [],
|
|
2855
|
-
isClean: () => true,
|
|
2856
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
2857
|
-
});
|
|
2858
|
-
|
|
2859
|
-
render(
|
|
2860
|
-
<TestWrapper>
|
|
2861
|
-
<HTMLEditor {...defaultProps} ref={ref} />
|
|
2862
|
-
</TestWrapper>
|
|
2863
|
-
);
|
|
2864
|
-
|
|
2865
|
-
act(() => {
|
|
2866
|
-
jest.runAllTimers();
|
|
2867
|
-
});
|
|
2868
|
-
|
|
2869
|
-
// Verify ref methods exist (coverage for lines 272-419)
|
|
2870
|
-
// The actual method calls are covered indirectly through validation tests
|
|
2871
|
-
await waitFor(() => {
|
|
2872
|
-
expect(ref.current).toBeTruthy();
|
|
2873
|
-
}, { timeout: 3000 });
|
|
2874
|
-
|
|
2875
|
-
// Methods should exist on ref (verifies useImperativeHandle is working)
|
|
2876
|
-
// Note: Direct method calls may fail in test environment due to async ref timing
|
|
2877
|
-
// but the code paths are covered through other tests
|
|
2878
|
-
expect(ref.current).toBeTruthy();
|
|
2879
|
-
});
|
|
2880
|
-
});
|
|
2881
|
-
|
|
2882
|
-
describe('getValidation, getContent, isContentEmpty ref methods (lines 275-277)', () => {
|
|
2883
|
-
// Note: These ref methods are exposed via useImperativeHandle but may not be available
|
|
2884
|
-
// in all test environments due to mocking and timing. The code paths are verified
|
|
2885
|
-
// through integration tests and the component's actual usage.
|
|
2886
|
-
|
|
2887
|
-
it('verifies ref methods exist when exposed (coverage for lines 275-277)', async () => {
|
|
2888
|
-
const ref = React.createRef();
|
|
2889
|
-
const mockValidation = {
|
|
2890
|
-
isValidating: false,
|
|
2891
|
-
getAllIssues: () => [{ message: 'test' }],
|
|
2892
|
-
isClean: () => false,
|
|
2893
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
2894
|
-
};
|
|
2895
|
-
mockUseValidationImpl.mockReturnValue(mockValidation);
|
|
2896
|
-
|
|
2897
|
-
render(
|
|
2898
|
-
<TestWrapper>
|
|
2899
|
-
<HTMLEditor {...defaultProps} ref={ref} />
|
|
2900
|
-
</TestWrapper>
|
|
2901
|
-
);
|
|
2902
|
-
|
|
2903
|
-
act(() => {
|
|
2904
|
-
jest.runAllTimers();
|
|
2905
|
-
});
|
|
2906
|
-
|
|
2907
|
-
await waitFor(() => {
|
|
2908
|
-
expect(ref.current).toBeTruthy();
|
|
2909
|
-
}, { timeout: 3000 });
|
|
2910
|
-
|
|
2911
|
-
// Verify ref is available - methods may or may not be present based on environment
|
|
2912
|
-
expect(ref.current).toBeDefined();
|
|
2913
|
-
});
|
|
2914
|
-
|
|
2915
|
-
it('getValidation returns validation state when method is available', async () => {
|
|
2916
|
-
const ref = React.createRef();
|
|
2917
|
-
mockUseValidationImpl.mockReturnValue({
|
|
2918
|
-
isValidating: false,
|
|
2919
|
-
getAllIssues: () => [],
|
|
2920
|
-
isClean: () => true,
|
|
2921
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
2922
|
-
});
|
|
2923
|
-
|
|
2924
|
-
render(
|
|
2925
|
-
<TestWrapper>
|
|
2926
|
-
<HTMLEditor {...defaultProps} ref={ref} initialContent="<p>Test content</p>" />
|
|
2927
|
-
</TestWrapper>
|
|
2928
|
-
);
|
|
2929
|
-
|
|
2930
|
-
act(() => {
|
|
2931
|
-
jest.runAllTimers();
|
|
2932
|
-
});
|
|
2933
|
-
|
|
2934
|
-
await waitFor(() => {
|
|
2935
|
-
expect(ref.current).toBeTruthy();
|
|
2936
|
-
}, { timeout: 3000 });
|
|
2937
|
-
|
|
2938
|
-
// Test method only if available (mocking affects method exposure)
|
|
2939
|
-
if (typeof ref.current.getValidation === 'function') {
|
|
2940
|
-
const validation = ref.current.getValidation();
|
|
2941
|
-
expect(validation).toBeDefined();
|
|
2942
|
-
} else {
|
|
2943
|
-
// Method may not be available in mocked test environment
|
|
2944
|
-
expect(ref.current).toBeDefined();
|
|
2945
|
-
}
|
|
2946
|
-
});
|
|
2947
|
-
|
|
2948
|
-
it('getContent returns string content when method is available', async () => {
|
|
2949
|
-
const ref = React.createRef();
|
|
2950
|
-
mockUseValidationImpl.mockReturnValue({
|
|
2951
|
-
isValidating: false,
|
|
2952
|
-
getAllIssues: () => [],
|
|
2953
|
-
isClean: () => true,
|
|
2954
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
2955
|
-
});
|
|
2956
|
-
|
|
2957
|
-
render(
|
|
2958
|
-
<TestWrapper>
|
|
2959
|
-
<HTMLEditor {...defaultProps} ref={ref} initialContent="" />
|
|
2960
|
-
</TestWrapper>
|
|
2961
|
-
);
|
|
2962
|
-
|
|
2963
|
-
act(() => {
|
|
2964
|
-
jest.runAllTimers();
|
|
2965
|
-
});
|
|
2966
|
-
|
|
2967
|
-
await waitFor(() => {
|
|
2968
|
-
expect(ref.current).toBeTruthy();
|
|
2969
|
-
}, { timeout: 3000 });
|
|
2970
|
-
|
|
2971
|
-
// Test method only if available
|
|
2972
|
-
if (typeof ref.current.getContent === 'function') {
|
|
2973
|
-
const content = ref.current.getContent();
|
|
2974
|
-
expect(typeof content).toBe('string');
|
|
2975
|
-
} else {
|
|
2976
|
-
expect(ref.current).toBeDefined();
|
|
2977
|
-
}
|
|
2978
|
-
});
|
|
2979
|
-
|
|
2980
|
-
it('isContentEmpty handles empty content when method is available (line 277)', async () => {
|
|
2981
|
-
const ref = React.createRef();
|
|
2982
|
-
mockUseValidationImpl.mockReturnValue({
|
|
2983
|
-
isValidating: false,
|
|
2984
|
-
getAllIssues: () => [],
|
|
2985
|
-
isClean: () => true,
|
|
2986
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
2987
|
-
});
|
|
2988
|
-
|
|
2989
|
-
render(
|
|
2990
|
-
<TestWrapper>
|
|
2991
|
-
<HTMLEditor {...defaultProps} ref={ref} initialContent="" />
|
|
2992
|
-
</TestWrapper>
|
|
2993
|
-
);
|
|
2994
|
-
|
|
2995
|
-
act(() => {
|
|
2996
|
-
jest.runAllTimers();
|
|
2997
|
-
});
|
|
2998
|
-
|
|
2999
|
-
await waitFor(() => {
|
|
3000
|
-
expect(ref.current).toBeTruthy();
|
|
3001
|
-
}, { timeout: 3000 });
|
|
3002
|
-
|
|
3003
|
-
// Test method only if available
|
|
3004
|
-
if (typeof ref.current.isContentEmpty === 'function') {
|
|
3005
|
-
expect(ref.current.isContentEmpty()).toBe(true);
|
|
3006
|
-
} else {
|
|
3007
|
-
expect(ref.current).toBeDefined();
|
|
3008
|
-
}
|
|
3009
|
-
});
|
|
3010
|
-
|
|
3011
|
-
it('isContentEmpty handles whitespace content when method is available', async () => {
|
|
3012
|
-
const ref = React.createRef();
|
|
3013
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3014
|
-
isValidating: false,
|
|
3015
|
-
getAllIssues: () => [],
|
|
3016
|
-
isClean: () => true,
|
|
3017
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3018
|
-
});
|
|
3019
|
-
|
|
3020
|
-
render(
|
|
3021
|
-
<TestWrapper>
|
|
3022
|
-
<HTMLEditor {...defaultProps} ref={ref} initialContent=" " />
|
|
3023
|
-
</TestWrapper>
|
|
3024
|
-
);
|
|
3025
|
-
|
|
3026
|
-
act(() => {
|
|
3027
|
-
jest.runAllTimers();
|
|
3028
|
-
});
|
|
3029
|
-
|
|
3030
|
-
await waitFor(() => {
|
|
3031
|
-
expect(ref.current).toBeTruthy();
|
|
3032
|
-
}, { timeout: 3000 });
|
|
3033
|
-
|
|
3034
|
-
// Test method only if available
|
|
3035
|
-
if (typeof ref.current.isContentEmpty === 'function') {
|
|
3036
|
-
expect(ref.current.isContentEmpty()).toBe(true);
|
|
3037
|
-
} else {
|
|
3038
|
-
expect(ref.current).toBeDefined();
|
|
3039
|
-
}
|
|
3040
|
-
});
|
|
3041
|
-
|
|
3042
|
-
it('isContentEmpty handles non-empty content when method is available', async () => {
|
|
3043
|
-
const ref = React.createRef();
|
|
3044
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3045
|
-
isValidating: false,
|
|
3046
|
-
getAllIssues: () => [],
|
|
3047
|
-
isClean: () => true,
|
|
3048
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3049
|
-
});
|
|
3050
|
-
|
|
3051
|
-
render(
|
|
3052
|
-
<TestWrapper>
|
|
3053
|
-
<HTMLEditor {...defaultProps} ref={ref} initialContent="<p>Has content</p>" />
|
|
3054
|
-
</TestWrapper>
|
|
3055
|
-
);
|
|
3056
|
-
|
|
3057
|
-
act(() => {
|
|
3058
|
-
jest.runAllTimers();
|
|
3059
|
-
});
|
|
3060
|
-
|
|
3061
|
-
await waitFor(() => {
|
|
3062
|
-
expect(ref.current).toBeTruthy();
|
|
3063
|
-
}, { timeout: 3000 });
|
|
3064
|
-
|
|
3065
|
-
// Test method only if available
|
|
3066
|
-
if (typeof ref.current.isContentEmpty === 'function') {
|
|
3067
|
-
expect(ref.current.isContentEmpty()).toBe(false);
|
|
3068
|
-
} else {
|
|
3069
|
-
expect(ref.current).toBeDefined();
|
|
3070
|
-
}
|
|
3071
|
-
});
|
|
3072
|
-
});
|
|
3073
|
-
});
|
|
3074
|
-
|
|
3075
|
-
describe('Ref advanced methods', () => {
|
|
3076
|
-
it('computes issue counts and validation state from ref', async () => {
|
|
3077
|
-
const ref = React.createRef();
|
|
3078
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
3079
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
3080
|
-
const { intl } = intlProvider.getChildContext();
|
|
3081
|
-
|
|
3082
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3083
|
-
isValidating: false,
|
|
3084
|
-
hasBlockingErrors: true,
|
|
3085
|
-
getAllIssues: () => [
|
|
3086
|
-
{ source: 'liquid-validator', rule: 'liquid-syntax', message: 'Liquid issue' },
|
|
3087
|
-
{ source: 'htmlhint', rule: 'tag-pair', message: 'tag must be paired' },
|
|
3088
|
-
{ source: 'htmlhint', rule: 'html-error', message: 'HTML error' },
|
|
3089
|
-
],
|
|
3090
|
-
isClean: () => false,
|
|
3091
|
-
summary: { totalErrors: 2, totalWarnings: 0 },
|
|
3092
|
-
});
|
|
3093
|
-
|
|
3094
|
-
render(
|
|
3095
|
-
<TestWrapper>
|
|
3096
|
-
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
3097
|
-
</TestWrapper>
|
|
3098
|
-
);
|
|
3099
|
-
|
|
3100
|
-
act(() => {
|
|
3101
|
-
jest.runAllTimers();
|
|
3102
|
-
});
|
|
3103
|
-
|
|
3104
|
-
await waitFor(() => {
|
|
3105
|
-
expect(ref.current).toBeTruthy();
|
|
3106
|
-
});
|
|
3107
|
-
|
|
3108
|
-
const issueCounts = ref.current.getIssueCounts();
|
|
3109
|
-
// None of the mock issues are blocking (no BLOCKING_ERROR_RULE_IDS, liquid has no severity 'error')
|
|
3110
|
-
expect(issueCounts).toEqual({
|
|
3111
|
-
errors: 0,
|
|
3112
|
-
warnings: 3,
|
|
3113
|
-
total: 3,
|
|
3114
|
-
});
|
|
3115
|
-
|
|
3116
|
-
const validationState = ref.current.getValidationState();
|
|
3117
|
-
expect(validationState.hasErrors).toBe(true);
|
|
3118
|
-
expect(validationState.issueCounts).toEqual({
|
|
3119
|
-
errors: 0,
|
|
3120
|
-
warnings: 3,
|
|
3121
|
-
total: 3,
|
|
3122
|
-
});
|
|
3123
|
-
});
|
|
3124
|
-
|
|
3125
|
-
it('returns empty counts when validation is missing getAllIssues', async () => {
|
|
3126
|
-
const ref = React.createRef();
|
|
3127
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
3128
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
3129
|
-
const { intl } = intlProvider.getChildContext();
|
|
3130
|
-
|
|
3131
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3132
|
-
isValidating: false,
|
|
3133
|
-
isClean: () => true,
|
|
3134
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3135
|
-
});
|
|
3136
|
-
|
|
3137
|
-
render(
|
|
3138
|
-
<TestWrapper>
|
|
3139
|
-
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
3140
|
-
</TestWrapper>
|
|
3141
|
-
);
|
|
3142
|
-
|
|
3143
|
-
act(() => {
|
|
3144
|
-
jest.runAllTimers();
|
|
3145
|
-
});
|
|
3146
|
-
|
|
3147
|
-
await waitFor(() => {
|
|
3148
|
-
expect(ref.current).toBeTruthy();
|
|
3149
|
-
});
|
|
3150
|
-
|
|
3151
|
-
expect(ref.current.getIssueCounts()).toEqual({
|
|
3152
|
-
errors: 0, warnings: 0, total: 0,
|
|
3153
|
-
});
|
|
3154
|
-
expect(ref.current.getValidationState().issueCounts).toEqual({
|
|
3155
|
-
errors: 0, warnings: 0, total: 0,
|
|
3156
|
-
});
|
|
3157
|
-
});
|
|
3158
|
-
});
|
|
3159
|
-
|
|
3160
|
-
describe('Props coverage (lines 54-84)', () => {
|
|
3161
|
-
it('should handle all props with different values', () => {
|
|
3162
|
-
const onSave = jest.fn();
|
|
3163
|
-
const onContentChange = jest.fn();
|
|
3164
|
-
const onTagContextChange = jest.fn();
|
|
3165
|
-
const onTagSelect = jest.fn();
|
|
3166
|
-
const onContextChange = jest.fn();
|
|
3167
|
-
const onErrorAcknowledged = jest.fn();
|
|
3168
|
-
const onValidationChange = jest.fn();
|
|
3169
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
3170
|
-
const tags = [{ id: 1, name: 'Tag1' }];
|
|
3171
|
-
const injectedTags = { custom: 'value' };
|
|
3172
|
-
const eventContextTags = ['event1', 'event2'];
|
|
3173
|
-
const selectedOfferDetails = [{ id: 1 }];
|
|
3174
|
-
const apiValidationErrors = {
|
|
3175
|
-
liquidErrors: [{ message: 'Liquid error' }],
|
|
3176
|
-
standardErrors: [{ message: 'Standard error' }],
|
|
3177
|
-
};
|
|
3178
|
-
|
|
3179
|
-
render(
|
|
3180
|
-
<TestWrapper>
|
|
3181
|
-
<HTMLEditor
|
|
3182
|
-
variant="inapp"
|
|
3183
|
-
layoutType="modal"
|
|
3184
|
-
initialContent="<p>Test</p>"
|
|
3185
|
-
onSave={onSave}
|
|
3186
|
-
onContentChange={onContentChange}
|
|
3187
|
-
className="custom-class"
|
|
3188
|
-
readOnly={true}
|
|
3189
|
-
showFullscreenButton={false}
|
|
3190
|
-
autoSave={false}
|
|
3191
|
-
autoSaveInterval={60000}
|
|
3192
|
-
tags={tags}
|
|
3193
|
-
injectedTags={injectedTags}
|
|
3194
|
-
location={{ query: { type: 'embedded' } }}
|
|
3195
|
-
eventContextTags={eventContextTags}
|
|
3196
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
3197
|
-
channel="EMAIL"
|
|
3198
|
-
userLocale="fr"
|
|
3199
|
-
moduleFilterEnabled={false}
|
|
3200
|
-
onTagContextChange={onTagContextChange}
|
|
3201
|
-
onTagSelect={onTagSelect}
|
|
3202
|
-
onContextChange={onContextChange}
|
|
3203
|
-
globalActions={globalActions}
|
|
3204
|
-
isLiquidEnabled={true}
|
|
3205
|
-
isFullMode={false}
|
|
3206
|
-
onErrorAcknowledged={onErrorAcknowledged}
|
|
3207
|
-
onValidationChange={onValidationChange}
|
|
3208
|
-
apiValidationErrors={apiValidationErrors}
|
|
3209
|
-
/>
|
|
3210
|
-
</TestWrapper>
|
|
3211
|
-
);
|
|
3212
|
-
|
|
3213
|
-
act(() => {
|
|
3214
|
-
jest.runAllTimers();
|
|
3215
|
-
});
|
|
3216
|
-
|
|
3217
|
-
// Component should render with all props
|
|
3218
|
-
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3219
|
-
});
|
|
3220
|
-
|
|
3221
|
-
it('should handle props with default values', () => {
|
|
3222
|
-
render(
|
|
3223
|
-
<TestWrapper>
|
|
3224
|
-
<HTMLEditor />
|
|
3225
|
-
</TestWrapper>
|
|
3226
|
-
);
|
|
3227
|
-
|
|
3228
|
-
act(() => {
|
|
3229
|
-
jest.runAllTimers();
|
|
3230
|
-
});
|
|
3231
|
-
|
|
3232
|
-
// Component should render with default props
|
|
3233
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3234
|
-
});
|
|
3235
|
-
|
|
3236
|
-
it('should handle variant prop with email value', () => {
|
|
3237
|
-
render(
|
|
3238
|
-
<TestWrapper>
|
|
3239
|
-
<HTMLEditor variant="email" />
|
|
3240
|
-
</TestWrapper>
|
|
3241
|
-
);
|
|
3242
|
-
|
|
3243
|
-
act(() => {
|
|
3244
|
-
jest.runAllTimers();
|
|
3245
|
-
});
|
|
3246
|
-
|
|
3247
|
-
expect(screen.queryByTestId('device-toggle')).not.toBeInTheDocument();
|
|
3248
|
-
});
|
|
3249
|
-
|
|
3250
|
-
it('should handle variant prop with inapp value', () => {
|
|
3251
|
-
render(
|
|
3252
|
-
<TestWrapper>
|
|
3253
|
-
<HTMLEditor variant="inapp" />
|
|
3254
|
-
</TestWrapper>
|
|
3255
|
-
);
|
|
3256
|
-
|
|
3257
|
-
act(() => {
|
|
3258
|
-
jest.runAllTimers();
|
|
3259
|
-
});
|
|
3260
|
-
|
|
3261
|
-
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3262
|
-
});
|
|
3263
|
-
|
|
3264
|
-
it('should handle isLiquidEnabled prop', () => {
|
|
3265
|
-
render(
|
|
3266
|
-
<TestWrapper>
|
|
3267
|
-
<HTMLEditor isLiquidEnabled={true} />
|
|
3268
|
-
</TestWrapper>
|
|
3269
|
-
);
|
|
3270
|
-
|
|
3271
|
-
act(() => {
|
|
3272
|
-
jest.runAllTimers();
|
|
3273
|
-
});
|
|
3274
|
-
|
|
3275
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3276
|
-
});
|
|
3277
|
-
|
|
3278
|
-
it('should handle isFullMode prop', () => {
|
|
3279
|
-
render(
|
|
3280
|
-
<TestWrapper>
|
|
3281
|
-
<HTMLEditor isFullMode={false} />
|
|
3282
|
-
</TestWrapper>
|
|
3283
|
-
);
|
|
3284
|
-
|
|
3285
|
-
act(() => {
|
|
3286
|
-
jest.runAllTimers();
|
|
3287
|
-
});
|
|
3288
|
-
|
|
3289
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3290
|
-
});
|
|
3291
|
-
|
|
3292
|
-
it('should handle apiValidationErrors prop', () => {
|
|
3293
|
-
const apiValidationErrors = {
|
|
3294
|
-
liquidErrors: [{ message: 'Liquid error', line: 1 }],
|
|
3295
|
-
standardErrors: [{ message: 'Standard error', line: 2 }],
|
|
3296
|
-
};
|
|
3297
|
-
|
|
3298
|
-
render(
|
|
3299
|
-
<TestWrapper>
|
|
3300
|
-
<HTMLEditor apiValidationErrors={apiValidationErrors} />
|
|
3301
|
-
</TestWrapper>
|
|
3302
|
-
);
|
|
3303
|
-
|
|
3304
|
-
act(() => {
|
|
3305
|
-
jest.runAllTimers();
|
|
3306
|
-
});
|
|
3307
|
-
|
|
3308
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3309
|
-
});
|
|
3310
|
-
});
|
|
3311
|
-
|
|
3312
|
-
describe('Props coverage for lines 67-83', () => {
|
|
3313
|
-
it('should handle tags prop with array of tag objects', () => {
|
|
3314
|
-
const tags = [
|
|
3315
|
-
{ id: 1, name: 'customer.name', label: 'Customer Name' },
|
|
3316
|
-
{ id: 2, name: 'customer.email', label: 'Customer Email' },
|
|
3317
|
-
];
|
|
3318
|
-
|
|
3319
|
-
render(
|
|
3320
|
-
<TestWrapper>
|
|
3321
|
-
<HTMLEditor tags={tags} />
|
|
3322
|
-
</TestWrapper>
|
|
3323
|
-
);
|
|
3324
|
-
|
|
3325
|
-
act(() => {
|
|
3326
|
-
jest.runAllTimers();
|
|
3327
|
-
});
|
|
3328
|
-
|
|
3329
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3330
|
-
});
|
|
3331
|
-
|
|
3332
|
-
it('should handle injectedTags prop with object', () => {
|
|
3333
|
-
const injectedTags = {
|
|
3334
|
-
'custom.tag1': 'value1',
|
|
3335
|
-
'custom.tag2': 'value2',
|
|
3336
|
-
};
|
|
3337
|
-
|
|
3338
|
-
render(
|
|
3339
|
-
<TestWrapper>
|
|
3340
|
-
<HTMLEditor injectedTags={injectedTags} />
|
|
3341
|
-
</TestWrapper>
|
|
3342
|
-
);
|
|
3343
|
-
|
|
3344
|
-
act(() => {
|
|
3345
|
-
jest.runAllTimers();
|
|
3346
|
-
});
|
|
3347
|
-
|
|
3348
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3349
|
-
});
|
|
3350
|
-
|
|
3351
|
-
it('should handle eventContextTags prop', () => {
|
|
3352
|
-
const eventContextTags = ['event.context1', 'event.context2'];
|
|
3353
|
-
|
|
3354
|
-
render(
|
|
3355
|
-
<TestWrapper>
|
|
3356
|
-
<HTMLEditor eventContextTags={eventContextTags} />
|
|
3357
|
-
</TestWrapper>
|
|
3358
|
-
);
|
|
3359
|
-
|
|
3360
|
-
act(() => {
|
|
3361
|
-
jest.runAllTimers();
|
|
3362
|
-
});
|
|
3363
|
-
|
|
3364
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3365
|
-
});
|
|
3366
|
-
|
|
3367
|
-
it('should handle selectedOfferDetails prop', () => {
|
|
3368
|
-
const selectedOfferDetails = [
|
|
3369
|
-
{ id: 'offer1', name: 'Offer 1' },
|
|
3370
|
-
{ id: 'offer2', name: 'Offer 2' },
|
|
3371
|
-
];
|
|
3372
|
-
|
|
3373
|
-
render(
|
|
3374
|
-
<TestWrapper>
|
|
3375
|
-
<HTMLEditor selectedOfferDetails={selectedOfferDetails} />
|
|
3376
|
-
</TestWrapper>
|
|
3377
|
-
);
|
|
3378
|
-
|
|
3379
|
-
act(() => {
|
|
3380
|
-
jest.runAllTimers();
|
|
3381
|
-
});
|
|
3382
|
-
|
|
3383
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3384
|
-
});
|
|
3385
|
-
|
|
3386
|
-
it('should handle channel prop with different values', () => {
|
|
3387
|
-
render(
|
|
3388
|
-
<TestWrapper>
|
|
3389
|
-
<HTMLEditor channel="SMS" />
|
|
3390
|
-
</TestWrapper>
|
|
3391
|
-
);
|
|
3392
|
-
|
|
3393
|
-
act(() => {
|
|
3394
|
-
jest.runAllTimers();
|
|
3395
|
-
});
|
|
3396
|
-
|
|
3397
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3398
|
-
});
|
|
3399
|
-
|
|
3400
|
-
it('should handle userLocale prop with different locales', () => {
|
|
3401
|
-
render(
|
|
3402
|
-
<TestWrapper>
|
|
3403
|
-
<HTMLEditor userLocale="de" />
|
|
3404
|
-
</TestWrapper>
|
|
3405
|
-
);
|
|
3406
|
-
|
|
3407
|
-
act(() => {
|
|
3408
|
-
jest.runAllTimers();
|
|
3409
|
-
});
|
|
3410
|
-
|
|
3411
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3412
|
-
});
|
|
3413
|
-
|
|
3414
|
-
it('should handle moduleFilterEnabled prop set to false', () => {
|
|
3415
|
-
render(
|
|
3416
|
-
<TestWrapper>
|
|
3417
|
-
<HTMLEditor moduleFilterEnabled={false} />
|
|
3418
|
-
</TestWrapper>
|
|
3419
|
-
);
|
|
3420
|
-
|
|
3421
|
-
act(() => {
|
|
3422
|
-
jest.runAllTimers();
|
|
3423
|
-
});
|
|
3424
|
-
|
|
3425
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3426
|
-
});
|
|
3427
|
-
|
|
3428
|
-
it('should handle onTagContextChange callback', () => {
|
|
3429
|
-
const onTagContextChange = jest.fn();
|
|
3430
|
-
|
|
3431
|
-
render(
|
|
3432
|
-
<TestWrapper>
|
|
3433
|
-
<HTMLEditor onTagContextChange={onTagContextChange} />
|
|
3434
|
-
</TestWrapper>
|
|
3435
|
-
);
|
|
3436
|
-
|
|
3437
|
-
act(() => {
|
|
3438
|
-
jest.runAllTimers();
|
|
3439
|
-
});
|
|
3440
|
-
|
|
3441
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3442
|
-
expect(onTagContextChange).toBeDefined();
|
|
3443
|
-
});
|
|
3444
|
-
|
|
3445
|
-
it('should handle onTagSelect callback', () => {
|
|
3446
|
-
const onTagSelect = jest.fn();
|
|
3447
|
-
|
|
3448
|
-
render(
|
|
3449
|
-
<TestWrapper>
|
|
3450
|
-
<HTMLEditor onTagSelect={onTagSelect} />
|
|
3451
|
-
</TestWrapper>
|
|
3452
|
-
);
|
|
3453
|
-
|
|
3454
|
-
act(() => {
|
|
3455
|
-
jest.runAllTimers();
|
|
3456
|
-
});
|
|
3457
|
-
|
|
3458
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3459
|
-
});
|
|
3460
|
-
|
|
3461
|
-
it('should handle onErrorAcknowledged callback', () => {
|
|
3462
|
-
const onErrorAcknowledged = jest.fn();
|
|
3463
|
-
|
|
3464
|
-
render(
|
|
3465
|
-
<TestWrapper>
|
|
3466
|
-
<HTMLEditor onErrorAcknowledged={onErrorAcknowledged} />
|
|
3467
|
-
</TestWrapper>
|
|
3468
|
-
);
|
|
3469
|
-
|
|
3470
|
-
act(() => {
|
|
3471
|
-
jest.runAllTimers();
|
|
3472
|
-
});
|
|
3473
|
-
|
|
3474
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3475
|
-
});
|
|
3476
|
-
|
|
3477
|
-
it('should handle null values for optional props', () => {
|
|
3478
|
-
render(
|
|
3479
|
-
<TestWrapper>
|
|
3480
|
-
<HTMLEditor
|
|
3481
|
-
tags={null}
|
|
3482
|
-
injectedTags={null}
|
|
3483
|
-
eventContextTags={null}
|
|
3484
|
-
selectedOfferDetails={null}
|
|
3485
|
-
onTagContextChange={null}
|
|
3486
|
-
onTagSelect={null}
|
|
3487
|
-
onContextChange={null}
|
|
3488
|
-
globalActions={null}
|
|
3489
|
-
onErrorAcknowledged={null}
|
|
3490
|
-
onValidationChange={null}
|
|
3491
|
-
apiValidationErrors={null}
|
|
3492
|
-
/>
|
|
3493
|
-
</TestWrapper>
|
|
3494
|
-
);
|
|
3495
|
-
|
|
3496
|
-
act(() => {
|
|
3497
|
-
jest.runAllTimers();
|
|
3498
|
-
});
|
|
3499
|
-
|
|
3500
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3501
|
-
});
|
|
3502
|
-
|
|
3503
|
-
it('should handle empty arrays for array props', () => {
|
|
3504
|
-
render(
|
|
3505
|
-
<TestWrapper>
|
|
3506
|
-
<HTMLEditor
|
|
3507
|
-
tags={[]}
|
|
3508
|
-
eventContextTags={[]}
|
|
3509
|
-
selectedOfferDetails={[]}
|
|
3510
|
-
/>
|
|
3511
|
-
</TestWrapper>
|
|
3512
|
-
);
|
|
3513
|
-
|
|
3514
|
-
act(() => {
|
|
3515
|
-
jest.runAllTimers();
|
|
3516
|
-
});
|
|
3517
|
-
|
|
3518
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3519
|
-
});
|
|
3520
|
-
|
|
3521
|
-
it('should handle empty object for injectedTags', () => {
|
|
3522
|
-
render(
|
|
3523
|
-
<TestWrapper>
|
|
3524
|
-
<HTMLEditor injectedTags={{}} />
|
|
3525
|
-
</TestWrapper>
|
|
3526
|
-
);
|
|
3527
|
-
|
|
3528
|
-
act(() => {
|
|
3529
|
-
jest.runAllTimers();
|
|
3530
|
-
});
|
|
3531
|
-
|
|
3532
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3533
|
-
});
|
|
3534
|
-
|
|
3535
|
-
it('should handle location prop with query parameters', () => {
|
|
3536
|
-
const location = {
|
|
3537
|
-
query: {
|
|
3538
|
-
type: 'embedded',
|
|
3539
|
-
module: 'library',
|
|
3540
|
-
id: '123',
|
|
3541
|
-
},
|
|
3542
|
-
pathname: '/email/edit/123',
|
|
3543
|
-
};
|
|
3544
|
-
|
|
3545
|
-
render(
|
|
3546
|
-
<TestWrapper>
|
|
3547
|
-
<HTMLEditor location={location} />
|
|
3548
|
-
</TestWrapper>
|
|
3549
|
-
);
|
|
3550
|
-
|
|
3551
|
-
act(() => {
|
|
3552
|
-
jest.runAllTimers();
|
|
3553
|
-
});
|
|
3554
|
-
|
|
3555
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3556
|
-
});
|
|
3557
|
-
});
|
|
3558
|
-
});
|